W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
本節(jié)介紹 Vue.js 組件的使用。
組件是 Vue.js 最強(qiáng)大的功能之一,使用組件可以擴(kuò)展 HTML 元素,封裝可重用的代碼。
之前說(shuō)過(guò),我們可以用 Vue.extend()
創(chuàng)建一個(gè)組件構(gòu)造器:
var MyComponent = Vue.extend({
// 選項(xiàng)...
})
要把這個(gè)構(gòu)造器用作組件,需要用 Vue.component(tag, constructor)
注冊(cè) :
// 全局注冊(cè)組件,tag 為 my-component
Vue.component('my-component', MyComponent)
對(duì)于自定義標(biāo)簽名字,Vue.js 不強(qiáng)制要求遵循 [W3C 規(guī)則](http://www.w3.org/TR/custom-elements/#concepts)(小寫,并且包含一個(gè)短杠),盡管遵循這個(gè)規(guī)則比較好。
在注冊(cè)之后,組件便可以用在父實(shí)例的模塊中,以自定義元素 <my-component>
的形式使用。要確保在初始化根實(shí)例之前注冊(cè)了組件:
<div id="example">
<my-component></my-component>
</div>
// 定義
var MyComponent = Vue.extend({
template: '<div>A custom component!</div>'
})
// 注冊(cè)
Vue.component('my-component', MyComponent)
// 創(chuàng)建根實(shí)例
new Vue({
el: '#example'
})
渲染為:
<div id="example">
<div>A custom component!</div>
</div>
{% raw %}
<div id="example" class="demo">
<my-component></my-component>
</div>
<script>
Vue.component('my-component', {
template: '<div>A custom component!</div>'
})
new Vue({ el: '#example' })
</script>
{% endraw %}
注意組件的模板替換了自定義元素,自定義元素的作用只是作為一個(gè)掛載點(diǎn)。這可以用實(shí)例選項(xiàng) replace
改變。
不需要全局注冊(cè)每個(gè)組件??梢宰尳M件只能用在其它組件內(nèi),用實(shí)例選項(xiàng) components
注冊(cè):
var Child = Vue.extend({ /* ... */ })
var Parent = Vue.extend({
template: '...',
components: {
// <my-component> 只能用在父組件模板內(nèi)
'my-component': Child
}
})
這種封裝也適用于其它資源,如指令、過(guò)濾器和過(guò)渡。
為了讓事件更簡(jiǎn)單,可以直接傳入選項(xiàng)對(duì)象而不是構(gòu)造器給 Vue.component()
和 component
選項(xiàng)。Vue.js 在背后自動(dòng)調(diào)用 Vue.extend()
:
// 在一個(gè)步驟中擴(kuò)展與注冊(cè)
Vue.component('my-component', {
template: '<div>A custom component!</div>'
})
// 局部注冊(cè)也可以這么做
var Parent = Vue.extend({
components: {
'my-component': {
template: '<div>A custom component!</div>'
}
}
})
傳入 Vue 構(gòu)造器的多數(shù)選項(xiàng)也可以用在 Vue.extend()
中,不過(guò)有兩個(gè)特例: data
and el
。試想如果我們簡(jiǎn)單地把一個(gè)對(duì)象作為 data
選項(xiàng)傳給 Vue.extend()
:
var data = { a: 1 }
var MyComponent = Vue.extend({
data: data
})
這么做的問(wèn)題是 MyComponent
所有的實(shí)例將共享同一個(gè) data
對(duì)象!這基本不是我們想要的,因此我們應(yīng)當(dāng)使用一個(gè)函數(shù)作為 data
選項(xiàng),函數(shù)返回一個(gè)新對(duì)象:
var MyComponent = Vue.extend({
data: function () {
return { a: 1 }
}
})
同理,el
選項(xiàng)用在 Vue.extend()
中時(shí)也須是一個(gè)函數(shù)。
is
特性一些 HTML 元素,如 <table>
,限制什么元素可以放在它里面。自定義元素不在白名單上,將被放在元素的外面,因而渲染不正確。這時(shí)應(yīng)當(dāng)使用 is
特性,指示它是一個(gè)自定義元素:
<table>
<tr is="my-component"></tr>
</table>
組件實(shí)例的作用域是孤立的。這意味著不能并且不應(yīng)該在子組件的模板內(nèi)直接引用父組件的數(shù)據(jù)??梢允褂?props 把數(shù)據(jù)傳給子組件。
"prop" 是組件數(shù)據(jù)的一個(gè)字段,期望從父組件傳下來(lái)。子組件需要顯式地用 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>
結(jié)果:
{% raw %}
<div id="prop-example-1" class="demo">
<child msg="hello!"></child>
</div>
<script>
new Vue({
el: '#prop-example-1',
components: {
child: {
props: ['msg'],
template: '<span>{{ msg }}</span>'
}
}
})
</script>
{% endraw %}
HTML 特性不區(qū)分大小寫。名字形式為 camelCase 的 prop 用作特性時(shí),需要轉(zhuǎn)為 kebab-case(短橫線隔開(kāi)):
Vue.component('child', {
// camelCase in JavaScript
props: ['myMessage'],
template: '<span>{{ myMessage }}</span>'
})
<!-- kebab-case in HTML -->
<child my-message="hello!"></child>
類似于綁定一個(gè)普通的特性到一個(gè)表達(dá)式,也可以用 v-bind
綁定動(dòng)態(tài) Props 到父組件的數(shù)據(jù)。每當(dāng)父組件的數(shù)據(jù)變化時(shí),也會(huì)傳導(dǎo)給子組件:
<div>
<input v-model="parentMsg">
<br>
<child v-bind:my-message="parentMsg"></child>
</div>
使用 v-bind
的縮寫語(yǔ)法通常更簡(jiǎn)單:
<child :my-message="parentMsg"></child>
結(jié)果:
{% raw %}
<div id="demo-2" class="demo">
<input v-model="parentMsg">
<br>
<child v-bind:my-message="parentMsg"></child>
</div>
<script>
new Vue({
el: '#demo-2',
data: {
parentMsg: 'Message from parent'
},
components: {
child: {
props: ['myMessage'],
template: '<span>{{myMessage}}</span>'
}
}
})
</script>
{% endraw %}
初學(xué)者常犯的一個(gè)錯(cuò)誤是使用字面量語(yǔ)法傳遞數(shù)值:
<!-- 傳遞了一個(gè)字符串 "1" -->
<comp some-prop="1"></comp>
因?yàn)樗且粋€(gè)字面 prop,它的值以字符串 "1"
而不是以實(shí)際的數(shù)字傳下去。如果想傳遞一個(gè)實(shí)際的 JavaScript 數(shù)字,需要使用動(dòng)態(tài)語(yǔ)法,從而讓它的值被當(dāng)作 JavaScript 表達(dá)式計(jì)算:
<!-- 傳遞實(shí)際的數(shù)字 -->
<comp :some-prop="1"></comp>
prop 默認(rèn)是單向綁定:當(dāng)父組件的屬性變化時(shí),將傳導(dǎo)給子組件,但是反過(guò)來(lái)不會(huì)。這是為了防止子組件無(wú)意修改了父組件的狀態(tài)——這會(huì)讓應(yīng)用的數(shù)據(jù)流難以理解。不過(guò),也可以使用 .sync
或 .once
綁定修飾符顯式地強(qiáng)制雙向或單次綁定:
比較語(yǔ)法:
<!-- 默認(rèn)為單向綁定 -->
<child :msg="parentMsg"></child>
<!-- 雙向綁定 -->
<child :msg.sync="parentMsg"></child>
<!-- 單次綁定 -->
<child :msg.once="parentMsg"></child>
雙向綁定會(huì)把子組件的 msg
屬性同步回父組件的 parentMsg
屬性。單次綁定在建立之后不會(huì)同步之后的變化。
注意如果 prop 是一個(gè)對(duì)象或數(shù)組,是按引用傳遞。在子組件內(nèi)修改它**會(huì)**影響父組件的狀態(tài),不管是使用哪種綁定類型。
組件可以為 props 指定驗(yàn)證要求。當(dāng)組件給其他人使用時(shí)這很有用,因?yàn)檫@些驗(yàn)證要求構(gòu)成了組件的 API,確保其他人正確地使用組件。此時(shí) props 的值是一個(gè)對(duì)象,包含驗(yàn)證要求:
Vue.component('example', {
props: {
// 基礎(chǔ)類型檢測(cè) (`null` 意思是任何類型都可以)
propA: Number,
// 必需且是字符串
propB: {
type: String,
required: true
},
// 數(shù)字,有默認(rèn)值
propC: {
type: Number,
default: 100
},
// 對(duì)象/數(shù)組的默認(rèn)值應(yīng)當(dāng)由一個(gè)函數(shù)返回
propD: {
type: Object,
default: function () {
return { msg: 'hello' }
}
},
// 指定這個(gè) prop 為雙向綁定
// 如果綁定類型不對(duì)將拋出一條警告
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)換為對(duì)象
}
}
}
})
type
可以是下面原生構(gòu)造器:
type
也可以是一個(gè)自定義構(gòu)造器,使用 instanceof
檢測(cè)。
當(dāng) prop 驗(yàn)證失敗了,Vue 將拒絕在子組件上設(shè)置此值,如果使用的是開(kāi)發(fā)版本會(huì)拋出一條警告。
子組件可以用 this.$parent
訪問(wèn)它的父組件。根實(shí)例的后代可以用 this.$root
訪問(wèn)它。父組件有一個(gè)數(shù)組 this.$children
,包含它所有的子元素。
盡管可以訪問(wèn)父鏈上任意的實(shí)例,不過(guò)子組件應(yīng)當(dāng)避免直接依賴父組件的數(shù)據(jù),盡量顯式地使用 props 傳遞數(shù)據(jù)。另外,在子組件中修改父組件的狀態(tài)是非常糟糕的做法,因?yàn)椋?/p>
這讓父組件與子組件緊密地耦合;
Vue 實(shí)例實(shí)現(xiàn)了一個(gè)自定義事件接口,用于在組件樹(shù)中通信。這個(gè)事件系統(tǒng)獨(dú)立于原生 DOM 事件,做法也不同。
每個(gè) Vue 實(shí)例都是一個(gè)事件觸發(fā)器:
使用 $on()
監(jiān)聽(tīng)事件;
使用 $emit()
在它上面觸發(fā)事件;
使用 $dispatch()
派發(fā)事件,事件沿著父鏈冒泡;
$broadcast()
廣播事件,事件向下傳導(dǎo)給所有的后代。不同于 DOM 事件,Vue 事件在冒泡過(guò)程中第一次觸發(fā)回調(diào)之后自動(dòng)停止冒泡,除非回調(diào)明確返回 `true`。
簡(jiǎn)單例子:
<!-- 子組件模板 -->
<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>
// 注冊(cè)子組件
// 將當(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 = ''
}
}
}
})
// 啟動(dòng)父組件
// 將收到消息時(shí)將事件推入一個(gè)數(shù)組
var parent = new Vue({
el: '#events-example',
data: {
messages: []
},
// 在創(chuàng)建實(shí)例時(shí) `events` 選項(xiàng)簡(jiǎn)單地調(diào)用 `$on`
events: {
'child-msg': function (msg) {
// 事件回調(diào)內(nèi)的 `this` 自動(dòng)綁定到注冊(cè)它的實(shí)例上
this.messages.push(msg)
}
}
})
{% raw %}
<script type="x/template" id="child-template">
<input v-model="msg">
<button v-on:click="notify">Dispatch Event</button>
</script>
<div id="events-example" class="demo">
<p>Messages: {{ messages | json }}</p>
<child></child>
</div>
<script>
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 = ''
}
}
}
})
var parent = new Vue({
el: '#events-example',
data: {
messages: []
},
events: {
'child-msg': function (msg) {
this.messages.push(msg)
}
}
})
</script>
{% endraw %}
上例非常好,不過(guò)看著父組件的代碼, "child-msg"
事件來(lái)自哪里不直觀。如果我們?cè)谀0逯凶咏M件用到的地方聲明事件處理器會(huì)更好。為了做到這點(diǎn),子組件可以用 v-on
監(jiān)聽(tīng)自定義事件:
<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 中直接訪問(wèn)子組件。為此可以使用 v-ref
為子組件指定一個(gè)索引 ID。例如:
<div id="parent">
<user-profile v-ref:profile></user-profile>
</div>
var parent = new Vue({ el: '#parent' })
// 訪問(wèn)子組件
var child = parent.$refs.profile
v-ref
和 v-for
一起用時(shí),ref 是一個(gè)數(shù)組或?qū)ο螅鄳?yīng)的子組件。
在使用組件時(shí),常常要像這樣組合它們:
<app>
<app-header></app-header>
<app-footer></app-footer>
</app>
注意兩點(diǎn):
<app>
組件不知道它的掛載點(diǎn)會(huì)有什么內(nèi)容,掛載點(diǎn)的內(nèi)容是由 <app>
的父組件決定的。
<app>
組件很可能有它自己的模板。為了讓組件可以組合,我們需要一種方式來(lái)混合父組件的內(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>
{{ msg }}
</child>
msg
應(yīng)該綁定到父組件的數(shù)據(jù),還是綁定到子組件的數(shù)據(jù)?答案是父組件。組件作用域簡(jiǎn)單地說(shuō)是:
父組件模板的內(nèi)容在父組件作用域內(nèi)編譯;子組件模板的內(nèi)容在子組件作用域內(nèi)編譯
一個(gè)常見(jiàn)錯(cuò)誤是試圖在父組件模板內(nèi)將一個(gè)指令綁定到子組件屬性/方法:
<!-- 無(wú)效 -->
<child v-show="someChildProperty"></child>
假定 someChildProperty
是子組件的屬性,上例不能如預(yù)期工作。父組件模板不知道子組件的狀態(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)編譯。
父組件的內(nèi)容將被拋棄,除非子組件模板包含 <slot>
。如果只有一個(gè)沒(méi)有特性的 slot,整個(gè)內(nèi)容將被插到它所在的地方,替換 slot。
<slot>
標(biāo)簽的內(nèi)容視為回退內(nèi)容。回退內(nèi)容在子組件的作用域內(nèi)編譯,只有當(dāng)宿主元素為空并且沒(méi)有內(nèi)容供插入時(shí)顯示。
假定 my-component
組件有下面模板:
<div>
<h1>This is my component!</h1>
<slot>
This will only be displayed if there is no content
to be distributed.
</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>
元素有一個(gè)特殊特性 name
,用于配置如何分發(fā)內(nèi)容。多個(gè) slot 可以有不同的名字。命名 slot 將匹配有對(duì)應(yīng) slot
特性的內(nèi)容片斷。
也可以有一個(gè)未命名 slot,它是默認(rèn) slot,作為找不到匹配內(nèi)容的回退插槽。如果沒(mé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ī)制。
多個(gè)組件可以使用同一個(gè)掛載點(diǎn),然后動(dòng)態(tài)地在它們之間切換。使用保留的 <component>
元素,動(dòng)態(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>
<!-- 非活動(dòng)組件將被緩存 -->
</component>
activate
鉤子在切換組件時(shí),切入組件在切入前可能需要進(jìn)行一些異步操作。為了控制組件切換時(shí)長(zhǎng),給切入組件添加 activate
鉤子:
Vue.component('activate-example', {
activate: function (done) {
var self = this
loadDataAsync(function (data) {
self.someData = data
done()
})
}
})
注意 activate
鉤子只作用于動(dòng)態(tài)組件切換或靜態(tài)組件初始化渲染的過(guò)程中,不作用于使用實(shí)例方法手工插入的過(guò)程中。
transition-mode
transition-mode
特性用于指定兩個(gè)動(dòng)態(tài)組件之間如何過(guò)渡。
在默認(rèn)情況下,進(jìn)入與離開(kāi)平滑地過(guò)渡。這個(gè)特性可以指定另外兩種模式:
in-out
:新組件先過(guò)渡進(jìn)入,等它的過(guò)渡完成之后當(dāng)前組件過(guò)渡出去。
out-in
:當(dāng)前組件先過(guò)渡出去,等它的過(guò)渡完成之后新組件過(guò)渡進(jìn)入。示例:
<!-- 先淡出再淡入 -->
<component
:is="view"
transition="fade"
transition-mode="out-in">
</component>
.fade-transition {
transition: opacity .3s ease;
}
.fade-enter, .fade-leave {
opacity: 0;
}
{% raw %}
<div id="transition-mode-demo" class="demo">
<input v-model="view" type="radio" value="v-a" id="a" name="view"><label for="a">A</label>
<input v-model="view" type="radio" value="v-b" id="b" name="view"><label for="b">B</label>
<component
:is="view"
transition="fade"
transition-mode="out-in">
</component>
</div>
<style>
.fade-transition {
transition: opacity .3s ease;
}
.fade-enter, .fade-leave {
opacity: 0;
}
</style>
<script>
new Vue({
el: '#transition-mode-demo',
data: {
view: 'v-a'
},
components: {
'v-a': {
template: '<div>Component A</div>'
},
'v-b': {
template: '<div>Component B</div>'
}
}
})
</script>
{% endraw %}
自定義組件可以像普通元素一樣直接使用 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>
不自動(dòng)把 item
注入組件的原因是這會(huì)導(dǎo)致組件跟當(dāng)前 v-for
緊密耦合。顯式聲明數(shù)據(jù)來(lái)自哪里可以讓組件復(fù)用在其它地方。
在編寫組件時(shí),記住是否要復(fù)用組件有好處。一次性組件跟其它組件緊密耦合沒(méi)關(guān)系,但是可復(fù)用組件應(yīng)當(dāng)定義一個(gè)清晰的公開(kāi)接口。
Vue.js 組件 API 來(lái)自三部分——prop,事件和 slot:
prop 允許外部環(huán)境傳遞數(shù)據(jù)給組件;
事件 允許組件觸發(fā)外部環(huán)境的 action;
使用 v-bind
和 v-on
的簡(jiǎn)寫語(yǔ)法,模板的縮進(jìn)清楚且簡(jiǎ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ù)器下載。為了讓事情更簡(jiǎn)單,Vue.js 允許將組件定義為一個(gè)工廠函數(shù),動(dòng)態(tài)地解析組件的定義。Vue.js 只在組件需要渲染時(shí)觸發(fā)工廠函數(shù),并且把結(jié)果緩存起來(lái),用于后面的再次渲染。例如:
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 語(yǔ)法告訴 webpack
// 自動(dòng)將編譯后的代碼分割成不同的塊,
// 這些塊將通過(guò) ajax 請(qǐng)求自動(dòng)下載。
require(['./my-async-component'], resolve)
})
一些資源,如組件和指令,是以 HTML 特性或 HTML 自定義元素的形式出現(xiàn)在模板中。因?yàn)?HTML 特性的名字和標(biāo)簽的名字不區(qū)分大小寫,所以資源的名字通常需使用 kebab-case 而不是 camelCase 的形式,這不大方便。
Vue.js 支持資源的名字使用 camelCase 或 PascalCase 的形式,并且在模板中自動(dòng)將它們轉(zhuǎn)為 kebab-case(類似于 prop 的命名約定):
// 在組件定義中
components: {
// 使用 camelCase 形式注冊(cè)
myComponent: { /*... */ }
}
<!-- 在模板中使用 kebab-case 形式 -->
<my-component></my-component>
ES6 對(duì)象字面量縮寫 也沒(méi)問(wèn)題:
// 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)用自己,不過(guò),只有當(dāng)它有 name
選項(xiàng)時(shí)才可以:
var StackOverflow = Vue.extend({
name: 'stack-overflow',
template:
'<div>' +
// 遞歸地調(diào)用它自己
'<stack-overflow></stack-overflow>' +
'</div>'
})
上面組件會(huì)導(dǎo)致一個(gè)錯(cuò)誤 "max stack size exceeded",所以要確保遞歸調(diào)用有終止條件。當(dāng)使用 Vue.component()
全局注冊(cè)一個(gè)組件時(shí),組件 ID 自動(dòng)設(shè)置為組件的 name
選項(xiàng)。
在使用 template
選項(xiàng)時(shí),模板的內(nèi)容將替換實(shí)例的掛載元素。因而推薦模板的頂級(jí)元素始終是單個(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>
下面幾種情況會(huì)讓實(shí)例變成一個(gè)片斷實(shí)例:
<partial>
或 vue-router 的 <router-view>
。v-if
或 v-for
。這些情況讓實(shí)例有未知數(shù)量的頂級(jí)元素,它將把它的 DOM 內(nèi)容當(dāng)作片斷。片斷實(shí)例仍然會(huì)正確地渲染內(nèi)容。不過(guò),它沒(méi)有一個(gè)根節(jié)點(diǎn),它的 $el
指向一個(gè)錨節(jié)點(diǎn),即一個(gè)空的文本節(jié)點(diǎn)(在開(kāi)發(fā)模式下是一個(gè)注釋節(jié)點(diǎn))。
但是更重要的是,組件元素上的非流程控制指令,非 prop 特性和過(guò)渡將被忽略,因?yàn)闆](méi)有根元素供綁定:
<!-- 不可以,因?yàn)闆](méi)有根元素 -->
<example v-show="ok" transition="fade"></example>
<!-- props 可以 -->
<example :prop="someData"></example>
<!-- 流程控制可以,但是不能有過(guò)渡 -->
<example v-if="ok"></example>
當(dāng)然片斷實(shí)例有它的用處,不過(guò)通常給組件一個(gè)根節(jié)點(diǎn)比較好。它會(huì)保證組件元素上的指令和特性能正確地轉(zhuǎn)換,同時(shí)性能也稍微好些。
如果子組件有 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)定義模板。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: