Vue.js(讀音/vju?/, 類似于 view)是一個構(gòu)建數(shù)據(jù)驅(qū)動的web 界面的漸進式框架布疙。Vue.js的目標是通過盡可能簡單的API 實現(xiàn)響應的數(shù)據(jù)綁定和組合的視圖組件。只聚焦于視圖層乎莉。
一埠啃、Vue實例
1死宣、創(chuàng)建一個vue實例
每個 Vue 應用都是通過用Vue 函數(shù)創(chuàng)建一個新的Vue 實例開始的:
?????? var vm = new Vue({ //選項? })
2、數(shù)據(jù)與方法
當一個Vue 實例被創(chuàng)建時碴开,它向Vue 的響應式系統(tǒng)中加入了其data 對象中能找到的所有的屬性毅该。當這些屬性的值發(fā)生改變時,視圖將會產(chǎn)生“響應”潦牛,即匹配更新為新的值眶掌。
只有當實例被創(chuàng)建時data 中存在的屬性才是響應式的。也就是說如果你添加一個新的屬性巴碗,那么對它的改動將不會觸發(fā)任何視圖的更新
使用 Object.freeze()畏线,這會阻止修改現(xiàn)有的屬性,也意味著響應系統(tǒng)無法再追蹤變化良价。
除了數(shù)據(jù)屬性,Vue 實例還暴露了一些有用的實例屬性與方法蒿叠。它們都有前綴$明垢,以便與用戶定義的屬性區(qū)分開來。
二市咽、模板語法
1痊银、插值
數(shù)據(jù)綁定最常見的形式就是使用“Mustache”語法(雙大括號)的文本插值:Mustache標簽將會被替代為對應數(shù)據(jù)對象上msg 屬性的值。無論何時施绎,綁定的數(shù)據(jù)對象上msg 屬性發(fā)生了改變溯革,插值處的內(nèi)容都會更新贞绳。
<span>Message:{{ msg }}</span>
通過使用v-once 指令撑蒜,你也能執(zhí)行一次性地插值垮衷,當數(shù)據(jù)改變時,插值處的內(nèi)容不會更新册招。
<span v-once>Message:{{msg}}</span>
?????? 原始html:雙大括號會將數(shù)據(jù)解釋為普通文本抖单,而非HTML 代碼萎攒。為了輸出真正的HTML,你需要使用v-html 指令:
<p>Using v-html directive:<span v-html="rowhtml"></span></p>
?????? 特性:Mustache語法不能作用在HTML 特性上矛绘,遇到這種情況應該使用v-bind 指令:
<p v-bind:id="textid"></p>
?????? 使用JavaScript表達式:對于所有的數(shù)據(jù)綁定耍休,Vue.js 都提供了完全的JavaScript 表達式支持。這些表達式會在所屬 Vue 實例的數(shù)據(jù)作用域下作為JavaScript 被解析货矮。有個限制就是羊精,每個綁定都只能包含單個表達式,所以下面的例子都不會生效囚玫。
<!-- 這是語句喧锦,不是表達式-->
{{ var a = 1 }}
流控制也不會生效,請使用三元表達式-->
{{ if (ok) { return message } }}
2劫灶、指令
?????? 指令 (Directives) 是帶有v- 前綴的特殊屬性裸违。指令屬性的值預期是單個JavaScript 表達式 (v-for 是例外情況,稍后我們再討論)本昏。指令的職責是供汛,當表達式的值改變時,將其產(chǎn)生的連帶影響涌穆,響應式地作用于DOM怔昨。
<p? v-if="seen">現(xiàn)在你看到我了</p>
v-if 指令將根據(jù)表達式seen 的值的真假來插入/移除<p> 元素。
?????? 參數(shù):一些指令能夠接收一個“參數(shù)”宿稀,在指令名稱之后以冒號表示趁舀。例如,v-bind指令可以用于響應式地更新HTML 屬性:
<a v-bind:href="href"></a>
<a :href="href"></a>
在這里 href 是參數(shù)祝沸,告知v-bind 指令將該元素的 href 屬性與表達式url 的值綁定矮烹。
另一個例子是v-on指令,它用于監(jiān)聽DOM事件罩锐,在這里參數(shù)是監(jiān)聽的事件名奉狈。
<a v-on:click="dosomething">...</a>
<a @click="dosomething"></a>
可以用方括號括起來的 JavaScript 表達式作為一個指令的參數(shù)
<a v-bind:[name]="textid"></a>
name其值為id,則上式可以轉(zhuǎn)換成<a v-bind:id="textid"></a>
?????? 修飾符:修飾符(Modifiers) 是以半角句號 . 指明的特殊后綴涩惑,用于指出一個指令應該以特殊方式綁定仁期,例如:
.prevent 修飾符告訴v-on 指令對于觸發(fā)的事件調(diào)event.preventDefault():
<form v-on:submit.prevent="onSubmit"></form>
三、計算屬性和偵聽器
1、計算屬性
<p>Computed reversed message: "{{ reversedMessage }}"</p>
computed: {
??? //計算屬性的getter
??? reversedMessage: function () {
????? // `this`指向vm 實例
????? return this.message.split('').reverse().join('')
??? }
? }
2跛蛋、在表達式中調(diào)用方法
<p>Reversedmessage: "{{ reversedMessage() }}"</p>
// 在組件中
methods: {
? reversedMessage: function () {
??? return this.message.split('').reverse().join('')
? }
}
3熬的、計算屬性vs方法
?????? 兩種方式的最終結(jié)果確實是完全相同的。然而赊级,不同的是計算屬性是基于它們的響應式依賴進行緩存的押框。計算屬性只有在它的相關(guān)響應式依賴發(fā)生改變時才會重新求值。這就意味著只要message 還沒有發(fā)生改變此衅,多次訪問 reversedMessage 計算屬性會立即返回之前的計算結(jié)果强戴,而不必再次執(zhí)行函數(shù)。相比之下挡鞍,每當觸發(fā)重新渲染時骑歹,調(diào)用方法將總會再次執(zhí)行函數(shù)。
4墨微、偵聽屬性
?????? 當你有一些數(shù)據(jù)需要隨著其它數(shù)據(jù)變動而變動時道媚,你很容易濫用watch
watch: {
??? firstName: function (val) {
????? this.fullName = val + ' ' +this.lastName
??? },
??? lastName: function (val) {
????? this.fullName = this.firstName + '' + val
??? }
? }
5、計算屬性vs偵聽屬性
computed: {
??? fullName: function () {
????? return this.firstName + ' ' +this.lastName
??? }
? }
偵聽屬性代碼命令式且重復
6翘县、計算屬性的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最域、偵聽器
?????? 雖然計算屬性在大多數(shù)情況下更合適,但有時也需要一個自定義的偵聽器锈麸。Vue通過watch 選項提供了一個更通用的方法镀脂,來響應數(shù)據(jù)的變化。當需要在數(shù)據(jù)變化時執(zhí)行異步或開銷較大的操作時忘伞,這個方式是最有用的薄翅。
????使用?watch?選項允許我們執(zhí)行異步操作 (訪問一個 API),限制我們執(zhí)行該操作的頻率氓奈,并在我們得到最終結(jié)果前翘魄,設置中間狀態(tài)。這些都是計算屬性無法做到的舀奶。
四暑竟、class與style綁定
1、綁定HTMLClass
?????? 對象語法:
v-bind:class 指令也可以與普通的class 屬性共存
綁定的數(shù)據(jù)對象不必內(nèi)聯(lián)定義在模板里:
data:? {
? classObject: {
??? active: true,
??? 'text-danger': false
? }
}
數(shù)組語法:
<div v-bind:class="[activeClass,errorClass]"></div>
把一個數(shù)組傳給v-bind:class育勺,以應用一個 class 列表:
data:? {
? activeClass: 'active',
? errorClass: 'text-danger'
}
渲染為:<div class="active text-danger"></div>
用在組件上:當在一個自定義組件上使用class 屬性時但荤,這些類將被添加到該組件的根元素上面。這個元素上已經(jīng)存在的類不會被覆蓋涧至。
?2纱兑、綁定內(nèi)聯(lián)樣式
對象語法:
<div v-bind:style="{color:activeColor,fontSize:fontSize+'px'}"></div>
data: {
? activeColor: 'red',
? fontSize: 30
}
直接綁定到一個樣式對象通常更好,這會讓模板更清晰:
<div v-bind:style="styleObject"></div>
data:? {
? styleObject: {
??? color: 'red',
??? fontSize: '13px'
? }
}
數(shù)組語法:
v-bind:style 的數(shù)組語法可以將多個樣式對象應用到同一個元素上:
<div :style="[baseStyles, overridingStyles]"></div>
自動添加前綴:
當v-bind:style 使用需要添加瀏覽器引擎前綴的 CSS 屬性時化借,如transform,Vue.js 會自動偵測并添加相應的前綴捡多。
多重值
可以為style 綁定中的屬性提供一個包含多個值的數(shù)組蓖康,常用于提供多個帶前綴的值铐炫,例如:
<div :style="{display:['-webkit-box', '-ms-flexbox', 'flex']}"></div>
這樣寫只會渲染數(shù)組中最后一個被瀏覽器支持的值。在本例中蒜焊,如果瀏覽器支持不帶瀏覽器前綴的flexbox倒信,那么就只會渲染 display: flex。
五泳梆、條件渲染
1.v-if
在<template> 元素上使用 v-if 條件渲染分組:
因為v-if 是一個指令鳖悠,所以必須將它添加到一個元素上。但是如果想切換多個元素呢优妙?此時可以把一個<template> 元素當做不可見的包裹元素乘综,并在上面使用 v-if。最終的渲染結(jié)果將不包含<template> 元素套硼。
?????? 可以使用v-else 指令來表示 v-if 的“else塊”卡辰,v-else 元素必須緊跟在帶 v-if 或者v-else-if 的元素的后面,否則它將不會被識別邪意,v-else-if 也必須緊跟在帶v-if 或者v-else-if 的元素之后九妈。
用key管理可復用元素
?????? Vue為你提供了一種方式來表達“兩個元素是完全獨立的,不要復用它們”雾鬼。只需添加一個具有唯一值的key 屬性即可:
2萌朱、v-show
另一個用于根據(jù)條件展示元素的選項是v-show 指令:
不同的是帶有v-show 的元素始終會被渲染并保留在 DOM 中。v-show只是簡單地切換元素的CSS 屬性display策菜。v-show 不支持<template> 元素晶疼,也不支持 v-else。
3做入、v-if vs v-show
v-if 是“真正”的條件渲染冒晰,因為它會確保在切換過程中條件塊內(nèi)的事件監(jiān)聽器和子組件適當?shù)乇讳N毀和重建。v-if也是惰性的:如果在初始渲染時條件為假竟块,則什么也不做——直到條件第一次變?yōu)檎鏁r壶运,才會開始渲染條件塊。
相比之下浪秘,v-show 就簡單得多——不管初始條件是什么蒋情,元素總是會被渲染,并且只是簡單地基于CSS 進行切換耸携。
一般來說棵癣,v-if 有更高的切換開銷,而v-show 有更高的初始渲染開銷夺衍。因此狈谊,如果需要非常頻繁地切換,則使用v-show 較好;如果在運行時條件很少改變河劝,則使用 v-if 較好壁榕。
4、v-if 與v-for?
當v-if 與v-for 一起使用時赎瞎,v-for 具有比v-if 更高的優(yōu)先級牌里。
六、列表渲染
1务甥、用v-for把一個數(shù)組對應為一組元素
用 v-for 指令根據(jù)一組數(shù)組的選項列表進行渲染牡辽。v-for指令需要使用item in items 形式的特殊語法,items 是源數(shù)據(jù)數(shù)組并且item 是數(shù)組元素迭代的別名敞临。
?????? 在v-for 塊中态辛,我們擁有對父作用域?qū)傩缘耐耆L問權(quán)限。v-for還支持一個可選的第二個參數(shù)為當前項的索引哟绊。
<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因妙、一個對象的v-for
可以用v-for 通過一個對象的屬性來迭代。
<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
??? }
? }
})
也可以提供第二個的參數(shù)為鍵名:
? {{ key }}: {{ value }}
第三個參數(shù)為索引:
? {{ index }}. {{ key }}: {{ value }}
3票髓、key
為了給Vue 一個提示攀涵,以便它能跟蹤每個節(jié)點的身份,從而重用和重新排序現(xiàn)有元素洽沟,你需要為每項提供一個唯一key 屬性以故。理想的key 值是每項都有的且唯一的id。這個特殊的屬性相當于Vue 1.x 的 track-by 裆操,但它的工作方式類似于一個屬性怒详,所以你需要用v-bind 來綁定動態(tài)值。建議盡可能在使用 v-for 時提供key踪区,因為它是Vue 識別節(jié)點的一個通用機制昆烁,key并不與v-for 特別關(guān)聯(lián)。
4缎岗、數(shù)組更新檢測
?變異方法:
Vue 包含一組觀察數(shù)組的變異方法静尼,所以它們也將會觸發(fā)視圖更新。
push()? pop()?shift()? unshift()? splice()?sort()? reverse()
變異方法(mutation method)传泊,顧名思義鼠渺,會改變被這些方法調(diào)用的原始數(shù)組。
替換數(shù)組:非變異(non-mutating method) 方法眷细,例如:filter(),
concat() 和 slice() 拦盹。這些不會改變原始數(shù)組,但總是返回一個新數(shù)組溪椎。當使用非變異方法時普舆,可以用新數(shù)組替換舊數(shù)組:
注意事項:
由于JavaScript 的限制恬口,Vue 不能檢測以下變動的數(shù)組:
當你利用索引直接設置一個項時:
?????? Vue.set(vm.items, indexOfItem, newValue)
也可以使用vm.$set實例方法,該方法是全局方法 Vue.set 的一個別名:
?????? vm.$set(vm.items, indexOfItem, newValue)
?????? 當你修改數(shù)組的長度時
?????? 使用vm.items.splice(newLength)
5奔害、對象更改檢測注意事項
?????? 由于JavaScript 的限制楷兽,Vue 不能檢測對象屬性的添加或刪除:可以使用
Vue.set(object,key, value) 方法向嵌套對象添加響應式屬性,也可以使用vm.$set 實例方法华临,它只是全局 Vue.set 的別名:Vue.$set(object,key, value)
?????? 有時你可能需要為已有對象賦予多個新屬性,比如使用Object.assign() 或 _.extend()端考。在這種情況下雅潭,你應該用兩個對象的屬性創(chuàng)建一個新的對象
vm.userProfile =Object.assign({}, vm.userProfile, {
? age: 27,
? favoriteColor: 'Vue Green'
})
6、顯示過濾/排序結(jié)果
有時却特,我們想要顯示一個數(shù)組的過濾或排序副本扶供,而不實際改變或重置原始數(shù)據(jù)。在這種情況下裂明,可以創(chuàng)建返回過濾或排序數(shù)組的計算屬性椿浓。
<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
??? })
? }
}
在計算屬性不適用的情況下(例如,在嵌套v-for 循環(huán)中) 你可以使用一個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> 渲染多個元素仙蛉。
9笋敞、v-for with v-if
當它們處于同一節(jié)點,v-for的優(yōu)先級比v-if 更高荠瘪,這意味著v-if 將分別重復運行于每個v-for 循環(huán)中:
10夯巷、一個組件的v-for
在自定義組件里,你可以像任何普通元素一樣用 v-for 哀墓。
當在組件中使用 v-for 時趁餐,key 現(xiàn)在是必須的。
然而篮绰,任何數(shù)據(jù)都不會被自動傳遞到組件里后雷,因為組件有自己獨立的作用域。為了把迭代數(shù)據(jù)傳遞到組件里阶牍,我們要用 props :
七喷面、事件處理
1、監(jiān)聽事件
可以用v-on 指令監(jiān)聽DOM 事件走孽,并在觸發(fā)時運行一些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 還可以接收一個需要調(diào)用的方法名稱盒齿。
3念逞、內(nèi)聯(lián)處理器中的方法
除了直接綁定到一個方法,也可以在內(nèi)聯(lián)JavaScript 語句中調(diào)用方法边翁,有時也需要在內(nèi)聯(lián)語句處理器中訪問原始的DOM 事件翎承。可以用特殊變量$event 把它傳入方法符匾。
4叨咖、事件修飾符
方法只有純粹的數(shù)據(jù)邏輯,而不是去處理DOM 事件細節(jié)啊胶。解決了這個問題甸各,Vue.js為v-on 提供了事件修飾符,修飾符是由點開頭的指令后綴來表示的焰坪。
<!-- 阻止單擊事件繼續(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)聽器時使用事件捕獲模式,即元素自身觸發(fā)的事件先在此處處理趣倾,然后才交由內(nèi)部元素進行處理-->
<div v-on:click.capture="dothis"></div>
只當在 event.target 是當前元素自身時觸發(fā)處理函數(shù),即事件不是從內(nèi)部元素觸發(fā)的-->
<div v-on:click.self="dothis"></div>
使用修飾符時,順序很重要某饰。
<!-- 點擊事件將只會觸發(fā)一次-->
<a v-on:click.once="dothis"></a>
<!-- 滾動事件的默認行為(即滾動行為)將會立即觸發(fā)儒恋,而不會等待 `onScroll` 完成,這其中包含 `event.preventDefault()`的情況-->
<div v-on:scroll.passive="onScroll"></div>
不要把.passive 和 .prevent 一起使用黔漂,因為.prevent 將會被忽略诫尽,同時瀏覽器可能會向你展示一個警告。請記住瘟仿,.passive會告訴瀏覽器你不想阻止事件的默認行為箱锐。
5、按鍵修飾符
<!-- 只有在`keyCode` 是 13 時調(diào)用`vm.submit()` -->
縮寫語法-->
全部的按鍵別名:
.enter? .tab?.delete (捕獲“刪除”和“退格”鍵)? .esc?.space? .up? .down?.left? .right
可以通過全局vue.config.keyCodes 對象自定義按鍵修飾符別名
自動匹配按鍵修飾符
你也可直接將KeyboardEvent.key暴露的任意有效按鍵名轉(zhuǎn)換為kebab-case 來作為修飾符:
在上面的例子中劳较,處理函數(shù)僅在$event.key === 'PageDown' 時被調(diào)用驹止。
6、系統(tǒng)修飾符
可以用如下修飾符來實現(xiàn)僅在按下相應按鍵時才觸發(fā)鼠標或鍵盤事件的監(jiān)聽器观蜗。
.ctrl? .alt?.shift? .meta
.exact 修飾符允許你控制由精確的系統(tǒng)修飾符組合觸發(fā)的事件臊恋。
?????? 鼠標按鈕修飾符.left? .right? .middle這些修飾符會限制處理函數(shù)僅響應特定的鼠標按鈕。
7墓捻、為什么在HTML中監(jiān)聽事件
所有的Vue.js 事件處理方法和表達式都嚴格綁定在當前視圖的ViewModel 上抖仅,它不會導致任何維護上的困難。實際上砖第,使用v-on 有幾個好處:
1撤卢、掃一眼HTML 模板便能輕松定位在JavaScript 代碼里對應的方法。
2梧兼、因為你無須在JavaScript 里手動綁定事件放吩,你的 ViewModel 代碼可以是非常純粹的邏輯,和DOM 完全解耦羽杰,更易于測試渡紫。
3到推、當一個ViewModel 被銷毀時,所有的事件處理器都會自動被刪除惕澎。你無須擔心如何自己清理它們莉测。
八、表單輸入綁定
1唧喉、基礎用法
你可以用v-model 指令在表單 <input> 及<textarea> 元素上創(chuàng)建雙向數(shù)據(jù)綁定捣卤。它會根據(jù)控件類型自動選取正確的方法來更新元素。
<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八孝、值綁定
有時我們可能想把值綁定到Vue 實例的一個動態(tài)屬性上腌零,這時可以用v-bind 實現(xiàn),并且這個屬性的值可以不是字符串唆阿。
3、修飾符
?????? .lazy:在默認情況下锈锤,v-model在每次input 事件觸發(fā)后將輸入框的值與數(shù)據(jù)進行同步 (除了上述輸入法組合文字時)驯鳖。你可以添加lazy 修飾符,從而轉(zhuǎn)變?yōu)槭褂胏hange 事件進行同步:
?????? .number:如果想自動將用戶的輸入值轉(zhuǎn)為數(shù)值類型久免,可以給v-model 添加 number 修飾符:
???? .trim:如果要自動過濾用戶輸入的首尾空白字符浅辙,可以給v-model 添加 trim 修飾符:
4、在組件上使用v-model(后面返回來學習)
九阎姥、組件
1记舆、什么是組件
?????? 組件(Component) 是 Vue.js 最強大的功能之一。組件可以擴展HTML 元素呼巴,封裝可重用的代碼泽腮。在較高層面上,組件是自定義元素衣赶,Vue.js的編譯器為它添加特殊功能诊赊。在有些情況下,組件也可以表現(xiàn)為用is 特性進行了擴展的原生HTML 元素府瞄。
所有的Vue 組件同時也都是Vue 的實例碧磅,所以可接受相同的選項對象(除了一些根級特有的選項)并提供相同的生命周期鉤子。
2遵馆、使用組件
要注冊一個全局組件鲸郊,可以使用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>'})
組件在注冊之后货邓,便可以作為自定義元素 <my-component></my-component> 在一個實例的模板中使用秆撮。注意確保在初始化根實例之前注冊組件:
***一個組件的?data?選項必須是一個函數(shù),因此每個實例可以維護一份被返回對象的獨立的拷貝:
創(chuàng)建根實例
new Vue({
? el: '#example'
})
局部注冊:
你不必把每個組件都注冊到全局逻恐。你可以通過某個 Vue 實例/組件的實例選項 components 注冊僅在其作用域中可用的組件:
var? Child = {
? template: 'A custom? component!'
}
new Vue({
? // ...
? components: {
??? //
將只在父組件模板中可用
??? 'my-component': Child
? }
})
Dom模板解析注意事項:
當使用? DOM 作為模板時 (例如像吻,使用 el 選項來把? Vue 實例掛載到一個已有內(nèi)容的元素上)峻黍,你會受到 HTML 本身的一些限制,因為? Vue 只有在瀏覽器解析拨匆、規(guī)范化模板之后才能獲取其內(nèi)容姆涩,在自定義組件中使用這些受限制的元素時會導致一些問題,自定義組件 <my-row> 會被當作無效的內(nèi)容惭每,因此會導致錯誤的渲染結(jié)果骨饿。變通的方案是使用特殊的 is 特性:應當注意,如果使用來自以下來源之一的字符串模板台腥,則沒有這些限制:
JavaScript
? 內(nèi)聯(lián)模板字符串
.vue
? 組件
因此宏赘,請盡可能使用字符串模板。
Data必須是函數(shù):構(gòu)造 Vue 實例時傳入的各種選項大多數(shù)都可以在組件里使用黎侈。只有一個例外:data 必須是函數(shù)察署。
組件組合:組件設計初衷就是要配合使用的,最常見的就是形成父子組件的關(guān)系:組件 A 在它的模板中使用了組件? B峻汉。在 Vue 中贴汪,父子組件的關(guān)系可以總結(jié)為 prop 向下傳遞,事件向上傳遞休吠。父組件通過 prop 給子組件下發(fā)數(shù)據(jù)扳埂,子組件通過事件給父組件發(fā)送消息。
3瘤礁、Prop
使用prop傳遞數(shù)據(jù)阳懂,組件實例的作用域是孤立的。這意味著不能 (也不應該)
? 在子組件的模板內(nèi)直接引用父組件的數(shù)據(jù)柜思。父組件的數(shù)據(jù)需要通過 prop 才能下發(fā)到子組件中岩调。
Vue.component('child',? {
? //
聲明props
? props: ['message'],
? //
就像? data 一樣,prop 也可以在模板中使用
? //
同樣也可以在? vm 實例中通過 this.message 來使用
? template: '{{ message? }}'
})
camelCase? vs. kebab-case
HTML
? 特性是不區(qū)分大小寫的酝蜒。所以誊辉,當使用的不是字符串模板時,camelCase (駝峰式命名)
? 的 prop 需要轉(zhuǎn)換為相對應的? kebab-case (短橫線分隔式命名):
Vue.component('child',? ? {
? //
在? ? JavaScript 中使用camelCase
? props: ['myMessage'],
? template: '{{ myMessage? ? }}'
})
<!--
? ? 在 HTML 中使用kebab-case -->
動態(tài)Prop
與綁定到任何普通的? HTML 特性相類似亡脑,我們可以用 v-bind 來動態(tài)地將 prop 綁定到父組件的數(shù)據(jù)堕澄。每當父組件的數(shù)據(jù)變化時,該變化也會傳導給子組件:
如果你想把一個對象的所有屬性作為 prop 進行傳遞霉咨,可以使用不帶任何參數(shù)的 v-bind (即用? v-bind 而不是v-bind:prop-name)
將等價于:
? v-bind:text="todo.text"
v-bind:is-complete="todo.isComplete"
>
字面量語法vs動態(tài)語法
初學者常犯的一個錯誤是使用字面量語法傳遞數(shù)值:
<!-- 傳遞了一個字符串"1" -->
因為它是一個字面量? ? prop蛙紫,它的值是字符串? ? "1" 而不是一個數(shù)值。如果想傳遞一個真正的? ? JavaScript 數(shù)值途戒,則需要使用 v-bind坑傅,從而讓它的值被當作 JavaScript 表達式計算:
<!--傳遞真正的數(shù)值-->
單向數(shù)據(jù)流
Prop
? ? 是單向綁定的:當父組件的屬性變化時,將傳導給子組件喷斋,但是反過來不會唁毒。在兩種情況下蒜茴,我們很容易忍不住想去修改 prop 中數(shù)據(jù):
Prop
? ? 作為初始值傳入后,子組件想把它當作局部數(shù)據(jù)來用浆西;
Prop
? ? 作為原始數(shù)據(jù)傳入粉私,由子組件處理成其它數(shù)據(jù)輸出。
?? 定義一個局部變量近零,并用 prop 的值初始化它:
props:? ? ? ['initialCounter'],
data: function () {
? return { counter:? ? ? this.initialCounter }
}
?? 定義一個計算屬性诺核,處理 prop 的值并返回:
props:? ? ? ['size'],
computed: {
? normalizedSize: function () {
??? return? ? ? this.size.trim().toLowerCase()
? }
}
Prop驗證
我們可以為組件的? prop 指定驗證規(guī)則。如果傳入的數(shù)據(jù)不符合要求久信,Vue
? 會發(fā)出警告窖杀,要指定驗證規(guī)則,需要用對象的形式來定義 prop裙士,而不能用字符串數(shù)組:
Vue.component('example',? {
? props: {
??? //
基礎類型檢測? (`null` 指允許任何類型)
??? propA: Number,
??? //
可能是多種類型
??? propB: [String, Number],
??? //
必傳且是字符串
??? propC: {
????? type: String,
????? required: true
??? },
??? //
數(shù)值且有默認值
??? propD: {
????? type: Number,
????? default: 100
??? },
??? //
數(shù)組/對象的默認值應當由一個工廠函數(shù)返回
??? propE: {
????? type: Object,
????? default: function () {
??????? return { message: 'hello' }
????? }
??? },
??? //
自定義驗證函數(shù)
??? propF: {
????? validator: function (value) {
??????? return value > 10
????? }
??? }
? }
})
type
? 可以是下面原生構(gòu)造器:
String? Number?
? Boolean? Function? Object?
? Array? Symbol
type
? 也可以是一個自定義構(gòu)造器函數(shù)入客,使用 instanceof 檢測。
當? prop 驗證失敗腿椎,Vue 會拋出警告 (如果使用的是開發(fā)版本)痊项。注意 prop 會在組件實例創(chuàng)建之前進行校驗,所以在 default 或? validator 函數(shù)里酥诽,諸如 data、computed 或? methods 等實例屬性還無法使用皱埠。
4肮帐、非prop特性
所謂非? prop 特性,就是指它可以直接傳入組件边器,而不需要定義相應的? prop训枢。盡管為組件定義明確的 prop 是推薦的傳參方式,組件的作者卻并不總能預見到組件被使用的場景忘巧。所以恒界,組件可以接收任意傳入的特性,這些特性都會被添加到組件的根元素上砚嘴。
替換/合并現(xiàn)有的特性
對于多數(shù)特性來說十酣,傳遞給組件的值會覆蓋組件本身設定的值。即例如傳遞 type="large" 將會覆蓋 type="date" 且有可能破壞該組件际长!所幸我們對待 class 和? style 特性會更聰明一些耸采,這兩個特性的值都會做合并? (merge) 操作,讓最終生成的值為:form-control
? date-picker-theme-dark工育。
5虾宇、自定義事件
使用v-on綁定自定義事件:每個 Vue 實例都實現(xiàn)了事件接口,即:
使用? $on(eventName) 監(jiān)聽事件
使用? $emit(eventName, optionalPayload) 觸發(fā)事件
父組件可以在使用子組件的地方直接用 v-on 來監(jiān)聽子組件觸發(fā)的事件如绸。
給組件綁定原生事件
有時候嘱朽,你可能想在某個組件的根元素上監(jiān)聽一個原生事件旭贬。可以使用 v-on 的修飾符.native
.sync修飾符
.sync
? 還是有其適用之處搪泳,比如在開發(fā)可復用的組件庫時稀轨。我們需要做的只是讓子組件改變父組件狀態(tài)的代碼更容易被區(qū)分。
使用自定義事件的表單輸入組件:
自定義事件可以用來創(chuàng)建自定義的表單輸入組件森书,使用 v-model 來進行數(shù)據(jù)雙向綁定靶端。
自定義組件的v-model
默認情況下,一個組件的? v-model 會使用 value prop 和 input 事件凛膏。但是諸如單選框杨名、復選框之類的輸入類型可能把 value 用作了別的目的。model
? 選項可以避免這樣的沖突猖毫。
非父子組件的通信
有時候台谍,非父子關(guān)系的兩個組件之間也需要通信。在簡單的場景下吁断,可以使用一個空的 Vue 實例作為事件總線:
6趁蕊、使用插槽分發(fā)內(nèi)容
為了讓組件可以組合,我們需要一種方式來混合父組件的內(nèi)容與子組件自己的模板仔役。這個過程被稱為內(nèi)容分發(fā) (即? Angular 用戶熟知的“transclusion”)掷伙。Vue.js 實現(xiàn)了一個內(nèi)容分發(fā)? API,參照了當前 Web Components 規(guī)范草案又兵,使用特殊的? <slot> 元素作為原始內(nèi)容的插槽任柜。
編譯作用域:父組件模板的內(nèi)容在父組件作用域內(nèi)編譯;子組件模板的內(nèi)容在子組件作用域內(nèi)編譯沛厨。
單個插槽:除非子組件模板包含至少一個 <slot> 插口宙地,否則父組件的內(nèi)容將會被丟棄。當子組件模板只有一個沒有屬性的插槽時逆皮,父組件傳入的整個內(nèi)容片段將插入到插槽所在的 DOM 位置宅粥,并替換掉插槽標簽本身。
具名插槽:<slot>
? 元素可以用一個特殊的特性 name 來進一步配置如何分發(fā)內(nèi)容电谣。多個插槽可以有不同的名字秽梅。具名插槽將匹配內(nèi)容片段中有對應 slot 特性的元素。仍然可以有一個匿名插槽剿牺,它是默認插槽风纠,作為找不到匹配的內(nèi)容片段的備用插槽。如果沒有默認插槽牢贸,這些找不到匹配的內(nèi)容片段將被拋棄竹观。
作用域插槽:作用域插槽是一種特殊類型的插槽,用作一個 (能被傳遞數(shù)據(jù)的)
? 可重用模板,來代替已經(jīng)渲染好的元素臭增。
7懂酱、動態(tài)組件
通過使用保留的? <component> 元素,并對其 is 特性進行動態(tài)綁定誊抛,你可以在同一個掛載點動態(tài)切換多個組件:
var? ? vm = new Vue({
? el: '#example',
? data: {
??? currentView: 'home'
? },
? components: {
??? home: { /* ... */ },
??? posts: { /* ... */ },
??? archive: { /* ... */ }
? }
})
組件在 vm.currentview 變化時改變藻糖!-->
也可以直接綁定到組件對象上:
var? Home = {
? template: '
Welcome? home!
'
}
var vm = new Vue({
? el: '#example',
? data: {
??? currentView: Home
? }
})
如果把切換出去的組件保留在內(nèi)存中鳖敷,可以保留它的狀態(tài)或避免重新渲染。為此可以添加一個 keep-alive 指令參數(shù):
非活動組件將被緩存!-->
8正罢、雜項
編寫可復用組件:在編寫組件時巩步,最好考慮好以后是否要進行復用松忍。一次性組件間有緊密的耦合沒關(guān)系潭枣,但是可復用組件應當定義一個清晰的公開接口,同時也不要對其使用的外層數(shù)據(jù)作出任何假設宾毒。
Vue
? 組件的 API 來自三部分——prop驼修、事件和插槽:
(1)Prop 允許外部環(huán)境傳遞數(shù)據(jù)給組件;
(2)事件允許從組件內(nèi)觸發(fā)外部環(huán)境的副作用诈铛;
(3)插槽允許外部環(huán)境將額外的內(nèi)容組合在組件中乙各。
子組件引用
盡管有? prop 和事件,但是有時仍然需要在 JavaScript 中直接訪問子組件幢竹。為此可以使用 ref 為子組件指定一個引用? ID耳峦。
var? parent = new Vue({ el: '#parent' })
//
訪問子組件實例
var child = parent.$refs.profile
當? ref 和 v-for 一起使用時,獲取到的引用會是一個數(shù)組焕毫,包含和循環(huán)數(shù)據(jù)源對應的子組件妇萄。
異步組件:在大型應用中,我們可能需要將應用拆分為多個小模塊咬荷,按需從服務器下載。為了進一步簡化轻掩,Vue.js 允許將組件定義為一個工廠函數(shù)幸乒,異步地解析組件的定義。Vue.js 只在組件需要渲染時觸發(fā)工廠函數(shù)唇牧,并且把結(jié)果緩存起來罕扎,用于后面的再次渲染。
Vue.component('async-example',? function (resolve, reject) {
? setTimeout(function () {
??? //
將組件定義傳入? resolve 回調(diào)函數(shù)
??? resolve({
????? template: '
I am? async!
'
??? })
? }, 1000)
})
組件命名約定:當注冊組件? (或者 prop) 時丐重,可以使用? kebab-case (短橫線分隔命名)腔召、camelCase (駝峰式命名)
? 或 PascalCase (單詞首字母大寫命名)。
遞歸組件:組件在它的模板內(nèi)可以遞歸地調(diào)用自己扮惦。不過臀蛛,只有當它有 name 選項時才可以這么做,當你利用 Vue.component 全局注冊了一個組件,全局的 ID 會被自動設置為組件的? name浊仆。要確保遞歸調(diào)用有終止條件 (比如遞歸調(diào)用時使用 v-if 并最終解析為? false)客峭。
組件間的循環(huán)引用:假設你正在構(gòu)建一個文件目錄樹,像在 Finder 或資源管理器中抡柿。你可能有一個 tree-folder 組件以及一個? tree-folder-contents 組件舔琅,在我們的例子中,可以選擇讓 tree-folder 組件中來做這件事洲劣。我們知道引起矛盾的子組件是 tree-folder-contents备蚓,所以我們要等到 beforeCreate 生命周期鉤子中才去注冊它。
內(nèi)聯(lián)模板:如果子組件有? inline-template 特性囱稽,組件將把它的內(nèi)容當作它的模板郊尝,而不是把它當作分發(fā)內(nèi)容。這讓模板編寫起來更靈活粗悯。
這些將作為組件自身的模板虚循。
而非父組件透傳進來的內(nèi)容。
inline-template
? 讓模板的作用域難以理解样傍。使用 template 選項在組件內(nèi)定義模板或者在 .vue 文件中使用? template 元素才是最佳實踐横缔。
X-template:另一種定義模板的方式是在 JavaScript 標簽里使用? text/x-template 類型,并且指定一個 id衫哥。
Hello hello? ? hello
Vue.component('hello-world',? ? {
? template: '#hello-world-template'
})
這在有很多大模板的演示應用或者特別小的應用中可能有用茎刚,其它場合應該避免使用,因為這將模板和組件的其它定義分離了撤逢。
對低開銷的靜態(tài)組件使用? v-once:盡管在 Vue 中渲染 HTML 很快膛锭,不過當組件中包含大量靜態(tài)內(nèi)容時,可以考慮使用 v-once 將渲染結(jié)果緩存起來蚊荣,就像這樣:
Vue.component('terms-of-service',? ? {
? template: '\
\
Terms of? ? Service
\
????? ...
很多靜態(tài)內(nèi)容...\
??? \
? '
})
十初狰、組件注冊
1、組件名
給予組件的名字可能依賴于你打算拿它來做什么互例。當直接在 DOM 中使用一個組件? (而不是在字符串模板或單文件組件)的時候奢入,我們強烈推薦遵循W3C規(guī)范中的自定義組件名 (字母全小寫且必須包含一個連字符)。
當使用? kebab-case (短橫線分隔命名) 定義一個組件時媳叨,你也必須在引用這個自定義元素時使用 kebab-case腥光,例如? <my-component-name>。
當使用? PascalCase (駝峰式命名) 定義一個組件時糊秆,你在引用這個自定義元素時兩種命名法都可以使用武福。也就是說 <my-component-name> 和 <MyComponentName> 都是可接受的。注意痘番,盡管如此捉片,直接在 DOM (即非字符串的模板)
? 中使用時只有 kebab-case 是有效的平痰。
2、全局注冊
到目前為止界睁,我們只用過? Vue.component 來創(chuàng)建組件觉增,這些組件是全局注冊的。也就是說它們在注冊之后可以用在任何新創(chuàng)建的 Vue 根實例? (new Vue) 的模板中翻斟。
3逾礁、局部注冊
4、模塊系統(tǒng)
在模塊系統(tǒng)中局部注冊:
需要在局部注冊之前導入每個你想使用的組件访惜。例如嘹履,在一個假設的 ComponentB.js 或? ComponentB.vue 文件中:
import? ? ComponentA from './ComponentA'
import ComponentC from './ComponentC'
export default {
? components: {
??? ComponentA,
??? ComponentC
? },
? // ...
}
現(xiàn)在? ComponentA 和 ComponentC 都可以在 ComponentB 的模板中使用了。
基礎組件的自動化全局注冊
可能你的許多組件只是包裹了一個輸入框或按鈕之類的元素债热,是相對通用的砾嫉。我們有時候會把它們稱為基礎組件,它們會在各個組件中被頻繁的用到窒篱。
所以會導致很多組件里都會有一個包含基礎組件的長列表: