Vue.js每天必學(xué)之組件與組件間的通信
什么是組件?
組件(Component)是 Vue.js 最強(qiáng)大的功能之一。組件可以擴(kuò)展 HTML 元素,封裝可重用的代碼。在較高層面上,組件是自定義元素,Vue.js 的編譯器為它添加特殊功能。在有些情況下,組件也可以是原生 HTML 元素的形式,以 is 特性擴(kuò)展。
使用組件
注冊
之前說過,我們可以用 Vue.extend() 創(chuàng)建一個(gè)組件構(gòu)造器:
var MyComponent = Vue.extend({ // 選項(xiàng)... })
要把這個(gè)構(gòu)造器用作組件,需要用 `Vue.component(tag, constructor)` **注冊** :
// 全局注冊組件,tag 為 my-component Vue.component('my-component', MyComponent)
<p class="tip">對于自定義標(biāo)簽名字,Vue.js 不強(qiáng)制要求遵循 W3C 規(guī)則(小寫,并且包含一個(gè)短杠),盡管遵循這個(gè)規(guī)則比較好。
組件在注冊之后,便可以在父實(shí)例的模塊中以自定義元素 <my-component> 的形式使用。要確保在初始化根實(shí)例之前注冊了組件:
<div id="example"> <my-component></my-component> </div> // 定義 var MyComponent = Vue.extend({ template: '<div>A custom component!</div>' }) // 注冊 Vue.component('my-component', MyComponent) // 創(chuàng)建根實(shí)例 new Vue({ el: '#example' })
渲染為:
<div id="example"> <div>A custom component!</div> </div>
注意組件的模板替換了自定義元素,自定義元素的作用只是作為一個(gè)掛載點(diǎn)??梢杂脤?shí)例選項(xiàng) replace 決定是否替換。
局部注冊
不需要全局注冊每個(gè)組件??梢宰尳M件只能用在其它組件內(nèi),用實(shí)例選項(xiàng) components 注冊:
var Child = Vue.extend({ /* ... */ }) var Parent = Vue.extend({ template: '...', components: { // <my-component> 只能用在父組件模板內(nèi) 'my-component': Child } })
這種封裝也適用于其它資源,如指令、過濾器和過渡。
注冊語法糖
為了讓事件更簡單,可以直接傳入選項(xiàng)對象而不是構(gòu)造器給 Vue.component() 和 component 選項(xiàng)。Vue.js 在背后自動調(diào)用 Vue.extend():
// 在一個(gè)步驟中擴(kuò)展與注冊 Vue.component('my-component', { template: '<div>A custom component!</div>' }) // 局部注冊也可以這么做 var Parent = Vue.extend({ components: { 'my-component': { template: '<div>A custom component!</div>' } } })
組件選項(xiàng)問題
傳入 Vue 構(gòu)造器的多數(shù)選項(xiàng)也可以用在 Vue.extend() 中,不過有兩個(gè)特例: data 和 el。試想如果我們簡單地把一個(gè)對象作為 data 選項(xiàng)傳給 Vue.extend():
var data = { a: 1 } var MyComponent = Vue.extend({ data: data })
這么做的問題是 `MyComponent` 所有的實(shí)例將共享同一個(gè) `data` 對象!這基本不是我們想要的,因此我們應(yīng)當(dāng)使用一個(gè)函數(shù)作為 `data` 選項(xiàng),讓這個(gè)函數(shù)返回一個(gè)新對象:
var MyComponent = Vue.extend({ data: function () { return { a: 1 } } })
同理,`el` 選項(xiàng)用在 `Vue.extend()` 中時(shí)也須是一個(gè)函數(shù)。
模板解析
Vue 的模板是 DOM 模板,使用瀏覽器原生的解析器而不是自己實(shí)現(xiàn)一個(gè)。相比字符串模板,DOM 模板有一些好處,但是也有問題,它必須是有效的 HTML 片段。一些 HTML 元素對什么元素可以放在它里面有限制。常見的限制:
•a 不能包含其它的交互元素(如按鈕,鏈接)
•ul 和 ol 只能直接包含 li
•select 只能包含 option 和 optgroup
•table 只能直接包含 thead, tbody, tfoot, tr, caption, col, colgroup
•tr 只能直接包含 th 和 td
在實(shí)際中,這些限制會導(dǎo)致意外的結(jié)果。盡管在簡單的情況下它可能可以工作,但是你不能依賴自定義組件在瀏覽器驗(yàn)證之前的展開結(jié)果。例如 <my-select><option>...</option></my-select> 不是有效的模板,即使 my-select 組件最終展開為 <select>...</select>。
另一個(gè)結(jié)果是,自定義標(biāo)簽(包括自定義元素和特殊標(biāo)簽,如 <component>、<template>、 <partial> )不能用在 ul, select, table 等對內(nèi)部元素有限制的標(biāo)簽內(nèi)。放在這些元素內(nèi)部的自定義標(biāo)簽將被提到元素的外面,因而渲染不正確。
對于自定義元素,應(yīng)當(dāng)使用 is 特性:
<table> <tr is="my-component"></tr> </table>
`` 不能用在 `` 內(nèi),這時(shí)應(yīng)使用 ``,`
` 可以有多個(gè) ``:
<table> <tbody v-for="item in items"> <tr>Even row</tr> <tr>Odd row</tr> </tbody> </table>
Props
使用 Props 傳遞數(shù)據(jù)
組件實(shí)例的作用域是孤立的。這意味著不能并且不應(yīng)該在子組件的模板內(nèi)直接引用父組件的數(shù)據(jù)??梢允褂?props 把數(shù)據(jù)傳給子組件。
“prop” 是組件數(shù)據(jù)的一個(gè)字段,期望從父組件傳下來。子組件需要顯式地用 props 選項(xiàng) 聲明 props:
Vue.component('child', { // 聲明 props props: ['msg'], // prop 可以用在模板內(nèi) // 可以用 `this.msg` 設(shè)置 template: '<span>{{ msg }}</span>' })
然后向它傳入一個(gè)普通字符串:
<child msg="hello!"></child>
駝峰式vs.橫杠式
HTML 特性不區(qū)分大小寫。名字形式為 camelCase 的 prop 用作特性時(shí),需要轉(zhuǎn)為 kebab-case(短橫線隔開):
Vue.component('child', { // camelCase in JavaScript props: ['myMessage'], template: '<span>{{ myMessage }}</span>' }) <!-- kebab-case in HTML --> <child my-message="hello!"></child>
動態(tài) Props
類似于用 v-bind 綁定 HTML 特性到一個(gè)表達(dá)式,也可以用 v-bind 綁定動態(tài) Props 到父組件的數(shù)據(jù)。每當(dāng)父組件的數(shù)據(jù)變化時(shí),也會傳導(dǎo)給子組件:
<div> <input v-model="parentMsg"> <br> <child v-bind:my-message="parentMsg"></child> </div>
使用 `v-bind` 的縮寫語法通常更簡單:
<child :my-message="parentMsg"></child>
字面量語法 vs. 動態(tài)語法
初學(xué)者常犯的一個(gè)錯(cuò)誤是使用字面量語法傳遞數(shù)值:
<!-- 傳遞了一個(gè)字符串 "1" -->
<comp some-prop="1"></comp>
因?yàn)樗且粋€(gè)字面 prop,它的值以字符串 `”1”` 而不是以實(shí)際的數(shù)字傳下去。如果想傳遞一個(gè)實(shí)際的 JavaScript 數(shù)字,需要使用動態(tài)語法,從而讓它的值被當(dāng)作 JavaScript 表達(dá)式計(jì)算:
<!-- 傳遞實(shí)際的數(shù)字 -->
<comp :some-prop="1"></comp>
Prop 綁定類型
prop 默認(rèn)是單向綁定:當(dāng)父組件的屬性變化時(shí),將傳導(dǎo)給子組件,但是反過來不會。這是為了防止子組件無意修改了父組件的狀態(tài)——這會讓應(yīng)用的數(shù)據(jù)流難以理解。不過,也可以使用 .sync 或 .once 綁定修飾符顯式地強(qiáng)制雙向或單次綁定:
比較語法:
<!-- 默認(rèn)為單向綁定 --> <child :msg="parentMsg"></child> <!-- 雙向綁定 --> <child :msg.sync="parentMsg"></child> <!-- 單次綁定 --> <child :msg.once="parentMsg"></child>
雙向綁定會把子組件的 msg 屬性同步回父組件的 parentMsg 屬性。單次綁定在建立之后不會同步之后的變化。
注意如果 prop 是一個(gè)對象或數(shù)組,是按引用傳遞。在子組件內(nèi)修改它會影響父組件的狀態(tài),不管是使用哪種綁定類型。
Prop 驗(yàn)證
組件可以為 props 指定驗(yàn)證要求。當(dāng)組件給其他人使用時(shí)這很有用,因?yàn)檫@些驗(yàn)證要求構(gòu)成了組件的 API,確保其他人正確地使用組件。此時(shí) props 的值是一個(gè)對象,包含驗(yàn)證要求:
Vue.component('example', { props: { // 基礎(chǔ)類型檢測 (`null` 意思是任何類型都可以) propA: Number, // 多種類型 (1.0.21+) propM: [String, Number], // 必需且是字符串 propB: { type: String, required: true }, // 數(shù)字,有默認(rèn)值 propC: { type: Number, default: 100 }, // 對象/數(shù)組的默認(rèn)值應(yīng)當(dāng)由一個(gè)函數(shù)返回 propD: { type: Object, default: function () { return { msg: 'hello' } } }, // 指定這個(gè) prop 為雙向綁定 // 如果綁定類型不對將拋出一條警告 propE: { twoWay: true }, // 自定義驗(yàn)證函數(shù) propF: { validator: function (value) { return value > 10 } }, // 轉(zhuǎn)換函數(shù)(1.0.12 新增) // 在設(shè)置值之前轉(zhuǎn)換值 propG: { coerce: function (val) { return val + '' // 將值轉(zhuǎn)換為字符串 } }, propH: { coerce: function (val) { return JSON.parse(val) // 將 JSON 字符串轉(zhuǎn)換為對象 } } } })
type 可以是下面原生構(gòu)造器:
•String
•Number
•Boolean
•Function
•Object
•Array
type 也可以是一個(gè)自定義構(gòu)造器,使用 instanceof 檢測。
當(dāng) prop 驗(yàn)證失敗了,Vue 將拒絕在子組件上設(shè)置此值,如果使用的是開發(fā)版本會拋出一條警告。
父子組件通信
父鏈
子組件可以用 this.$parent 訪問它的父組件。根實(shí)例的后代可以用 this.$root 訪問它。父組件有一個(gè)數(shù)組 this.$children,包含它所有的子元素。
盡管可以訪問父鏈上任意的實(shí)例,不過子組件應(yīng)當(dāng)避免直接依賴父組件的數(shù)據(jù),盡量顯式地使用 props 傳遞數(shù)據(jù)。另外,在子組件中修改父組件的狀態(tài)是非常糟糕的做法,因?yàn)椋?br /> 1.這讓父組件與子組件緊密地耦合;
2.只看父組件,很難理解父組件的狀態(tài)。因?yàn)樗赡鼙蝗我庾咏M件修改!理想情況下,只有組件自己能修改它的狀態(tài)。
自定義事件
Vue 實(shí)例實(shí)現(xiàn)了一個(gè)自定義事件接口,用于在組件樹中通信。這個(gè)事件系統(tǒng)獨(dú)立于原生 DOM 事件,用法也不同。
每個(gè) Vue 實(shí)例都是一個(gè)事件觸發(fā)器:
•使用 $on() 監(jiān)聽事件;
•使用 $emit() 在它上面觸發(fā)事件;
•使用 $dispatch() 派發(fā)事件,事件沿著父鏈冒泡;
•使用 $broadcast() 廣播事件,事件向下傳導(dǎo)給所有的后代。
不同于 DOM 事件,Vue 事件在冒泡過程中第一次觸發(fā)回調(diào)之后自動停止冒泡,除非回調(diào)明確返回 true。
簡單例子:
<!-- 子組件模板 --> <template id="child-template"> <input v-model="msg"> <button v-on:click="notify">Dispatch Event</button> </template> <!-- 父組件模板 --> <div id="events-example"> <p>Messages: {{ messages | json }}</p> <child></child> </div> // 注冊子組件 // 將當(dāng)前消息派發(fā)出去 Vue.component('child', { template: '#child-template', data: function () { return { msg: 'hello' } }, methods: { notify: function () { if (this.msg.trim()) { this.$dispatch('child-msg', this.msg) this.msg = '' } } } }) // 初始化父組件 // 將收到消息時(shí)將事件推入一個(gè)數(shù)組 var parent = new Vue({ el: '#events-example', data: { messages: [] }, // 在創(chuàng)建實(shí)例時(shí) `events` 選項(xiàng)簡單地調(diào)用 `$on` events: { 'child-msg': function (msg) { // 事件回調(diào)內(nèi)的 `this` 自動綁定到注冊它的實(shí)例上 this.messages.push(msg) } } })
使用 v-on 綁定自定義事件
上例非常好,不過從父組件的代碼中不能直觀的看到 "child-msg" 事件來自哪里。如果我們在模板中子組件用到的地方聲明事件處理器會更好。為此子組件可以用 v-on 監(jiān)聽自定義事件:
<child v-on:child-msg="handleIt"></child>
這樣就很清楚了:當(dāng)子組件觸發(fā)了 `”child-msg”` 事件,父組件的 `handleIt` 方法將被調(diào)用。所有影響父組件狀態(tài)的代碼放到父組件的 `handleIt` 方法中;子組件只關(guān)注觸發(fā)事件。
子組件索引
盡管有 props 和 events,但是有時(shí)仍然需要在 JavaScript 中直接訪問子組件。為此可以使用 v-ref 為子組件指定一個(gè)索引 ID。例如:
<div id="parent"> <user-profile v-ref:profile></user-profile> </div> var parent = new Vue({ el: '#parent' }) // 訪問子組件 var child = parent.$refs.profile
v-ref 和 v-for 一起用時(shí),ref 是一個(gè)數(shù)組或?qū)ο?,包含相?yīng)的子組件。
使用 Slot 分發(fā)內(nèi)容
在使用組件時(shí),常常要像這樣組合它們:
<app> <app-header></app-header> <app-footer></app-footer> </app>
注意兩點(diǎn):
1.<app> 組件不知道它的掛載點(diǎn)會有什么內(nèi)容,掛載點(diǎn)的內(nèi)容是由 <app> 的父組件決定的。
2.<app> 組件很可能有它自己的模板。
為了讓組件可以組合,我們需要一種方式來混合父組件的內(nèi)容與子組件自己的模板。這個(gè)處理稱為內(nèi)容分發(fā)(或 “transclusion”,如果你熟悉 Angular)。Vue.js 實(shí)現(xiàn)了一個(gè)內(nèi)容分發(fā) API,參照了當(dāng)前 Web 組件規(guī)范草稿,使用特殊的 <slot> 元素作為原始內(nèi)容的插槽。
編譯作用域
在深入內(nèi)容分發(fā) API 之前,我們先明確內(nèi)容的編譯作用域。假定模板為:
<child-component>
{{ msg }}
</child-component>
msg 應(yīng)該綁定到父組件的數(shù)據(jù),還是綁定到子組件的數(shù)據(jù)?答案是父組件。組件作用域簡單地說是:
父組件模板的內(nèi)容在父組件作用域內(nèi)編譯;子組件模板的內(nèi)容在子組件作用域內(nèi)編譯
一個(gè)常見錯(cuò)誤是試圖在父組件模板內(nèi)將一個(gè)指令綁定到子組件的屬性/方法:
<!-- 無效 -->
<child-component v-show="someChildProperty"></child-component>
假定 someChildProperty 是子組件的屬性,上例不會如預(yù)期那樣工作。父組件模板不應(yīng)該知道子組件的狀態(tài)。
如果要綁定子組件內(nèi)的指令到一個(gè)組件的根節(jié)點(diǎn),應(yīng)當(dāng)在它的模板內(nèi)這么做:
Vue.component('child-component', { // 有效,因?yàn)槭窃谡_的作用域內(nèi) template: '<div v-show="someChildProperty">Child</div>', data: function () { return { someChildProperty: true } } })
類似地,分發(fā)內(nèi)容是在父組件作用域內(nèi)編譯。
單個(gè) Slot
父組件的內(nèi)容將被拋棄,除非子組件模板包含 <slot>。如果子組件模板只有一個(gè)沒有特性的 slot,父組件的整個(gè)內(nèi)容將插到 slot 所在的地方并替換它。
<slot> 標(biāo)簽的內(nèi)容視為回退內(nèi)容?;赝藘?nèi)容在子組件的作用域內(nèi)編譯,當(dāng)宿主元素為空并且沒有內(nèi)容供插入時(shí)顯示這個(gè)回退內(nèi)容。
假定 my-component 組件有下面模板:
<div> <h1>This is my component!</h1> <slot> 如果沒有分發(fā)內(nèi)容則顯示我。 </slot> </div>
父組件模板:
<my-component>
<p>This is some original content</p>
<p>This is some more original content</p>
</my-component>
渲染結(jié)果:
<div> <h1>This is my component!</h1> <p>This is some original content</p> <p>This is some more original content</p> </div>
具名 Slot
<slot> 元素可以用一個(gè)特殊特性 name 配置如何分發(fā)內(nèi)容。多個(gè) slot 可以有不同的名字。具名 slot 將匹配內(nèi)容片段中有對應(yīng) slot 特性的元素。
仍然可以有一個(gè)匿名 slot,它是默認(rèn) slot,作為找不到匹配的內(nèi)容片段的回退插槽。如果沒有默認(rèn)的 slot,這些找不到匹配的內(nèi)容片段將被拋棄。
例如,假定我們有一個(gè) multi-insertion 組件,它的模板為:
<div> <slot name="one"></slot> <slot></slot> <slot name="two"></slot> </div>
父組件模板:
<multi-insertion> <p slot="one">One</p> <p slot="two">Two</p> <p>Default A</p> </multi-insertion>
渲染結(jié)果為:
<div> <p slot="one">One</p> <p>Default A</p> <p slot="two">Two</p> </div>
在組合組件時(shí),內(nèi)容分發(fā) API 是非常有用的機(jī)制。
動態(tài)組件
多個(gè)組件可以使用同一個(gè)掛載點(diǎn),然后動態(tài)地在它們之間切換。使用保留的 <component> 元素,動態(tài)地綁定到它的 is 特性:
new Vue({ el: 'body', data: { currentView: 'home' }, components: { home: { /* ... */ }, posts: { /* ... */ }, archive: { /* ... */ } } }) <component :is="currentView"> <!-- 組件在 vm.currentview 變化時(shí)改變 --> </component>
keep-alive
如果把切換出去的組件保留在內(nèi)存中,可以保留它的狀態(tài)或避免重新渲染。為此可以添加一個(gè) keep-alive 指令參數(shù):
<component :is="currentView" keep-alive>
<!-- 非活動組件將被緩存 -->
</component>
activate 鉤子
在切換組件時(shí),切入組件在切入前可能需要進(jìn)行一些異步操作。為了控制組件切換時(shí)長,給切入組件添加 activate 鉤子:
Vue.component('activate-example', { activate: function (done) { var self = this loadDataAsync(function (data) { self.someData = data done() }) } })
注意 `activate` 鉤子只作用于動態(tài)組件切換或靜態(tài)組件初始化渲染的過程中,不作用于使用實(shí)例方法手工插入的過程中。
transition-mode
transition-mode 特性用于指定兩個(gè)動態(tài)組件之間如何過渡。
在默認(rèn)情況下,進(jìn)入與離開平滑地過渡。這個(gè)特性可以指定另外兩種模式:
•in-out:新組件先過渡進(jìn)入,等它的過渡完成之后當(dāng)前組件過渡出去。
•out-in:當(dāng)前組件先過渡出去,等它的過渡完成之后新組件過渡進(jìn)入。
示例:
<!-- 先淡出再淡入 --> <component :is="view" transition="fade" transition-mode="out-in"> </component> .fade-transition { transition: opacity .3s ease; } .fade-enter, .fade-leave { opacity: 0; }
雜項(xiàng)
組件和 v-for
自定義組件可以像普通元素一樣直接使用 v-for:
<my-component v-for="item in items"></my-component>
但是,不能傳遞數(shù)據(jù)給組件,因?yàn)榻M件的作用域是孤立的。為了傳遞數(shù)據(jù)給組件,應(yīng)當(dāng)使用 props:
<my-component
v-for="item in items"
:item="item"
:index="$index">
</my-component>
不自動把 item 注入組件的原因是這會導(dǎo)致組件跟當(dāng)前 v-for 緊密耦合。顯式聲明數(shù)據(jù)來自哪里可以讓組件復(fù)用在其它地方。
編寫可復(fù)用組件
在編寫組件時(shí),記住是否要復(fù)用組件有好處。一次性組件跟其它組件緊密耦合沒關(guān)系,但是可復(fù)用組件應(yīng)當(dāng)定義一個(gè)清晰的公開接口。
Vue.js 組件 API 來自三部分——prop,事件和 slot:
•prop 允許外部環(huán)境傳遞數(shù)據(jù)給組件;
•事件 允許組件觸發(fā)外部環(huán)境的 action;
•slot 允許外部環(huán)境插入內(nèi)容到組件的視圖結(jié)構(gòu)內(nèi)。
使用 v-bind 和 v-on 的簡寫語法,模板的縮進(jìn)清楚且簡潔:
<my-component
:foo="baz"
:bar="qux"
@event-a="doThis"
@event-b="doThat">
<!-- content -->
<img slot="icon" src="...">
<p slot="main-text">Hello!</p>
</my-component>
異步組件
在大型應(yīng)用中,我們可能需要將應(yīng)用拆分為小塊,只在需要時(shí)才從服務(wù)器下載。為了讓事情更簡單,Vue.js 允許將組件定義為一個(gè)工廠函數(shù),動態(tài)地解析組件的定義。Vue.js 只在組件需要渲染時(shí)觸發(fā)工廠函數(shù),并且把結(jié)果緩存起來,用于后面的再次渲染。例如:
Vue.component('async-example', function (resolve, reject) { setTimeout(function () { resolve({ template: '<div>I am async!</div>' }) }, 1000) })
工廠函數(shù)接收一個(gè) resolve 回調(diào),在收到從服務(wù)器下載的組件定義時(shí)調(diào)用。也可以調(diào)用 reject(reason) 指示加載失敗。這里 setTimeout 只是為了演示。怎么獲取組件完全由你決定。推薦配合使用 Webpack 的代碼分割功能:
Vue.component('async-webpack-example', function (resolve) { // 這個(gè)特殊的 require 語法告訴 webpack // 自動將編譯后的代碼分割成不同的塊, // 這些塊將通過 ajax 請求自動下載。 require(['./my-async-component'], resolve) })
資源命名約定
一些資源,如組件和指令,是以 HTML 特性或 HTML 自定義元素的形式出現(xiàn)在模板中。因?yàn)?HTML 特性的名字和標(biāo)簽的名字不區(qū)分大小寫,所以資源的名字通常需使用 kebab-case 而不是 camelCase 的形式,這不大方便。
Vue.js 支持資源的名字使用 camelCase 或 PascalCase 的形式,并且在模板中自動將它們轉(zhuǎn)為 kebab-case(類似于 prop 的命名約定):
// 在組件定義中 components: { // 使用 camelCase 形式注冊 myComponent: { /*... */ } } <!-- 在模板中使用 kebab-case 形式 --> <my-component></my-component> ES6 對象字面量縮寫 也沒問題: // PascalCase import TextBox from './components/text-box'; import DropdownMenu from './components/dropdown-menu'; export default { components: { // 在模板中寫作 <text-box> 和 <dropdown-menu> TextBox, DropdownMenu } }
遞歸組件
組件在它的模板內(nèi)可以遞歸地調(diào)用自己,不過,只有當(dāng)它有 name 選項(xiàng)時(shí)才可以:
var StackOverflow = Vue.extend({ name: 'stack-overflow', template: '<div>' + // 遞歸地調(diào)用它自己 '<stack-overflow></stack-overflow>' + '</div>' })
上面組件會導(dǎo)致一個(gè)錯(cuò)誤 “max stack size exceeded”,所以要確保遞歸調(diào)用有終止條件。當(dāng)使用 Vue.component() 全局注冊一個(gè)組件時(shí),組件 ID 自動設(shè)置為組件的 name 選項(xiàng)。
片斷實(shí)例
在使用 template 選項(xiàng)時(shí),模板的內(nèi)容將替換實(shí)例的掛載元素。因而推薦模板的頂級元素始終是單個(gè)元素。
不這么寫模板:
<div>root node 1</div>
<div>root node 2</div>
推薦這么寫:
<div>
I have a single root node!
<div>node 1</div>
<div>node 2</div>
</div>
下面幾種情況會讓實(shí)例變成一個(gè)片斷實(shí)例:
1.模板包含多個(gè)頂級元素。
2.模板只包含普通文本。
3.模板只包含其它組件(其它組件可能是一個(gè)片段實(shí)例)。
4.模板只包含一個(gè)元素指令,如 <partial> 或 vue-router 的 <router-view>。
5.模板根節(jié)點(diǎn)有一個(gè)流程控制指令,如 v-if 或 v-for。
這些情況讓實(shí)例有未知數(shù)量的頂級元素,它將把它的 DOM 內(nèi)容當(dāng)作片斷。片斷實(shí)例仍然會正確地渲染內(nèi)容。不過,它沒有一個(gè)根節(jié)點(diǎn),它的 $el 指向一個(gè)錨節(jié)點(diǎn),即一個(gè)空的文本節(jié)點(diǎn)(在開發(fā)模式下是一個(gè)注釋節(jié)點(diǎn))。
但是更重要的是,組件元素上的非流程控制指令,非 prop 特性和過渡將被忽略,因?yàn)闆]有根元素供綁定:
<!-- 不可以,因?yàn)闆]有根元素 -->
<example v-show="ok" transition="fade"></example>
<!-- props 可以 -->
<example :prop="someData"></example>
<!-- 流程控制可以,但是不能有過渡 -->
<example v-if="ok"></example>
當(dāng)然片斷實(shí)例有它的用處,不過通常給組件一個(gè)根節(jié)點(diǎn)比較好。它會保證組件元素上的指令和特性能正確地轉(zhuǎn)換,同時(shí)性能也稍微好些。
內(nèi)聯(lián)模板
如果子組件有 inline-template 特性,組件將把它的內(nèi)容當(dāng)作它的模板,而不是把它當(dāng)作分發(fā)內(nèi)容。這讓模板更靈活。
<my-component inline-template>
<p>These are compiled as the component's own template</p>
<p>Not parent's transclusion content.</p>
</my-component>
但是 inline-template 讓模板的作用域難以理解,并且不能緩存模板編譯結(jié)果。最佳實(shí)踐是使用 template 選項(xiàng)在組件內(nèi)定義模板。
本文已被整理到了《Vue.js前端組件學(xué)習(xí)教程》,歡迎大家學(xué)習(xí)閱讀。
關(guān)于vue.js組件的教程,請大家點(diǎn)擊專題vue.js組件學(xué)習(xí)教程進(jìn)行學(xué)習(xí)。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
詳解auto-vue-file:一個(gè)自動創(chuàng)建vue組件的包
這篇文章主要介紹了auto-vue-file:一個(gè)自動創(chuàng)建vue組件的包,需要的朋友可以參考下2019-04-04一文了解vue-router之hash模式和history模式
這篇文章主要介紹了一文了解vue-router之hash模式和history模式,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-05-05運(yùn)行npm?run?dev報(bào)錯(cuò)的原因及解決
剛剛創(chuàng)建好vue項(xiàng)目的時(shí)候,運(yùn)行 npm run dev 會報(bào)錯(cuò),下面這篇文章主要給大家介紹了關(guān)于運(yùn)行npm?run?dev報(bào)錯(cuò)的原因及解決方法,需要的朋友可以參考下2022-10-10Vue?import?from省略后綴/加載文件夾的方法/實(shí)例詳解
本文介紹Vue在import時(shí)省略后綴以及import文件夾的方法,結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-08-08