上一章咱們介紹了關於Vue實例中一些基本用法,可是組件、自定義指令、Render函數這些放到了本章來介紹,緣由是它們要比前面講的要難一些,組件是Vue.js最核心的功能,學習使用組件也是必不可少的知識點。html
在咱們學習組件以前,更深刻的瞭解下Vue實例,它除了data數據對象屬性外,Vue實例還暴露了一些有用的實例屬性和方法,它們都有前綴$,以便與用戶定義的屬性區分開來,詳細適用方法能夠查閱官方API文檔。vue
實例屬性:node
實例方法 / 數據:webpack
實例方法 / 事件:web
實例方法 / 生命週期:spring
組件是Vue.js中最強大的功能之一,核心目標是爲了可重用性高,減小重複性開發。組件須要註冊纔可使用,註冊有全局註冊和局部註冊兩種方式,全局註冊的組件能夠在任何Vue實例上均可以使用。Vue實例中使用compoents選項來註冊局部組件,局部組件只能在當前實例中使用,組件中也可使用compoents選項來註冊子組件,使組件能夠嵌套使用。vuex
全局註冊,代碼示例以下:express
<div id="app"> <my-component></my-component> </div> <script> Vue.component('my-component', { template: '<div>這裏是組件內容</div>' }) var app = new Vue({ el: '#app' }) </script>
渲染後的結果是:數組
<div id="app"> <div>這裏是組件內容</div> </div>
局部註冊,代碼示例以下:服務器
<div id="app"> <my-component></my-component> </div> <script> var app = new Vue({ el: '#app', components: { 'my-component': {template:'<div>這裏是組件內容</div>'} } }) </script>
組件中除了template選項外,還能夠像Vue實例那樣使用其餘選項,好比data、methods、computed等,data選項必須是函數,必須return返回纔有效。
代碼實例以下:
<div id="app"> <my-component></my-component> </div> <script> Vue.component('my-component', { template: '<div>這裏是組件內容</div>', data: function(){ return { message: '組件內容' //組件內部定義的數據 } } }) var app = new Vue({ el: '#app' }) </script>
prop傳遞數據:
Vue實例或父組件中調用子組件時,一般須要向子組件傳遞數據,這個過程須要經過prop來實現,組件中提供了props選項來接收參數,props的值分兩種,一種是字符串數組,另外一種是對象,使用對象方式實際項目中最爲常見,代碼示例以下:
<div id="app"> <my-component message="來自父組件的數據"></my-component> </div> <script> Vue.component('my-component', { props: ['message'], template: '<div>{{ message }}</div>' }) var app = new Vue({ el: '#app' }) </script>
一般父組件中傳遞的數據並非寫死的,而是來自父級的動態數據,這時可使用指令v-bind來動態綁定prop的值,當父組件的數據變化時也會傳遞給子組件,上面例子中props的值使用的是字符串數組方式,下面咱們使用另外一種對象方式接收,代碼實例以下:
<div id="app"> <input type="test" v-model="msg"> <my-component :message="msg"></my-component> </div> <script> Vue.component('my-component', { props: { message: String }, template: '<div>子組件中顯示:{{ message }}</div>' }) var app = new Vue({ el: '#app', data: { msg: '' } }) </script>
數據驗證:
當props值爲對象時,定義參數類型type,包括String、Number、Boolean、Object、Array、Function,參數也能夠設置初始值default,定義是否必傳參數required:true,還有自定義驗證函數等,當prop驗證失敗時,在開發版本下會在控制檯拋出一條警告。
代碼實例以下:
<script> Vue.component('my-component', { props: { propA: Number, //必須是數字類型 propB: [String, Number], //必須是字符串或數字類型 propC: { //布爾類型,若是未傳入,默認值爲true type: Boolean, default: true }, propD: { //數字類型,必傳參數 type: Number, required: true }, propE: { //若是是數組或對象類型,默認值必須是一個函數來返回 type: Array, default: function () { return []; } }, propF: { //自定義一個驗證函數 validator: function () { return value > 10; } } } }) </script>
自定義事件:
上面咱們知道了父組件向子組件傳遞數據時使用prop來完成,這裏說明一下prop值屬於引用類型,當改變prop值會直接影響父組件,重複使用組件時直接改變prop值時就失去了複用的目的。那麼子組件中向父組件傳遞數據要怎麼處理呢,這裏咱們就用到了自定義事件,自定義事件首先要在父組件中經過v-on監聽一個事件,事件鉤子函數在父組件實例中建立,在子組件中經過this.$emit()來觸發這個自定義事件,$emit()方法的第一個參數是自定義事件的名稱,後面參數爲要傳遞的數據,後面參數能夠爲空或多個,代碼實例以下:
<div id="app"> <p>總數:{{ total }}</p> <my-component @inccount="handleInc"></my-component> </div> <script> Vue.component('my-component', { template: '<div><button @click="handleIncrease">+1</button></div>', data: function(){ return { counter: 0 } }, methods: { handleIncrease: function(){ this.counter++; this.$emit('inccount', this.counter); //觸發父組件中自定義事件 } }, }) var app = new Vue({ el: '#app', data: { total: 0 }, methods: { handleInc: function (count){ this.total = count; } } }) </script>
組件上使用v-model:
前面章節中咱們講過v-model是一個特殊的語法糖,實際它等同於input自定義事件,咱們這裏經過v-model來建立自定義的表單輸入組件,進行數據雙向綁定,代碼實例以下:
<div id="app"> <p>總數:{{ total }}</p> <my-component v-model="total"></my-component> </div> <script> Vue.component('my-component', { props: ['value'], template: '<div><input :value="value" @input="updateValue"></div>', methods: { updateValue: function(){ this.$emit('input', event.target.value); } }, }) var app = new Vue({ el: '#app', data: { total: 0 } }) </script>
非父子組件通訊:
在實際業務中,除了父子組件通訊外,還有不少非父子組件通訊的場景,好比兄弟組件和跨多級組件,Vue.js中提供了一個方法,建立一個空的Vue實例做爲中央事件總線(bus),也就是一箇中介,初始化Vue實例時,監聽這個中介事件來完成本身的業務邏輯。除了它Vue還提供了一個更好的解決方案 vuex狀態管理插件。
Vue.js 實現了內容分發,使用slot元素做爲承載分發內容的出口,混合父組件內容與子組件的模板時使用。
單個Slot:
在子組件內使用特殊的<slot>元素就能夠開啓一個slot默認插槽,在父組件模板中調用子組件標籤內的全部內容將替換子組件的<slot>元素內的內容,代碼實例以下:
<div id="app"> <my-component> <p>分發的內容</p> <p>更多分發的內容</p> </my-component> </div> <script> Vue.component('my-component', { template: '\ <div>\ <slot>\ <p>若是父組件沒有插入內容,我將做爲默認出現</p>\ </slot>\ </div>' }) var app = new Vue({ el: '#app' }) </script>
渲染後的結果是:
<div id="app"> <div> <p>分發的內容</p> <p>更多分發的內容</p> </div> </div>
具名Slot:
子組件模板中有時咱們須要多個插槽,slot元素中指定name屬性,能夠分發多個內容,具名Slot能夠與單個Slot共存,自 2.6.0 起咱們能夠在一個 <template> 元素上使用 v-slot 指令,代碼實例以下:
<div id="app"> <my-component> <h2 slot="header">標題</h2> <p>分發的內容</p> <p>更多分發的內容</p> <h5 slot="footer">底部信息</h5> </my-component> <!-- 自 2.6.0 起新推薦的語法 --> <my-component> <template v-slot:header> <h2>標題</h2> </template> <p>分發的內容</p> <p>更多分發的內容</p> <template v-slot:footer> <h5>底部信息</h5> </template> </my-component> </div> <script> Vue.component('my-component', { template: '\ <div>\ <div class="header">\ <slot name="header"><slot>\ </div>\ <div class="main">\ <slot><slot>\ </div>\ <div class="footer">\ <slot name="footer"><slot>\ </div>\ </div>' }) var app = new Vue({ el: '#app' }) </script>
編譯做用域:
父級模板裏的全部內容都是在父級做用域中編譯的;子模板裏的全部內容都是在子做用域中編譯的。
做用域插槽:
做用域插槽是一種特殊的Slot,使用slot-scope特性能夠接收傳遞給插槽的 prop,自 2.6.0 起已廢棄的使用 slot-scope 特性語法,新推薦的語法在<slot>元素的一個特性綁定子組件內部數據,這個特性綁定稱爲插槽prop,在父組件中v-slot具名插槽帶一個值類定義咱們提供的插槽prop的名字,代碼實例以下:
<div id="app"> <my-component :user="{firstName:'newfirst',lastName:'newlast'}"> <!-- user名字能夠隨意定義 --> <template v-slot:default="user"> {{user.lastName}} </template> <template v-slot:main="data"> {{data.message}} </template> </my-component> </div> <script> Vue.component('my-component', { data() { return { message: '子組件內部消息' } }, props: { user: { type: Object, default: function () { return { firstName: 'first', lastName: 'last' }; } } }, template: '\ <div>\ <div>\ <slot :lastName="user.lastName">\ {{user.firstName}}\ </slot>\ </div>\ <div>\ <slot name="main" :message="message">\ </slot>\ </div>\ </div>' }) var app = new Vue({ el: '#app' }) </script>
咱們已經介紹過了許多Vue內置的指令,好比 v-if、 v-show 等,這些內置指令能知足咱們絕大部分業務需求,不過在須要一些特殊功能時,咱們仍然但願對DOM底層進行操做,這時咱們就要用到自定義指令來完成。
註冊自定義指令:
自定義指令的註冊方法也分全局註冊和局部註冊,跟組件註冊方式很像,代碼實例以下:
// 註冊一個全局自定義指令
Vue.directive('focus', {
inserted: function (el) {
el.focus()
}
})
//在組件中註冊局部自定義指令
var app = new Vue({
el: '#app',
directives: {
focus: {
inserted: function (el) {
el.focus()
}
}
}
})
鉤子函數:
一個指令定義對象能夠提供以下幾個鉤子函數,每一個都是可選的。
鉤子函數參數:
指令鉤子函數會被傳入如下參數:
下面是結合了以上參數的一個具體示例,代碼實例以下:
<div id="hook-arguments-example" v-demo:foo.a.b="message"></div> Vue.directive('demo', { bind: function (el, binding, vnode) { var s = JSON.stringify el.innerHTML = 'name: ' + s(binding.name) + '<br>' + 'value: ' + s(binding.value) + '<br>' + 'expression: ' + s(binding.expression) + '<br>' + 'argument: ' + s(binding.arg) + '<br>' + 'modifiers: ' + s(binding.modifiers) + '<br>' + 'vnode keys: ' + Object.keys(vnode).join(', ') } }) new Vue({ el: '#hook-arguments-example', data: { message: 'hello!' } })
什麼是Render函數,Vue.js2.x開始使用了Virtual Dom(虛擬DOM)來更新DOM節點,提高渲染性能,Vue.js編譯時會把template模板解析爲Virtual Dom,Vue.js也提供了Render函數選項,即渲染函數,使用 JavaScript 代替模板功能。組件的template基本上知足咱們業務需求,但有些場景中,使用Virtual Dom會更簡單。(這個咱們暫時用不到,若是想了解的朋友能夠去官方文檔中進一步學習)
Vue.js