組件html
# 建立一個 Vue 實例:
new Vue({
el: '#some-element',
// 選項
})
# 註冊全局組件
Vue.component('my-component', {
// 選項 組件名小寫,而且包含一個短槓
})
組件在註冊以後,即可以做爲自定義元素 ```<my-component></my-component>```
# 局部註冊
var Child = {
template: '<div>A custom component!</div>'
}
new Vue({
// ...
components: {
// <my-component> 將只在父組件模板中可用
'my-component': Child
}
})
複製代碼
DOM 模板解析注意事項vue
<ul>、<ol>、<table>、<select>這樣的元素裏容許包含的元素有限制,而另外一些像<option> 這樣的元素
只能出如今某些特定元素的內部。
在自定義組件中使用這些受限制的元素時會致使一些問題,例如:
<table>
<my-row>...</my-row>
</table>
自定義組件 <my-row>
會被看成無效的內容,所以會致使錯誤的渲染結果。變通的方案是使用特殊的 is 特性:
<table>
<tr is="my-row"></tr>
</table>
複製代碼
datawebpack
必須是函數:可是在組件中,由於可能在多處調用同一組件,因此爲了避免讓多處的組件共享同一data對象,只能
返回函數。
那麼 Vue 會中止運行,並在控制檯發出警告,告訴你在組件實例中 data 必須是一個函數。但理解這種規則
爲什麼存在也是頗有益處的,因此讓咱們先做個弊:
<div id="example-2">
<simple-counter></simple-counter>
<simple-counter></simple-counter>
<simple-counter></simple-counter>
</div>
var data = { counter: 0 }
Vue.component('simple-counter', {
template: '<button v-on:click="counter += 1">{{ counter }}</button>',
// 技術上 data 的確是一個函數了,所以 Vue 不會警告,
// 可是咱們卻給每一個組件實例返回了同一個對象的引用
data: function () {
return data
}
})
new Vue({
el: '#example-2'
})
因爲這三個組件實例共享了同一個 data 對象,所以遞增一個 counter 會影響全部組件!這就錯了。咱們能夠
經過爲每一個組件返回全新的數據對象來修復這個問題:
data: function () {
return {
counter: 0
}
}
如今每一個 counter 都有它本身內部的狀態了:
複製代碼
父子組件Propweb
prop 向下傳遞,事件向上傳遞。父組件的數據須要經過 prop才能下發到子組件中。
子組件要顯式地用 props 選項聲明它預期的數據:
Vue.component('child', {
// 聲明 props
props: ['message'],
// 就像 data 同樣,prop 也能夠在模板中使用
// 一樣也能夠在 vm 實例中經過 this.message 來使用
template: '<span>{{ message }}</span>'
})
而後咱們能夠這樣向它傳入一個普通字符串:
<child message="hello!"></child>
# 動態 Prop
與綁定到任何普通的 HTML 特性相相似,咱們能夠用 v-bind 來動態地將 prop 綁定到父組件的數據。每當
父組件的數據變化時,該變化也會傳導給子組件:
<child v-bind:my-message="parentMsg"></child>
# Message from parent
若是你想把一個對象的全部屬性做爲 prop 進行傳遞,可使用不帶任何參數的 v-bind (即用 v-bind 而
不是 v-bind:prop-name)。例如,已知一個 todo 對象:
todo: {
text: 'Learn Vue',
isComplete: false
}
<todo-item
:text="todo.text"
:is-complete="todo.isComplete"
/>
# 字面量語法 vs 動態語法
初學者常犯的一個錯誤是使用字面量語法傳遞數值:
<!-- 傳遞了一個字符串 "1" -->
<comp some-prop="1"></comp>
由於它是一個字面量 prop,它的值是字符串 "1" 而不是一個數值。若是想傳遞一個真正的 JavaScript
數值,則須要使用 v-bind,從而讓它的值被看成 JavaScript 表達式計算:
<!-- 傳遞真正的數值 -->
<comp v-bind:some-prop="1"></comp>
# 單向數據流:Prop 是單向綁定的
每次父組件更新時,子組件的全部 prop 都會更新爲最新值。
在兩種狀況下,咱們很容易忍不住想去修改 prop 中數據:
Prop 做爲初始值傳入後,子組件想把它看成局部數據來用;
Prop 做爲原始數據傳入,由子組件處理成其它數據輸出。
對這兩種狀況,正確的應對方式是:
定義一個局部變量,並用 prop 的值初始化它:
props: ['initialCounter'],
data: function () {
return { counter: this.initialCounter }
}
定義一個計算屬性,處理 prop 的值並返回:
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
注意在 JavaScript 中對象和數組是引用類型,指向同一個內存空間,若是 prop 是一個對象或數組,在子
組件內部改變它會影響父組件的狀態。
# Prop 驗證
爲組件的 prop 指定驗證規則。若是傳入的數據不符合要求,Vue 會發出警告。這對於開發給他人使用的組件
很是有用。
要指定驗證規則,須要用對象的形式來定義 prop,而不能用字符串數組:
Vue.component('example', {
props: {
// 基礎類型檢測 (`null` 指容許任何類型)
propA: Number,
// 多是多種類型
propB: [String, Number],
// 必傳且是字符串
propC: {
type: String,
required: true
},
// 數值且有默認值
propD: {
type: Number,
default: 100
},
// 數組/對象的默認值應當由一個工廠函數返回
propE: {
type: Object,
default: function () {
return { message: 'hello' }
}
},
// 自定義驗證函數
propF: {
validator: function (value) {
return value > 10
}
}
}
})
type 能夠是下面原生構造器:
String
Number
Boolean
Function
Object
Array
Symbol
type 也能夠是一個自定義構造器函數,使用 instanceof 檢測。
當 prop 驗證失敗,Vue 會拋出警告 (若是使用的是開發版本)。注意 prop 會在組件實例建立以前進行校驗,
因此在 default 或 validator 函數裏,諸如 data、computed 或 methods 等實例屬性還沒法使用。
# 非 Prop 特性:能夠直接傳入組件,而不須要定義相應的 prop。
組件能夠接收任意傳入的特性,這些特性都會被添加到組件的根元素上。
把特性直接添加到組件上 (不須要事先定義 prop):
<bs-date-input data-3d-date-picker="true"></bs-date-input>
# 替換/合併現有的特性
<input type="date" class="form-control">
爲了給該日期選擇器插件增長一個特殊的主題,咱們可能須要增長一個特殊的 class,好比:
<bs-date-input
data-3d-date-picker="true"
class="date-picker-theme-dark"
></bs-date-input>
在這個例子當中,咱們定義了兩個不一樣的 class 值:
對於多數特性來講,傳遞給組件的值會覆蓋組件自己設定的值。即例如傳遞 type="large" 將會覆蓋
type="date" 且有可能破壞該組件!所幸咱們對待 class 和 style 特性會更聰明一些,這兩個特性的值都會作
合併 (merge) 操做,讓最終生成的值爲:form-control date-picker-theme-dark。
複製代碼
子-父通訊:$emit&自定義事件系統vue-router
使用 $on(eventName) 監聽事件
使用 $emit(eventName, optionalPayload) 觸發事件
父組件能夠在使用子組件的地方直接用 v-on 來監聽子組件觸發的事件。
不能用 $on 監聽子組件釋放的事件,而必須在模板裏直接用 v-on 綁定,參見下面的例子。
<div id="counter-event-example">
<p>{{ total }}</p>
<button-counter @increment="incrementTotal"></button-counter>
</div>
Vue.component('button-counter', {
template: '<button @click="incrementCounter">{{ counter }}</button>',
data: function () {
return {
counter: 0
}
},
methods: {
incrementCounter: function () {
this.counter += 1
this.$emit('increment')
}
},
})
new Vue({
el: '#counter-event-example',
data: {
total: 0
},
methods: {
incrementTotal: function () {
this.total += 1
}
}
})
在本例中,子組件已經和它外部徹底解耦了。它所作的只是報告本身的內部事件,由於父組件可能會關心這些事件。
# 這裏有一個如何使用載荷 (payload) 數據的示例:
<div id="message-event-example" class="demo">
<p v-for="msg in messages">{{ msg }}</p>
<button-message @message="handleMessage"></button-message>
</div>
Vue.component('button-message', {
template: `<div>
<input type="text" v-model="message" />
<button @click="handleSendMessage">Send</button>
</div>`,
data: function () {
return {
message: 'test message'
}
},
methods: {
handleSendMessage: function () {
this.$emit('message', { message: this.message })
}
}
})
new Vue({
el: '#message-event-example',
data: {
messages: []
},
methods: {
handleMessage: function (payload) {
this.messages.push(payload.message)
}
}
})
記錄其自身的活動,活動記錄是包括一份傳入事件觸發器的載荷數據在內的,只是爲了展現父組件能夠不關注的
一個場景。
# camelCase vs. kebab-case
HTML 特性是不區分大小寫的。因此,當使用的不是字符串模板時,camelCase (駝峯式命名) 的 prop
須要轉換爲相對應的 kebab-case (短橫線分隔式命名):
Vue.component('child', {
// 在 JavaScript 中使用 camelCase
props: ['myMessage'],
template: '<span>{{ myMessage }}</span>'
})
<!-- 在 HTML 中使用 kebab-case -->
<child my-message="hello!"></child>
若是你使用字符串模板,則沒有這些限制。
複製代碼
非父子組件的通訊 vue實例數組
非父子關係的兩個組件之間通訊。
# 在簡單的場景下,可使用一個空的 Vue 實例做爲事件總線:
var bus = new Vue()
// 觸發組件 A 中的事件
bus.$emit('id-selected', 1)
// 在組件 B 建立的鉤子中監聽事件
bus.$on('id-selected', function (id) {
// ...
})
# 在複雜的狀況下,咱們應該考慮使用專門的狀態管理模式。
複製代碼
給組件綁定原生事件瀏覽器
在某個組件的根元素上監聽一個原生事件。可使用 v-on 的修飾符 .native。例如:
<my-component @click.native="doTheThing"></my-component>
# .sync 修飾符(2.3.0+)
對一個 prop 進行「雙向綁定」.當一個子組件改變了一個帶 .sync 的 prop 的值時,這個變化也會同步到
父組件中所綁定的值。這很方便,但也會致使問題,由於它破壞了單向數據流。在 2.0 發佈以後的實際應用中,
.sync 仍是有其適用之處,好比在開發可複用的組件庫時。咱們須要作的只是讓子組件改變父組件狀態的代碼更
容易被區分。
可是此次它只是做爲一個編譯時的語法糖存在。它會被擴展爲一個自動更新父組件屬性的 v-on 監聽器。
以下代碼
<comp :foo.sync="bar"></comp>
會被擴展爲:
<comp :foo="bar" @update:foo="val => bar = val"></comp>
當子組件須要更新 foo 的值時,它須要顯式地觸發一個更新事件:
this.$emit('update:foo', newValue)
當使用一個對象一次性設置多個屬性的時候,這個 .sync 修飾符也能夠和 v-bind 一塊兒使用:
<comp v-bind.sync="{ foo: 1, bar: 2 }"></comp>
這個例子會爲 foo 和 bar 同時添加用於更新的 v-on 監聽器。
# 使用自定義事件的表單輸入組件
自定義事件能夠用來建立自定義的表單輸入組件,使用 v-model 來進行數據雙向綁定。要牢記:
<input v-model="something">
這不過是如下示例的語法糖:
<input
v-bind:value="something"
v-on:input="something = $event.target.value">
因此在組件中使用時,它至關於下面的簡寫:
<custom-input
v-bind:value="something"
v-on:input="something = arguments[0]">
</custom-input>
因此要讓組件的 v-model 生效,它應該 (從 2.2.0 起是可配置的):
#接受一個 value prop(待研究)
在有新的值時觸發 input 事件並將新值做爲參數
咱們來看一個很是簡單的貨幣輸入的自定義控件:
<currency-input v-model="price"></currency-input>
Vue.component('currency-input', {
template: '\ <span>\ $\ <input\ ref="input"\ v-bind:value="value"\ v-on:input="updateValue($event.target.value)"\ >\ </span>\ ',
props: ['value'],
methods: {
// 不是直接更新值,而是使用此方法來對輸入值進行格式化和位數限制
updateValue: function (value) {
var formattedValue = value
// 刪除兩側的空格符
.trim()
// 保留 2 位小數
.slice(
0,
value.indexOf('.') === -1
? value.length
: value.indexOf('.') + 3
)
// 若是值尚不合規,則手動覆蓋爲合規的值
if (formattedValue !== value) {
this.$refs.input.value = formattedValue
}
// 經過 input 事件帶出數值
this.$emit('input', Number(formattedValue))
}
}
})
固然,上面的例子仍是比較初級的。好比,用戶輸入多個小數點或句號也是容許的,好惡心吧!所以咱們須要
一個複雜一些的例子,下面是一個更加完善的貨幣過濾器:
# 自定義組件的 v-model(2.2.0 新增)
默認狀況下,一個組件的 v-model 會使用 value prop 和 input 事件。可是諸如單選框、複選框之類的
輸入類型可能把 value 用做了別的目的。model 選項能夠避免這樣的衝突:
Vue.component('my-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean,
// 這樣就容許拿 `value` 這個 prop 作其它事了
value: String
},
// ...
})
<my-checkbox v-model="foo" value="some value"></my-checkbox>
上述代碼等價於:
<my-checkbox
:checked="foo"
@change="val => { foo = val }"
value="some value">
</my-checkbox>
注意你仍然須要顯式聲明 checked 這個 prop。
複製代碼
內容分發slot緩存
簡單來講,假如父組件須要在子組件內放一些DOM,那麼這些DOM是顯示、不顯示、在哪一個地方顯示、如何顯示,
就是slot分發負責的活。這個過程被稱爲內容分發 (即 Angular 用戶熟知的「transclusion」)。Vue.js
實現了一個內容分發 API,參照了當前 Web Components 規範草案,使用特殊的 <slot>元素做爲原始內容
的插槽。
# 編譯做用域
在深刻內容分發 API 以前,咱們先明確內容在哪一個做用域裏編譯。假定模板爲:
<child-component>
{{ message }}
</child-component>
message 應該綁定到父組件的數據,組件做用域簡單地說是:
父組件模板的內容在父組件做用域內編譯;子組件模板的內容在子組件做用域內編譯。
一個常見錯誤是試圖在父組件模板內將一個指令綁定到子組件的屬性/方法:
<!-- 無效 -->
<child-component v-show="someChildProperty"></child-component>
假定 someChildProperty 是子組件的屬性,上例不會如預期那樣工做。父組件模板並不感知子組件的狀態。
若是要綁定子組件做用域內的指令到一個組件的根節點,你應當在子組件本身的模板裏作:
Vue.component('child-component', {
// 有效,由於是在正確的做用域內
template: '<div v-show="someChildProperty">Child</div>',
data: function () {
return {
someChildProperty: true
}
}
})
相似地,被分發的內容會在父做用域內編譯。
# 單個插槽
除非子組件模板包含至少一個 <slot> 插口,不然父組件的內容將會被丟棄。當子組件模板只有一個沒有屬性
的插槽時,父組件傳入的整個內容片斷將插入到插槽所在的 DOM 位置,並替換掉插槽標籤自己。
最初在<slot>標籤中的任何內容都被視爲備用內容。備用內容在子組件的做用域內編譯,而且只有在宿主元素爲
空,且沒有要插入的內容時才顯示備用內容。
假定 my-component 組件有以下模板:
<div>
<h2>我是子組件的標題</h2>
<slot>
只有在沒有要分發的內容時纔會顯示。
</slot>
</div>
父組件模板:
<div>
<h1>我是父組件的標題</h1>
<my-component>
<p>這是一些初始內容</p>
<p>這是更多的初始內容</p>
</my-component>
渲染結果:
<div>
<h1>我是父組件的標題</h1>
<div>
<h2>我是子組件的標題</h2>
<p>這是一些初始內容</p>
<p>這是更多的初始內容</p>
</div>
</div>
# 具名slot
將放在子組件裏的不一樣html標籤放在不一樣的位置
父組件在要分發的標籤裏添加 slot=」name名」 屬性
子組件在對應分發的位置的slot標籤裏,添加name=」name名」 屬性,
而後就會將對應的標籤放在對應的位置了。
示例代碼:
<div id="app">
<children>
<span slot="first">12345</span>
<span slot="second">56789</span>
<!--上面這行不會顯示-->
</children>
</div>
<script>
var vm = new Vue({
el: '#app',
components: {
children: { //這個無返回值,不會繼續派發
template: "<button> <slot name='first'></slot>爲了明確做用範圍, <slot name='second'></slot>因此使用button標籤 </button>"
}
}
});
</script>
顯示結果爲:(爲了方便查看,已手動調整換行)
<button>
<span slot="first">12345</span>
爲了明確做用範圍,
<span slot="second">56789</span>
因此使用button標籤
</button>
# 做用域插槽(2.1.0 新增)
做用域插槽是一種特殊類型的插槽,用做一個 (能被傳遞數據的) 可重用模板,來代替已經渲染好的元素。
在子組件中,只需將數據傳遞到插槽,就像你將 prop 傳遞給組件同樣:
<div class="child">
<slot text="hello from child"></slot>
</div>
在父級中,具備特殊特性 slot-scope 的 <template> 元素必須存在,表示它是做用域插槽的模板。
slot-scope 的值將被用做一個臨時變量名,此變量接收從子組件傳遞過來的 prop 對象:
<div class="parent">
<child>
<template slot-scope="props">
<span>hello from parent</span>
<span>{{ props.text }}</span>
</template>
</child>
</div>
若是咱們渲染上述模板,獲得的輸出會是:
<div class="parent">
<div class="child">
<span>hello from parent</span>
<span>hello from child</span>
</div>
</div>
在 2.5.0+,slot-scope 能被用在任意元素或組件中而再也不侷限於 <template>。
做用域插槽更典型的用例是在列表組件中,容許使用者自定義如何渲染列表的每一項:
<my-awesome-list :items="items">
<!-- 做用域插槽也能夠是具名的 -->
<li
slot="item"
slot-scope="props"
class="my-fancy-item">
{{ props.text }}
</li>
</my-awesome-list>
列表組件的模板:
<ul>
<slot name="item"
v-for="item in items"
:text="item.text">
<!-- 這裏寫入備用內容 -->
</slot>
</ul>
# 解構
slot-scope 的值其實是一個能夠出如今函數簽名參數位置的合法的 JavaScript 表達式。這意味着在
受支持的環境 (單文件組件或現代瀏覽器)
<child>
<span slot-scope="{ text }">{{ text }}</span>
</child>
複製代碼
動態組件bash
經過使用保留的 <component>元素,並對其 is 特性進行動態綁定,你能夠在同一個掛載點動態切換多個
組件:
var vm = new Vue({
el: '#example',
data: {
currentView: 'home'
},
components: {
home: { /* ... */ },
posts: { /* ... */ },
archive: { /* ... */ }
}
})
<component v-bind:is="currentView">
<!-- 組件在 vm.currentview 變化時改變! -->
</component>
也能夠直接綁定到組件對象上:
var Home = {
template: '<p>Welcome home!</p>'
}
var vm = new Vue({
el: '#example',
data: {
currentView: Home
}
})
複製代碼
keep-alive服務器
若是把切換出去的組件保留在內存中,能夠保留它的狀態或避免從新渲染。爲此能夠添加一個 keep-alive
指令參數:
<keep-alive>
<component :is="currentView">
<!-- 非活動組件將被緩存! -->
</component>
</keep-alive>
複製代碼
編寫可複用組件
在編寫組件時,最好考慮好之後是否要進行復用。一次性組件間有緊密的耦合不要緊,可是可複用組件應當定義
一個清晰的公開接口,同時也不要對其使用的外層數據做出任何假設。
Vue 組件的 API 來自三部分——prop、事件和插槽:
Prop 容許外部環境傳遞數據給組件;
事件容許從組件內觸發外部環境的反作用;
插槽容許外部環境將額外的內容組合在組件中。
使用 v-bind 和 v-on 的簡寫語法,模板的意圖會更清楚且簡潔:
<my-component
:foo="baz"
:bar="qux"
@event-a="doThis"
@event-b="doThat"
>
<img slot="icon" src="...">
<p slot="main-text">Hello!</p>
</my-component>
複製代碼
子組件引用
儘管有 prop 和事件,可是有時仍然須要在 JavaScript 中直接訪問子組件。爲此可使用 ref 爲子組件
指定一個引用 ID。例如:
// 訪問子組件實例
當 ref 和 v-for 一塊兒使用時,獲取到的引用會是一個數組,包含和循環數據源對應的子組件。
$refs 只在組件渲染完成後才填充,而且它是非響應式的。它僅僅是一個直接操做子組件的應急方案——應當避
免在模板或計算屬性中使用 $refs。
複製代碼
異步組件
在大型應用中,咱們可能須要將應用拆分爲多個小模塊,按需從服務器下載。爲了進一步簡化,Vue.js 容許
將組件定義爲一個工廠函數,異步地解析組件的定義。Vue.js 只在組件須要渲染時觸
發工廠函數,而且把結果緩存起來,用於後面的再次渲染.
複製代碼
工廠函數: 就是指這些內置(建)函數都是類對象,當你調用他們時,其實是建立了一個類實例」。意思就是當我調用這個函數,其實是先利用類建立了一個對象,而後返回這個對象 例如:
Vue.component('async-example', function (resolve, reject) {
setTimeout(function () {
// 將組件定義傳入 resolve 回調函數
resolve({
template: '<div>I am async!</div>'
})
}, 1000)
})
工廠函數接收一個 resolve 回調,在收到從服務器下載的組件定義時調用。也能夠調用 reject(reason)
指示加載失敗。這裏使用 setTimeout 只是爲了演示,實際上如何獲取組件徹底由你決定。推薦配合
webpack 的代碼分割功能 來使用:
Vue.component('async-webpack-example', function (resolve) {
// 這個特殊的 require 語法告訴 webpack
// 自動將編譯後的代碼分割成不一樣的塊,
// 這些塊將經過 Ajax 請求自動下載。
require(['./my-async-component'], resolve)
})
你能夠在工廠函數中返回一個 Promise,因此當使用 webpack 2 + ES2015 的語法時能夠這樣:
Vue.component(
'async-webpack-example',
// 該 `import` 函數返回一個 `Promise` 對象。
() => import('./my-async-component')
)
當使用局部註冊時,也能夠直接提供一個返回 Promise 的函數:
new Vue({
// ...
components: {
'my-component': () => import('./my-async-component')
}
})
若是你是 Browserify 用戶,可能就沒法使用異步組件了,它的做者已經代表 Browserify 將「永遠不會支
持異步加載」。Browserify 社區發現了一些解決方法,可能會有助於已存在的複雜應用。對於其餘場景,咱們
推薦使用 webpack,由於它對異步加載進行了內置、全面的支持。
複製代碼
高級異步組件(2.3.0 新增)待研究
異步組件的工廠函數也能夠返回一個以下的對象:
const AsyncComp = () => ({
// 須要加載的組件。應當是一個 Promise
component: import('./MyComp.vue'),
// 加載中應當渲染的組件
loading: LoadingComp,
// 出錯時渲染的組件
error: ErrorComp,
// 渲染加載中組件前的等待時間。默認:200ms。
delay: 200,
// 最長等待時間。超出此時間則渲染錯誤組件。默認:Infinity
timeout: 3000
注意,當一個異步組件被做爲 vue-router 的路由組件使用時,這些高級選項都是無效的,由於在路由切換前
就會提早加載所須要的異步組件。另外,若是你要在路由組件中使用上述寫法,須要使用 vue-router 2.4.0
以上的版本。
複製代碼
組件命名約定
當註冊組件 (或者 prop) 時,可使用 kebab-case (短橫線分隔命名)、camelCase (駝峯式命名) 或
PascalCase (單詞首字母大寫命名)。
// 在組件定義中
components: {
// 使用 kebab-case 註冊
'kebab-cased-component': { /* ... */ },
// 使用 camelCase 註冊
'camelCasedComponent': { /* ... */ },
// 使用 PascalCase 註冊
'PascalCasedComponent': { /* ... */ }
}
在 HTML 模板中,請使用 kebab-case:
<!-- 在 HTML 模板中始終使用 kebab-case -->
<kebab-cased-component></kebab-cased-component>
<camel-cased-component></camel-cased-component>
<pascal-cased-component></pascal-cased-component>
當使用字符串模式時,能夠不受 HTML 大小寫不敏感的限制。這意味實際上在模板中,你可使用下面的方式來
引用你的組件:
kebab-case
camelCase 或 kebab-case (若是組件已經被定義爲 camelCase)
kebab-case、camelCase 或 PascalCase (若是組件已經被定義爲 PascalCase)
components: {
'kebab-cased-component': { /* ... */ },
camelCasedComponent: { /* ... */ },
PascalCasedComponent: { /* ... */ }
}
<kebab-cased-component></kebab-cased-component>
<camel-cased-component></camel-cased-component>
<camelCasedComponent></camelCasedComponent>
<pascal-cased-component></pascal-cased-component>
<pascalCasedComponent></pascalCasedComponent>
<PascalCasedComponent></PascalCasedComponent>
這意味着 PascalCase 是最通用的聲明約定而 kebab-case 是最通用的使用約定。
若是組件未經 slot 元素傳入內容,你甚至能夠在組件名後使用 / 使其自閉合:
<my-component/>
固然,這隻在字符串模板中有效。由於自閉的自定義元素是無效的 HTML,瀏覽器原生的解析器也沒法識別它。
複製代碼
遞歸組件(待研究)
# 組件在它的模板內能夠遞歸地調用本身。不過,只有當它有 name 選項時才能夠這麼作:
name: 'unique-name-of-my-component'
當你利用 Vue.component 全局註冊了一個組件,全局的 ID 會被自動設置爲組件的 name。
Vue.component('unique-name-of-my-component', {
// ...
})
若是稍有不慎,遞歸組件可能致使死循環:
name: 'stack-overflow',
template: '<div><stack-overflow></stack-overflow></div>'
上面組件會致使一個「max stack size exceeded」錯誤,因此要確保遞歸調用有終止條件 (好比遞歸調用時
使用 v-if 並最終解析爲 false)。
# 組件間的循環引用
假設你正在構建一個文件目錄樹,像在 Finder 或資源管理器中。你可能有一個 tree-folder 組件:
<p>
<span>{{ folder.name }}</span>
<tree-folder-contents :children="folder.children"/>
</p>
以及一個 tree-folder-contents 組件:
<ul>
<li v-for="child in children">
<tree-folder v-if="child.children" :folder="child"/>
<span v-else>{{ child.name }}</span>
</li>
</ul>
當你仔細看時,會發如今渲染樹上這兩個組件同時爲對方的父節點和子節點——這是矛盾的!當使用
Vue.component 將這兩個組件註冊爲全局組件的時候,框架會自動爲你解決這個矛盾。若是你已是這樣作的
,就跳過下面這段吧。
然而,若是你使用諸如 webpack 或者 Browserify 之類的模塊化管理工具來 require/import 組件的話,就會
報錯了:
Failed to mount component: template or render function not defined.
爲了解釋爲何會報錯,簡單的將上面兩個組件稱爲 A 和 B。模塊系統看到它須要 A,可是首先 A 須要 B,
可是 B 須要 A,而 A 須要 B,循環往復。由於不知道到底應該先解析哪一個,因此將會陷入無限循環。要解決
這個問題,咱們須要在其中一個組件中告訴模塊化管理系統:「A 雖然最後會用到 B,可是不須要優先導入 B」。
在咱們的例子中,能夠選擇讓 tree-folder 組件中來作這件事。咱們知道引發矛盾的子組件是
tree-folder-contents,因此咱們要等到 beforeCreate 生命週期鉤子中才去註冊它:
beforeCreate: function () {
this.$options.components.TreeFolderContents =
require('./tree-folder-contents.vue').default
}
複製代碼
內聯模板
# 若是子組件有 inline-template 特性,
組件將把它的內容看成它的模板,而不是把它看成分發內容。這讓模板編寫起來更靈活。
<my-component inline-template>
<div>
<p>這些將做爲組件自身的模板。</p>
<p>而非父組件透傳進來的內容。</p>
</div>
</my-component>
可是 inline-template 讓模板的做用域難以理解。使用 template 選項在組件內定義模板或者在 .vue
文件中使用 template 元素纔是最佳實踐。
# X-Template
另外一種定義模板的方式是在 JavaScript 標籤裏使用 text/x-template 類型,而且指定一個 id。例如:
<script type="text/x-template" id="hello-world-template">
<p>Hello hello hello</p>
</script>
Vue.component('hello-world', {
template: '#hello-world-template'
})
這在有不少大模板的演示應用或者特別小的應用中可能有用,其它場合應該避免使用,由於這將模板和組件的
其它定義分離了。
# 對低開銷的靜態組件使用 v-once
儘管在 Vue 中渲染 HTML 很快,不過當組件中包含大量靜態內容時,能夠考慮使用 v-once 將渲染結果緩存
起來,就像這樣:
Vue.component('terms-of-service', {
template: '\ <div v-once>\ <h1>Terms of Service</h1>\ ...不少靜態內容...\ </div>\ '
})
複製代碼
$mount()
當Vue實例沒有el屬性時,則該實例尚沒有掛載到某個dom中;
假如須要延遲掛載,能夠在以後手動調用vm.$mount()方法來掛載。例如:
<div id="app">
{{a}}
</div>
<button onclick="test()">掛載</button>
<script>
var obj = {a: 1}
var vm = new Vue({
data: obj
})
function test() {
vm.$mount("#app");
}
初始,顯示的是{{a}}
當點擊按鈕後,變成了1
複製代碼