Vue.js(讀音/vju?/, 類似于 view)是一個(gè)構(gòu)建數(shù)據(jù)驅(qū)動(dòng)的web 界面的漸進(jìn)式框架。Vue.js的目標(biāo)是通過盡可能簡單的API 實(shí)現(xiàn)響應(yīng)的數(shù)據(jù)綁定和組合的視圖組件。只聚焦于視圖層。
一、Vue實(shí)例
1、創(chuàng)建一個(gè)vue實(shí)例
每個(gè) Vue 應(yīng)用都是通過用Vue 函數(shù)創(chuàng)建一個(gè)新的Vue 實(shí)例開始的:
?????? var vm = new Vue({ //選項(xiàng)? })
2、數(shù)據(jù)與方法
當(dāng)一個(gè)Vue 實(shí)例被創(chuàng)建時(shí),它向Vue 的響應(yīng)式系統(tǒng)中加入了其data 對(duì)象中能找到的所有的屬性。當(dāng)這些屬性的值發(fā)生改變時(shí),視圖將會(huì)產(chǎn)生“響應(yīng)”,即匹配更新為新的值。
只有當(dāng)實(shí)例被創(chuàng)建時(shí)data 中存在的屬性才是響應(yīng)式的。也就是說如果你添加一個(gè)新的屬性,那么對(duì)它的改動(dòng)將不會(huì)觸發(fā)任何視圖的更新
使用 Object.freeze(),這會(huì)阻止修改現(xiàn)有的屬性,也意味著響應(yīng)系統(tǒng)無法再追蹤變化。
除了數(shù)據(jù)屬性,Vue 實(shí)例還暴露了一些有用的實(shí)例屬性與方法。它們都有前綴$,以便與用戶定義的屬性區(qū)分開來。
二、模板語法
1、插值
數(shù)據(jù)綁定最常見的形式就是使用“Mustache”語法(雙大括號(hào))的文本插值:Mustache標(biāo)簽將會(huì)被替代為對(duì)應(yīng)數(shù)據(jù)對(duì)象上msg 屬性的值。無論何時(shí),綁定的數(shù)據(jù)對(duì)象上msg 屬性發(fā)生了改變,插值處的內(nèi)容都會(huì)更新。
<span>Message:{{ msg }}</span>
通過使用v-once 指令,你也能執(zhí)行一次性地插值,當(dāng)數(shù)據(jù)改變時(shí),插值處的內(nèi)容不會(huì)更新。
<span v-once>Message:{{msg}}</span>
?????? 原始html:雙大括號(hào)會(huì)將數(shù)據(jù)解釋為普通文本,而非HTML 代碼。為了輸出真正的HTML,你需要使用v-html 指令:
<p>Using v-html directive:<span v-html="rowhtml"></span></p>
?????? 特性:Mustache語法不能作用在HTML 特性上,遇到這種情況應(yīng)該使用v-bind 指令:
<p v-bind:id="textid"></p>
?????? 使用JavaScript表達(dá)式:對(duì)于所有的數(shù)據(jù)綁定,Vue.js 都提供了完全的JavaScript 表達(dá)式支持。這些表達(dá)式會(huì)在所屬 Vue 實(shí)例的數(shù)據(jù)作用域下作為JavaScript 被解析。有個(gè)限制就是,每個(gè)綁定都只能包含單個(gè)表達(dá)式,所以下面的例子都不會(huì)生效。
<!-- 這是語句,不是表達(dá)式-->
{{ var a = 1 }}
流控制也不會(huì)生效,請(qǐng)使用三元表達(dá)式-->
{{ if (ok) { return message } }}
2、指令
?????? 指令 (Directives) 是帶有v- 前綴的特殊屬性。指令屬性的值預(yù)期是單個(gè)JavaScript 表達(dá)式 (v-for 是例外情況,稍后我們?cè)儆懻?。指令的職責(zé)是,當(dāng)表達(dá)式的值改變時(shí),將其產(chǎn)生的連帶影響,響應(yīng)式地作用于DOM。
<p? v-if="seen">現(xiàn)在你看到我了</p>
v-if 指令將根據(jù)表達(dá)式seen 的值的真假來插入/移除<p> 元素。
?????? 參數(shù):一些指令能夠接收一個(gè)“參數(shù)”,在指令名稱之后以冒號(hào)表示。例如,v-bind指令可以用于響應(yīng)式地更新HTML 屬性:
<a v-bind:href="href"></a>
<a :href="href"></a>
在這里 href 是參數(shù),告知v-bind 指令將該元素的 href 屬性與表達(dá)式url 的值綁定。
另一個(gè)例子是v-on指令,它用于監(jiān)聽DOM事件,在這里參數(shù)是監(jiān)聽的事件名。
<a v-on:click="dosomething">...</a>
<a @click="dosomething"></a>
可以用方括號(hào)括起來的 JavaScript 表達(dá)式作為一個(gè)指令的參數(shù)
<a v-bind:[name]="textid"></a>
name其值為id,則上式可以轉(zhuǎn)換成<a v-bind:id="textid"></a>
?????? 修飾符:修飾符(Modifiers) 是以半角句號(hào) . 指明的特殊后綴,用于指出一個(gè)指令應(yīng)該以特殊方式綁定,例如:
.prevent 修飾符告訴v-on 指令對(duì)于觸發(fā)的事件調(diào)event.preventDefault():
<form v-on:submit.prevent="onSubmit"></form>
三、計(jì)算屬性和偵聽器
1、計(jì)算屬性
<p>Computed reversed message: "{{ reversedMessage }}"</p>
computed: {
??? //計(jì)算屬性的getter
??? reversedMessage: function () {
????? // `this`指向vm 實(shí)例
????? return this.message.split('').reverse().join('')
??? }
? }
2、在表達(dá)式中調(diào)用方法
<p>Reversedmessage: "{{ reversedMessage() }}"</p>
// 在組件中
methods: {
? reversedMessage: function () {
??? return this.message.split('').reverse().join('')
? }
}
3、計(jì)算屬性vs方法
?????? 兩種方式的最終結(jié)果確實(shí)是完全相同的。然而,不同的是計(jì)算屬性是基于它們的響應(yīng)式依賴進(jìn)行緩存的。計(jì)算屬性只有在它的相關(guān)響應(yīng)式依賴發(fā)生改變時(shí)才會(huì)重新求值。這就意味著只要message 還沒有發(fā)生改變,多次訪問 reversedMessage 計(jì)算屬性會(huì)立即返回之前的計(jì)算結(jié)果,而不必再次執(zhí)行函數(shù)。相比之下,每當(dāng)觸發(fā)重新渲染時(shí),調(diào)用方法將總會(huì)再次執(zhí)行函數(shù)。
4、偵聽屬性
?????? 當(dāng)你有一些數(shù)據(jù)需要隨著其它數(shù)據(jù)變動(dòng)而變動(dòng)時(shí),你很容易濫用watch
watch: {
??? firstName: function (val) {
????? this.fullName = val + ' ' +this.lastName
??? },
??? lastName: function (val) {
????? this.fullName = this.firstName + '' + val
??? }
? }
5、計(jì)算屬性vs偵聽屬性
computed: {
??? fullName: function () {
????? return this.firstName + ' ' +this.lastName
??? }
? }
偵聽屬性代碼命令式且重復(fù)
6、計(jì)算屬性的setter
computed: {
? fullName: {
??? // getter
??? get: function () {
????? return this.firstName + ' ' +this.lastName
??? },
??? // setter
??? set: function (newValue) {
????? var names = newValue.split(' ')
????? this.firstName = names[0]
????? this.lastName = names[names.length- 1]
??? }
? }
}
7、偵聽器
?????? 雖然計(jì)算屬性在大多數(shù)情況下更合適,但有時(shí)也需要一個(gè)自定義的偵聽器。Vue通過watch 選項(xiàng)提供了一個(gè)更通用的方法,來響應(yīng)數(shù)據(jù)的變化。當(dāng)需要在數(shù)據(jù)變化時(shí)執(zhí)行異步或開銷較大的操作時(shí),這個(gè)方式是最有用的。
????使用?watch?選項(xiàng)允許我們執(zhí)行異步操作 (訪問一個(gè) API),限制我們執(zhí)行該操作的頻率,并在我們得到最終結(jié)果前,設(shè)置中間狀態(tài)。這些都是計(jì)算屬性無法做到的。
四、class與style綁定
1、綁定HTMLClass
?????? 對(duì)象語法:
v-bind:class 指令也可以與普通的class 屬性共存
綁定的數(shù)據(jù)對(duì)象不必內(nèi)聯(lián)定義在模板里:
data:? {
? classObject: {
??? active: true,
??? 'text-danger': false
? }
}
數(shù)組語法:
<div v-bind:class="[activeClass,errorClass]"></div>
把一個(gè)數(shù)組傳給v-bind:class,以應(yīng)用一個(gè) class 列表:
data:? {
? activeClass: 'active',
? errorClass: 'text-danger'
}
渲染為:<div class="active text-danger"></div>
用在組件上:當(dāng)在一個(gè)自定義組件上使用class 屬性時(shí),這些類將被添加到該組件的根元素上面。這個(gè)元素上已經(jīng)存在的類不會(huì)被覆蓋。
?2、綁定內(nèi)聯(lián)樣式
對(duì)象語法:
<div v-bind:style="{color:activeColor,fontSize:fontSize+'px'}"></div>
data: {
? activeColor: 'red',
? fontSize: 30
}
直接綁定到一個(gè)樣式對(duì)象通常更好,這會(huì)讓模板更清晰:
<div v-bind:style="styleObject"></div>
data:? {
? styleObject: {
??? color: 'red',
??? fontSize: '13px'
? }
}
數(shù)組語法:
v-bind:style 的數(shù)組語法可以將多個(gè)樣式對(duì)象應(yīng)用到同一個(gè)元素上:
<div :style="[baseStyles, overridingStyles]"></div>
自動(dòng)添加前綴:
當(dāng)v-bind:style 使用需要添加瀏覽器引擎前綴的 CSS 屬性時(shí),如transform,Vue.js 會(huì)自動(dòng)偵測(cè)并添加相應(yīng)的前綴。
多重值
可以為style 綁定中的屬性提供一個(gè)包含多個(gè)值的數(shù)組,常用于提供多個(gè)帶前綴的值,例如:
<div :style="{display:['-webkit-box', '-ms-flexbox', 'flex']}"></div>
這樣寫只會(huì)渲染數(shù)組中最后一個(gè)被瀏覽器支持的值。在本例中,如果瀏覽器支持不帶瀏覽器前綴的flexbox,那么就只會(huì)渲染 display: flex。
五、條件渲染
1.v-if
在<template> 元素上使用 v-if 條件渲染分組:
因?yàn)関-if 是一個(gè)指令,所以必須將它添加到一個(gè)元素上。但是如果想切換多個(gè)元素呢?此時(shí)可以把一個(gè)<template> 元素當(dāng)做不可見的包裹元素,并在上面使用 v-if。最終的渲染結(jié)果將不包含<template> 元素。
?????? 可以使用v-else 指令來表示 v-if 的“else塊”,v-else 元素必須緊跟在帶 v-if 或者v-else-if 的元素的后面,否則它將不會(huì)被識(shí)別,v-else-if 也必須緊跟在帶v-if 或者v-else-if 的元素之后。
用key管理可復(fù)用元素
?????? Vue為你提供了一種方式來表達(dá)“兩個(gè)元素是完全獨(dú)立的,不要復(fù)用它們”。只需添加一個(gè)具有唯一值的key 屬性即可:
2、v-show
另一個(gè)用于根據(jù)條件展示元素的選項(xiàng)是v-show 指令:
不同的是帶有v-show 的元素始終會(huì)被渲染并保留在 DOM 中。v-show只是簡單地切換元素的CSS 屬性display。v-show 不支持<template> 元素,也不支持 v-else。
3、v-if vs v-show
v-if 是“真正”的條件渲染,因?yàn)樗鼤?huì)確保在切換過程中條件塊內(nèi)的事件監(jiān)聽器和子組件適當(dāng)?shù)乇讳N毀和重建。v-if也是惰性的:如果在初始渲染時(shí)條件為假,則什么也不做——直到條件第一次變?yōu)檎鏁r(shí),才會(huì)開始渲染條件塊。
相比之下,v-show 就簡單得多——不管初始條件是什么,元素總是會(huì)被渲染,并且只是簡單地基于CSS 進(jìn)行切換。
一般來說,v-if 有更高的切換開銷,而v-show 有更高的初始渲染開銷。因此,如果需要非常頻繁地切換,則使用v-show 較好;如果在運(yùn)行時(shí)條件很少改變,則使用 v-if 較好。
4、v-if 與v-for?
當(dāng)v-if 與v-for 一起使用時(shí),v-for 具有比v-if 更高的優(yōu)先級(jí)。
六、列表渲染
1、用v-for把一個(gè)數(shù)組對(duì)應(yīng)為一組元素
用 v-for 指令根據(jù)一組數(shù)組的選項(xiàng)列表進(jìn)行渲染。v-for指令需要使用item in items 形式的特殊語法,items 是源數(shù)據(jù)數(shù)組并且item 是數(shù)組元素迭代的別名。
?????? 在v-for 塊中,我們擁有對(duì)父作用域?qū)傩缘耐耆L問權(quán)限。v-for還支持一個(gè)可選的第二個(gè)參數(shù)為當(dāng)前項(xiàng)的索引。
<ul id="example-2">
<li v-for="(item,index) in items">
??? {{ parentMessage }} - {{ index }} -? {{ item.message }}
</li>
</ul>
var? example2 = new Vue({
? el: '#example-2',
? data: {
??? parentMessage: 'Parent',
??? items: [
????? { message: 'Foo' },
????? { message: 'Bar' }
?? ?]
? }
})
也可以用? of 替代 in 作為分隔符
2、一個(gè)對(duì)象的v-for
可以用v-for 通過一個(gè)對(duì)象的屬性來迭代。
<ul id="v-for-object">
<li v-for="value in object">
{{value}}
</li>
</ul>
new? Vue({
? el: '#v-for-object',
? data: {
??? object: {
????? firstName: 'John',
????? lastName: 'Doe',
????? age: 30
??? }
? }
})
也可以提供第二個(gè)的參數(shù)為鍵名:
? {{ key }}: {{ value }}
第三個(gè)參數(shù)為索引:
? {{ index }}. {{ key }}: {{ value }}
3、key
為了給Vue 一個(gè)提示,以便它能跟蹤每個(gè)節(jié)點(diǎn)的身份,從而重用和重新排序現(xiàn)有元素,你需要為每項(xiàng)提供一個(gè)唯一key 屬性。理想的key 值是每項(xiàng)都有的且唯一的id。這個(gè)特殊的屬性相當(dāng)于Vue 1.x 的 track-by ,但它的工作方式類似于一個(gè)屬性,所以你需要用v-bind 來綁定動(dòng)態(tài)值。建議盡可能在使用 v-for 時(shí)提供key,因?yàn)樗荲ue 識(shí)別節(jié)點(diǎn)的一個(gè)通用機(jī)制,key并不與v-for 特別關(guān)聯(lián)。
4、數(shù)組更新檢測(cè)
?變異方法:
Vue 包含一組觀察數(shù)組的變異方法,所以它們也將會(huì)觸發(fā)視圖更新。
push()? pop()?shift()? unshift()? splice()?sort()? reverse()
變異方法(mutation method),顧名思義,會(huì)改變被這些方法調(diào)用的原始數(shù)組。
替換數(shù)組:非變異(non-mutating method) 方法,例如:filter(),
concat() 和 slice() 。這些不會(huì)改變?cè)紨?shù)組,但總是返回一個(gè)新數(shù)組。當(dāng)使用非變異方法時(shí),可以用新數(shù)組替換舊數(shù)組:
注意事項(xiàng):
由于JavaScript 的限制,Vue 不能檢測(cè)以下變動(dòng)的數(shù)組:
當(dāng)你利用索引直接設(shè)置一個(gè)項(xiàng)時(shí):
?????? Vue.set(vm.items, indexOfItem, newValue)
也可以使用vm.$set實(shí)例方法,該方法是全局方法 Vue.set 的一個(gè)別名:
?????? vm.$set(vm.items, indexOfItem, newValue)
?????? 當(dāng)你修改數(shù)組的長度時(shí)
?????? 使用vm.items.splice(newLength)
5、對(duì)象更改檢測(cè)注意事項(xiàng)
?????? 由于JavaScript 的限制,Vue 不能檢測(cè)對(duì)象屬性的添加或刪除:可以使用
Vue.set(object,key, value) 方法向嵌套對(duì)象添加響應(yīng)式屬性,也可以使用vm.$set 實(shí)例方法,它只是全局 Vue.set 的別名:Vue.$set(object,key, value)
?????? 有時(shí)你可能需要為已有對(duì)象賦予多個(gè)新屬性,比如使用Object.assign() 或 _.extend()。在這種情況下,你應(yīng)該用兩個(gè)對(duì)象的屬性創(chuàng)建一個(gè)新的對(duì)象
vm.userProfile =Object.assign({}, vm.userProfile, {
? age: 27,
? favoriteColor: 'Vue Green'
})
6、顯示過濾/排序結(jié)果
有時(shí),我們想要顯示一個(gè)數(shù)組的過濾或排序副本,而不實(shí)際改變或重置原始數(shù)據(jù)。在這種情況下,可以創(chuàng)建返回過濾或排序數(shù)組的計(jì)算屬性。
<li v-for="value in evenNumbers">{{ n }}</li>
data:? {
??numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
? evenNumbers: function () {
??? return this.numbers.filter(function? (number) {
????? return number % 2 === 0
??? })
? }
}
在計(jì)算屬性不適用的情況下(例如,在嵌套v-for 循環(huán)中) 你可以使用一個(gè)method 方法:
<li v-for="value in even(numbers)">{{ n }}</li>
data:? {
? numbers: [ 1, 2, 3, 4, 5 ]
},
methods: {
? even: function (numbers) {
??? return numbers.filter(function? (number) {
????? return number % 2 === 0
??? })
? }
}
7、一段取值范圍的v-for
?<span v-for="n in 10"> {{ n }}</span>
8、在template上使用v-for
類似于v-if,你也可以利用帶有v-for 的 <template> 渲染多個(gè)元素。
9、v-for with v-if
當(dāng)它們處于同一節(jié)點(diǎn),v-for的優(yōu)先級(jí)比v-if 更高,這意味著v-if 將分別重復(fù)運(yùn)行于每個(gè)v-for 循環(huán)中:
10、一個(gè)組件的v-for
在自定義組件里,你可以像任何普通元素一樣用 v-for 。
當(dāng)在組件中使用 v-for 時(shí),key 現(xiàn)在是必須的。
然而,任何數(shù)據(jù)都不會(huì)被自動(dòng)傳遞到組件里,因?yàn)榻M件有自己獨(dú)立的作用域。為了把迭代數(shù)據(jù)傳遞到組件里,我們要用 props :
七、事件處理
1、監(jiān)聽事件
可以用v-on 指令監(jiān)聽DOM 事件,并在觸發(fā)時(shí)運(yùn)行一些JavaScript 代碼。
<button v-on:click="count+=1">Add1</button>
<p>{{count}}</p>
var? example1 = new Vue({
? el: '#example-1',
? data: {
??? counter: 0
? }
})
2、事件處理方法
直接把JavaScript 代碼寫在 v-on 指令中是不可行的。因此v-on 還可以接收一個(gè)需要調(diào)用的方法名稱。
3、內(nèi)聯(lián)處理器中的方法
除了直接綁定到一個(gè)方法,也可以在內(nèi)聯(lián)JavaScript 語句中調(diào)用方法,有時(shí)也需要在內(nèi)聯(lián)語句處理器中訪問原始的DOM 事件??梢杂锰厥庾兞?event 把它傳入方法。
4、事件修飾符
方法只有純粹的數(shù)據(jù)邏輯,而不是去處理DOM 事件細(xì)節(jié)。解決了這個(gè)問題,Vue.js為v-on 提供了事件修飾符,修飾符是由點(diǎn)開頭的指令后綴來表示的。
<!-- 阻止單擊事件繼續(xù)傳播-->
<a v-on:click.stop="dothis"></a>
提交事件不再重載頁面-->
<form v-on:submit.prevent="onsubmit"></form>
修飾符可以串聯(lián)-->
<a v-on:click.stop.prevent="dothat"></a>
添加事件監(jiān)聽器時(shí)使用事件捕獲模式,即元素自身觸發(fā)的事件先在此處處理,然后才交由內(nèi)部元素進(jìn)行處理-->
<div v-on:click.capture="dothis"></div>
只當(dāng)在 event.target 是當(dāng)前元素自身時(shí)觸發(fā)處理函數(shù),即事件不是從內(nèi)部元素觸發(fā)的-->
<div v-on:click.self="dothis"></div>
使用修飾符時(shí),順序很重要。
<!-- 點(diǎn)擊事件將只會(huì)觸發(fā)一次-->
<a v-on:click.once="dothis"></a>
<!-- 滾動(dòng)事件的默認(rèn)行為(即滾動(dòng)行為)將會(huì)立即觸發(fā),而不會(huì)等待 `onScroll` 完成,這其中包含 `event.preventDefault()`的情況-->
<div v-on:scroll.passive="onScroll"></div>
不要把.passive 和 .prevent 一起使用,因?yàn)?prevent 將會(huì)被忽略,同時(shí)瀏覽器可能會(huì)向你展示一個(gè)警告。請(qǐng)記住,.passive會(huì)告訴瀏覽器你不想阻止事件的默認(rèn)行為。
5、按鍵修飾符
<!-- 只有在`keyCode` 是 13 時(shí)調(diào)用`vm.submit()` -->
縮寫語法-->
全部的按鍵別名:
.enter? .tab?.delete (捕獲“刪除”和“退格”鍵)? .esc?.space? .up? .down?.left? .right
可以通過全局vue.config.keyCodes 對(duì)象自定義按鍵修飾符別名
自動(dòng)匹配按鍵修飾符
你也可直接將KeyboardEvent.key暴露的任意有效按鍵名轉(zhuǎn)換為kebab-case 來作為修飾符:
在上面的例子中,處理函數(shù)僅在$event.key === 'PageDown' 時(shí)被調(diào)用。
6、系統(tǒng)修飾符
可以用如下修飾符來實(shí)現(xiàn)僅在按下相應(yīng)按鍵時(shí)才觸發(fā)鼠標(biāo)或鍵盤事件的監(jiān)聽器。
.ctrl? .alt?.shift? .meta
.exact 修飾符允許你控制由精確的系統(tǒng)修飾符組合觸發(fā)的事件。
?????? 鼠標(biāo)按鈕修飾符.left? .right? .middle這些修飾符會(huì)限制處理函數(shù)僅響應(yīng)特定的鼠標(biāo)按鈕。
7、為什么在HTML中監(jiān)聽事件
所有的Vue.js 事件處理方法和表達(dá)式都嚴(yán)格綁定在當(dāng)前視圖的ViewModel 上,它不會(huì)導(dǎo)致任何維護(hù)上的困難。實(shí)際上,使用v-on 有幾個(gè)好處:
1、掃一眼HTML 模板便能輕松定位在JavaScript 代碼里對(duì)應(yīng)的方法。
2、因?yàn)槟銦o須在JavaScript 里手動(dòng)綁定事件,你的 ViewModel 代碼可以是非常純粹的邏輯,和DOM 完全解耦,更易于測(cè)試。
3、當(dāng)一個(gè)ViewModel 被銷毀時(shí),所有的事件處理器都會(huì)自動(dòng)被刪除。你無須擔(dān)心如何自己清理它們。
八、表單輸入綁定
1、基礎(chǔ)用法
你可以用v-model 指令在表單 <input> 及<textarea> 元素上創(chuàng)建雙向數(shù)據(jù)綁定。它會(huì)根據(jù)控件類型自動(dòng)選取正確的方法來更新元素。
<input v-model="data1" placeholder="hahaha">
? ? <input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
? ? <label for="jack">Jack</label>
? ? <input type="checkbox" id="john" value="John" v-model="checkedNames">
? ? <label for="john">John</label>
? ? <br>
? ? <span>Checked names: {{ checkedNames }}</span>
2、值綁定
有時(shí)我們可能想把值綁定到Vue 實(shí)例的一個(gè)動(dòng)態(tài)屬性上,這時(shí)可以用v-bind 實(shí)現(xiàn),并且這個(gè)屬性的值可以不是字符串。
3、修飾符
?????? .lazy:在默認(rèn)情況下,v-model在每次input 事件觸發(fā)后將輸入框的值與數(shù)據(jù)進(jìn)行同步 (除了上述輸入法組合文字時(shí))。你可以添加lazy 修飾符,從而轉(zhuǎn)變?yōu)槭褂胏hange 事件進(jìn)行同步:
?????? .number:如果想自動(dòng)將用戶的輸入值轉(zhuǎn)為數(shù)值類型,可以給v-model 添加 number 修飾符:
???? .trim:如果要自動(dòng)過濾用戶輸入的首尾空白字符,可以給v-model 添加 trim 修飾符:
4、在組件上使用v-model(后面返回來學(xué)習(xí))
九、組件
1、什么是組件
?????? 組件(Component) 是 Vue.js 最強(qiáng)大的功能之一。組件可以擴(kuò)展HTML 元素,封裝可重用的代碼。在較高層面上,組件是自定義元素,Vue.js的編譯器為它添加特殊功能。在有些情況下,組件也可以表現(xiàn)為用is 特性進(jìn)行了擴(kuò)展的原生HTML 元素。
所有的Vue 組件同時(shí)也都是Vue 的實(shí)例,所以可接受相同的選項(xiàng)對(duì)象(除了一些根級(jí)特有的選項(xiàng))并提供相同的生命周期鉤子。
2、使用組件
要注冊(cè)一個(gè)全局組件,可以使用Vue.component(tagName, options)。例如:
Vue.component('my-component',? {data:function(){
return {count:0}
},template:'<button v-on:click="count++">You clicked me {{ count }} times.</button>'})
組件在注冊(cè)之后,便可以作為自定義元素 <my-component></my-component> 在一個(gè)實(shí)例的模板中使用。注意確保在初始化根實(shí)例之前注冊(cè)組件:
***一個(gè)組件的?data?選項(xiàng)必須是一個(gè)函數(shù),因此每個(gè)實(shí)例可以維護(hù)一份被返回對(duì)象的獨(dú)立的拷貝:
創(chuàng)建根實(shí)例
new Vue({
? el: '#example'
})
局部注冊(cè):
你不必把每個(gè)組件都注冊(cè)到全局。你可以通過某個(gè) Vue 實(shí)例/組件的實(shí)例選項(xiàng) components 注冊(cè)僅在其作用域中可用的組件:
var? Child = {
? template: 'A custom? component!'
}
new Vue({
? // ...
? components: {
??? //
將只在父組件模板中可用
??? 'my-component': Child
? }
})
Dom模板解析注意事項(xiàng):
當(dāng)使用? DOM 作為模板時(shí) (例如,使用 el 選項(xiàng)來把? Vue 實(shí)例掛載到一個(gè)已有內(nèi)容的元素上),你會(huì)受到 HTML 本身的一些限制,因?yàn)? Vue 只有在瀏覽器解析、規(guī)范化模板之后才能獲取其內(nèi)容,在自定義組件中使用這些受限制的元素時(shí)會(huì)導(dǎo)致一些問題,自定義組件 <my-row> 會(huì)被當(dāng)作無效的內(nèi)容,因此會(huì)導(dǎo)致錯(cuò)誤的渲染結(jié)果。變通的方案是使用特殊的 is 特性:應(yīng)當(dāng)注意,如果使用來自以下來源之一的字符串模板,則沒有這些限制:
JavaScript
? 內(nèi)聯(lián)模板字符串
.vue
? 組件
因此,請(qǐng)盡可能使用字符串模板。
Data必須是函數(shù):構(gòu)造 Vue 實(shí)例時(shí)傳入的各種選項(xiàng)大多數(shù)都可以在組件里使用。只有一個(gè)例外:data 必須是函數(shù)。
組件組合:組件設(shè)計(jì)初衷就是要配合使用的,最常見的就是形成父子組件的關(guān)系:組件 A 在它的模板中使用了組件? B。在 Vue 中,父子組件的關(guān)系可以總結(jié)為 prop 向下傳遞,事件向上傳遞。父組件通過 prop 給子組件下發(fā)數(shù)據(jù),子組件通過事件給父組件發(fā)送消息。
3、Prop
使用prop傳遞數(shù)據(jù),組件實(shí)例的作用域是孤立的。這意味著不能 (也不應(yīng)該)
? 在子組件的模板內(nèi)直接引用父組件的數(shù)據(jù)。父組件的數(shù)據(jù)需要通過 prop 才能下發(fā)到子組件中。
Vue.component('child',? {
? //
聲明props
? props: ['message'],
? //
就像? data 一樣,prop 也可以在模板中使用
? //
同樣也可以在? vm 實(shí)例中通過 this.message 來使用
? template: '{{ message? }}'
})
camelCase? vs. kebab-case
HTML
? 特性是不區(qū)分大小寫的。所以,當(dāng)使用的不是字符串模板時(shí),camelCase (駝峰式命名)
? 的 prop 需要轉(zhuǎn)換為相對(duì)應(yīng)的? kebab-case (短橫線分隔式命名):
Vue.component('child',? ? {
? //
在? ? JavaScript 中使用camelCase
? props: ['myMessage'],
? template: '{{ myMessage? ? }}'
})
<!--
? ? 在 HTML 中使用kebab-case -->
動(dòng)態(tài)Prop
與綁定到任何普通的? HTML 特性相類似,我們可以用 v-bind 來動(dòng)態(tài)地將 prop 綁定到父組件的數(shù)據(jù)。每當(dāng)父組件的數(shù)據(jù)變化時(shí),該變化也會(huì)傳導(dǎo)給子組件:
如果你想把一個(gè)對(duì)象的所有屬性作為 prop 進(jìn)行傳遞,可以使用不帶任何參數(shù)的 v-bind (即用? v-bind 而不是v-bind:prop-name)
將等價(jià)于:
? v-bind:text="todo.text"
v-bind:is-complete="todo.isComplete"
>
字面量語法vs動(dòng)態(tài)語法
初學(xué)者常犯的一個(gè)錯(cuò)誤是使用字面量語法傳遞數(shù)值:
<!-- 傳遞了一個(gè)字符串"1" -->
因?yàn)樗且粋€(gè)字面量? ? prop,它的值是字符串? ? "1" 而不是一個(gè)數(shù)值。如果想傳遞一個(gè)真正的? ? JavaScript 數(shù)值,則需要使用 v-bind,從而讓它的值被當(dāng)作 JavaScript 表達(dá)式計(jì)算:
<!--傳遞真正的數(shù)值-->
單向數(shù)據(jù)流
Prop
? ? 是單向綁定的:當(dāng)父組件的屬性變化時(shí),將傳導(dǎo)給子組件,但是反過來不會(huì)。在兩種情況下,我們很容易忍不住想去修改 prop 中數(shù)據(jù):
Prop
? ? 作為初始值傳入后,子組件想把它當(dāng)作局部數(shù)據(jù)來用;
Prop
? ? 作為原始數(shù)據(jù)傳入,由子組件處理成其它數(shù)據(jù)輸出。
?? 定義一個(gè)局部變量,并用 prop 的值初始化它:
props:? ? ? ['initialCounter'],
data: function () {
? return { counter:? ? ? this.initialCounter }
}
?? 定義一個(gè)計(jì)算屬性,處理 prop 的值并返回:
props:? ? ? ['size'],
computed: {
? normalizedSize: function () {
??? return? ? ? this.size.trim().toLowerCase()
? }
}
Prop驗(yàn)證
我們可以為組件的? prop 指定驗(yàn)證規(guī)則。如果傳入的數(shù)據(jù)不符合要求,Vue
? 會(huì)發(fā)出警告,要指定驗(yàn)證規(guī)則,需要用對(duì)象的形式來定義 prop,而不能用字符串?dāng)?shù)組:
Vue.component('example',? {
? props: {
??? //
基礎(chǔ)類型檢測(cè)? (`null` 指允許任何類型)
??? propA: Number,
??? //
可能是多種類型
??? propB: [String, Number],
??? //
必傳且是字符串
??? propC: {
????? type: String,
????? required: true
??? },
??? //
數(shù)值且有默認(rèn)值
??? propD: {
????? type: Number,
????? default: 100
??? },
??? //
數(shù)組/對(duì)象的默認(rèn)值應(yīng)當(dāng)由一個(gè)工廠函數(shù)返回
??? propE: {
????? type: Object,
????? default: function () {
??????? return { message: 'hello' }
????? }
??? },
??? //
自定義驗(yàn)證函數(shù)
??? propF: {
????? validator: function (value) {
??????? return value > 10
????? }
??? }
? }
})
type
? 可以是下面原生構(gòu)造器:
String? Number?
? Boolean? Function? Object?
? Array? Symbol
type
? 也可以是一個(gè)自定義構(gòu)造器函數(shù),使用 instanceof 檢測(cè)。
當(dāng)? prop 驗(yàn)證失敗,Vue 會(huì)拋出警告 (如果使用的是開發(fā)版本)。注意 prop 會(huì)在組件實(shí)例創(chuàng)建之前進(jìn)行校驗(yàn),所以在 default 或? validator 函數(shù)里,諸如 data、computed 或? methods 等實(shí)例屬性還無法使用。
4、非prop特性
所謂非? prop 特性,就是指它可以直接傳入組件,而不需要定義相應(yīng)的? prop。盡管為組件定義明確的 prop 是推薦的傳參方式,組件的作者卻并不總能預(yù)見到組件被使用的場(chǎng)景。所以,組件可以接收任意傳入的特性,這些特性都會(huì)被添加到組件的根元素上。
替換/合并現(xiàn)有的特性
對(duì)于多數(shù)特性來說,傳遞給組件的值會(huì)覆蓋組件本身設(shè)定的值。即例如傳遞 type="large" 將會(huì)覆蓋 type="date" 且有可能破壞該組件!所幸我們對(duì)待 class 和? style 特性會(huì)更聰明一些,這兩個(gè)特性的值都會(huì)做合并? (merge) 操作,讓最終生成的值為:form-control
? date-picker-theme-dark。
5、自定義事件
使用v-on綁定自定義事件:每個(gè) Vue 實(shí)例都實(shí)現(xiàn)了事件接口,即:
使用? $on(eventName) 監(jiān)聽事件
使用? $emit(eventName, optionalPayload) 觸發(fā)事件
父組件可以在使用子組件的地方直接用 v-on 來監(jiān)聽子組件觸發(fā)的事件。
給組件綁定原生事件
有時(shí)候,你可能想在某個(gè)組件的根元素上監(jiān)聽一個(gè)原生事件。可以使用 v-on 的修飾符.native
.sync修飾符
.sync
? 還是有其適用之處,比如在開發(fā)可復(fù)用的組件庫時(shí)。我們需要做的只是讓子組件改變父組件狀態(tài)的代碼更容易被區(qū)分。
使用自定義事件的表單輸入組件:
自定義事件可以用來創(chuàng)建自定義的表單輸入組件,使用 v-model 來進(jìn)行數(shù)據(jù)雙向綁定。
自定義組件的v-model
默認(rèn)情況下,一個(gè)組件的? v-model 會(huì)使用 value prop 和 input 事件。但是諸如單選框、復(fù)選框之類的輸入類型可能把 value 用作了別的目的。model
? 選項(xiàng)可以避免這樣的沖突。
非父子組件的通信
有時(shí)候,非父子關(guān)系的兩個(gè)組件之間也需要通信。在簡單的場(chǎng)景下,可以使用一個(gè)空的 Vue 實(shí)例作為事件總線:
6、使用插槽分發(fā)內(nèi)容
為了讓組件可以組合,我們需要一種方式來混合父組件的內(nèi)容與子組件自己的模板。這個(gè)過程被稱為內(nèi)容分發(fā) (即? Angular 用戶熟知的“transclusion”)。Vue.js 實(shí)現(xiàn)了一個(gè)內(nèi)容分發(fā)? API,參照了當(dāng)前 Web Components 規(guī)范草案,使用特殊的? <slot> 元素作為原始內(nèi)容的插槽。
編譯作用域:父組件模板的內(nèi)容在父組件作用域內(nèi)編譯;子組件模板的內(nèi)容在子組件作用域內(nèi)編譯。
單個(gè)插槽:除非子組件模板包含至少一個(gè) <slot> 插口,否則父組件的內(nèi)容將會(huì)被丟棄。當(dāng)子組件模板只有一個(gè)沒有屬性的插槽時(shí),父組件傳入的整個(gè)內(nèi)容片段將插入到插槽所在的 DOM 位置,并替換掉插槽標(biāo)簽本身。
具名插槽:<slot>
? 元素可以用一個(gè)特殊的特性 name 來進(jìn)一步配置如何分發(fā)內(nèi)容。多個(gè)插槽可以有不同的名字。具名插槽將匹配內(nèi)容片段中有對(duì)應(yīng) slot 特性的元素。仍然可以有一個(gè)匿名插槽,它是默認(rèn)插槽,作為找不到匹配的內(nèi)容片段的備用插槽。如果沒有默認(rèn)插槽,這些找不到匹配的內(nèi)容片段將被拋棄。
作用域插槽:作用域插槽是一種特殊類型的插槽,用作一個(gè) (能被傳遞數(shù)據(jù)的)
? 可重用模板,來代替已經(jīng)渲染好的元素。
7、動(dòng)態(tài)組件
通過使用保留的? <component> 元素,并對(duì)其 is 特性進(jìn)行動(dòng)態(tài)綁定,你可以在同一個(gè)掛載點(diǎn)動(dòng)態(tài)切換多個(gè)組件:
var? ? vm = new Vue({
? el: '#example',
? data: {
??? currentView: 'home'
? },
? components: {
??? home: { /* ... */ },
??? posts: { /* ... */ },
??? archive: { /* ... */ }
? }
})
組件在 vm.currentview 變化時(shí)改變!-->
也可以直接綁定到組件對(duì)象上:
var? Home = {
? template: '
Welcome? home!
'
}
var vm = new Vue({
? el: '#example',
? data: {
??? currentView: Home
? }
})
如果把切換出去的組件保留在內(nèi)存中,可以保留它的狀態(tài)或避免重新渲染。為此可以添加一個(gè) keep-alive 指令參數(shù):
非活動(dòng)組件將被緩存!-->
8、雜項(xiàng)
編寫可復(fù)用組件:在編寫組件時(shí),最好考慮好以后是否要進(jìn)行復(fù)用。一次性組件間有緊密的耦合沒關(guān)系,但是可復(fù)用組件應(yīng)當(dāng)定義一個(gè)清晰的公開接口,同時(shí)也不要對(duì)其使用的外層數(shù)據(jù)作出任何假設(shè)。
Vue
? 組件的 API 來自三部分——prop、事件和插槽:
(1)Prop 允許外部環(huán)境傳遞數(shù)據(jù)給組件;
(2)事件允許從組件內(nèi)觸發(fā)外部環(huán)境的副作用;
(3)插槽允許外部環(huán)境將額外的內(nèi)容組合在組件中。
子組件引用
盡管有? prop 和事件,但是有時(shí)仍然需要在 JavaScript 中直接訪問子組件。為此可以使用 ref 為子組件指定一個(gè)引用? ID。
var? parent = new Vue({ el: '#parent' })
//
訪問子組件實(shí)例
var child = parent.$refs.profile
當(dāng)? ref 和 v-for 一起使用時(shí),獲取到的引用會(huì)是一個(gè)數(shù)組,包含和循環(huán)數(shù)據(jù)源對(duì)應(yīng)的子組件。
異步組件:在大型應(yīng)用中,我們可能需要將應(yīng)用拆分為多個(gè)小模塊,按需從服務(wù)器下載。為了進(jìn)一步簡化,Vue.js 允許將組件定義為一個(gè)工廠函數(shù),異步地解析組件的定義。Vue.js 只在組件需要渲染時(shí)觸發(fā)工廠函數(shù),并且把結(jié)果緩存起來,用于后面的再次渲染。
Vue.component('async-example',? function (resolve, reject) {
? setTimeout(function () {
??? //
將組件定義傳入? resolve 回調(diào)函數(shù)
??? resolve({
????? template: '
I am? async!
'
??? })
? }, 1000)
})
組件命名約定:當(dāng)注冊(cè)組件? (或者 prop) 時(shí),可以使用? kebab-case (短橫線分隔命名)、camelCase (駝峰式命名)
? 或 PascalCase (單詞首字母大寫命名)。
遞歸組件:組件在它的模板內(nèi)可以遞歸地調(diào)用自己。不過,只有當(dāng)它有 name 選項(xiàng)時(shí)才可以這么做,當(dāng)你利用 Vue.component 全局注冊(cè)了一個(gè)組件,全局的 ID 會(huì)被自動(dòng)設(shè)置為組件的? name。要確保遞歸調(diào)用有終止條件 (比如遞歸調(diào)用時(shí)使用 v-if 并最終解析為? false)。
組件間的循環(huán)引用:假設(shè)你正在構(gòu)建一個(gè)文件目錄樹,像在 Finder 或資源管理器中。你可能有一個(gè) tree-folder 組件以及一個(gè)? tree-folder-contents 組件,在我們的例子中,可以選擇讓 tree-folder 組件中來做這件事。我們知道引起矛盾的子組件是 tree-folder-contents,所以我們要等到 beforeCreate 生命周期鉤子中才去注冊(cè)它。
內(nèi)聯(lián)模板:如果子組件有? inline-template 特性,組件將把它的內(nèi)容當(dāng)作它的模板,而不是把它當(dāng)作分發(fā)內(nèi)容。這讓模板編寫起來更靈活。
這些將作為組件自身的模板。
而非父組件透傳進(jìn)來的內(nèi)容。
inline-template
? 讓模板的作用域難以理解。使用 template 選項(xiàng)在組件內(nèi)定義模板或者在 .vue 文件中使用? template 元素才是最佳實(shí)踐。
X-template:另一種定義模板的方式是在 JavaScript 標(biāo)簽里使用? text/x-template 類型,并且指定一個(gè) id。
Hello hello? ? hello
Vue.component('hello-world',? ? {
? template: '#hello-world-template'
})
這在有很多大模板的演示應(yīng)用或者特別小的應(yīng)用中可能有用,其它場(chǎng)合應(yīng)該避免使用,因?yàn)檫@將模板和組件的其它定義分離了。
對(duì)低開銷的靜態(tài)組件使用? v-once:盡管在 Vue 中渲染 HTML 很快,不過當(dāng)組件中包含大量靜態(tài)內(nèi)容時(shí),可以考慮使用 v-once 將渲染結(jié)果緩存起來,就像這樣:
Vue.component('terms-of-service',? ? {
? template: '\
\
Terms of? ? Service
\
????? ...
很多靜態(tài)內(nèi)容...\
??? \
? '
})
十、組件注冊(cè)
1、組件名
給予組件的名字可能依賴于你打算拿它來做什么。當(dāng)直接在 DOM 中使用一個(gè)組件? (而不是在字符串模板或單文件組件)的時(shí)候,我們強(qiáng)烈推薦遵循W3C規(guī)范中的自定義組件名 (字母全小寫且必須包含一個(gè)連字符)。
當(dāng)使用? kebab-case (短橫線分隔命名) 定義一個(gè)組件時(shí),你也必須在引用這個(gè)自定義元素時(shí)使用 kebab-case,例如? <my-component-name>。
當(dāng)使用? PascalCase (駝峰式命名) 定義一個(gè)組件時(shí),你在引用這個(gè)自定義元素時(shí)兩種命名法都可以使用。也就是說 <my-component-name> 和 <MyComponentName> 都是可接受的。注意,盡管如此,直接在 DOM (即非字符串的模板)
? 中使用時(shí)只有 kebab-case 是有效的。
2、全局注冊(cè)
到目前為止,我們只用過? Vue.component 來創(chuàng)建組件,這些組件是全局注冊(cè)的。也就是說它們?cè)谧?cè)之后可以用在任何新創(chuàng)建的 Vue 根實(shí)例? (new Vue) 的模板中。
3、局部注冊(cè)
4、模塊系統(tǒng)
在模塊系統(tǒng)中局部注冊(cè):
需要在局部注冊(cè)之前導(dǎo)入每個(gè)你想使用的組件。例如,在一個(gè)假設(shè)的 ComponentB.js 或? ComponentB.vue 文件中:
import? ? ComponentA from './ComponentA'
import ComponentC from './ComponentC'
export default {
? components: {
??? ComponentA,
??? ComponentC
? },
? // ...
}
現(xiàn)在? ComponentA 和 ComponentC 都可以在 ComponentB 的模板中使用了。
基礎(chǔ)組件的自動(dòng)化全局注冊(cè)
可能你的許多組件只是包裹了一個(gè)輸入框或按鈕之類的元素,是相對(duì)通用的。我們有時(shí)候會(huì)把它們稱為基礎(chǔ)組件,它們會(huì)在各個(gè)組件中被頻繁的用到。
所以會(huì)導(dǎo)致很多組件里都會(huì)有一個(gè)包含基礎(chǔ)組件的長列表: