vue組件
1 組件基礎
1.1 什么是組件
組件的概念
組件(Component)是 Vue.js 最強大的功能之一。組件可以擴展 HTML 元素,封裝可重用的代碼。在較高層面上,組件是自定義元素,Vue.js 的編譯器為它添加特殊功能。在有些情況下,組件也可以表現為用 is 特性進行了擴展的原生 HTML 元素。
所有的 Vue 組件同時也都是 Vue 的實例,所以可接受相同的選項對象(除了一些根級特有的選項)并提供相同的生命周期鉤子。
如何理解組件
簡單理解,組件其實就是一個獨立的 HTML,它的內部可能有各種結構、樣式、邏輯,某些地方來說有些像 iframe,它都是在頁面中引入之后展現另一個頁面的內容,但實際上它與 iframe 又完全不同,iframe 是一個獨立封閉的內容,而組件既是一個獨立的內容,還是一個受引入頁面控制的內容。
通常一個應用會以一棵嵌套的組件樹的形式來組織:
例如,你可能會有頁頭、側邊欄、內容區等組件,每個組件又包含了其它的像導航鏈接、博文之類的組件。
為什么要使用組件
舉個簡單的列子,最近我的項目中有一個日歷模塊,多個頁面都要用這個日歷,而每個頁面的日歷都存在一些差別,如果不使用組件,我要完成這個項目,做到各個頁面的日歷大體一致,而部分地方存在差異,我可能就需要寫幾套日歷代碼了。
而使用組件呢?一套代碼,一個標簽,然后分別在不同地方引用,根據不同的需求進行差異控制即可。
<calendar></calendar>
我可以通過給 calendar 傳遞值實現在本頁面對日歷的控制,讓它滿足我這個頁面的某些單獨需求。
有人會問,你 calendar 標簽是什么鬼?前面有這么一句話,組件是自定義元素。calendar 就是我自定義的元素,它就是一個組件。所以在項目中,你會發現有各種五花八門的標簽名,他們就是一個個組件。
1.2 創建組件
注冊組件
我們把創建一個組件稱為注冊組件,如果你把組件理解成為變量,那么注冊組件你就可以理解為聲明變量。我們通過 Vue.component 來注冊一個全局組件
Vue.component(componentName, {
//選項
})
對于自定義組件的命名,Vue.js 不強制遵循 W3C 規則(小寫,并且包含一個短杠),盡管這被認為是最佳實踐。
組件的選項
-
與創建Vue示例時的選項相同(除了一些根級特有的選項)
-
一個組件的 data 選項必須是一個函數 (每個組件實例具有自己的作用域,組件復用不會互相影響)
// 定義一個名為 button-counter 的新組件
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
組件的使用
<div id="components-demo">
<button-counter></button-counter>
</div>
組件可以復用
<div id="components-demo">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
組件模板
每個組件模板必須只有一個根元素
模板的形式:
-
template 選項指定字符串 (模板字符串)
-
單文件組件(.vue)
-
內聯模板 (不推薦)
<my-component inline-template> <div> <p>These are compiled as the component's own template.</p> <p>Not parent's transclusion content.</p> </div> </my-component> -
X-Templates
<script type="text/x-template" id="hello-world-template">Vue.component('hello-world', { template: '#hello-world-template' })
全局組件和局部組件
使用 Vue.component()注冊的組件都是全局組件
我們可以定義局部組件
var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }
new Vue({
el: '#app'
components: {
'component-a': ComponentA,
'component-b': ComponentB
}
})
2. 組件之間的嵌套使用和互相通信
組件設計初衷就是要配合使用的,最常見的就是形成父子組件的關系:組件 A 在它的模板中使用了組件 B。它們之間必然需要相互通信:父組件可能要給子組件下發數據,子組件則可能要將它內部發生的事情告知父組件。
每個組件的作用域都是獨立的,所以在組件嵌套使用的時候子組件不能直接使用父組件中的數據。
2.1 通過Prop向子組件傳遞數據
基本使用
在子組件中聲明 prop,然后添加一個 message
Vue.component('child', {
// 聲明 props
props: ['message'],
// 就像 data 一樣,prop 也可以在模板中使用
// 同樣也可以在 vm 實例中通過 this.message 來使用
template: '<span>{{ message }}</span>'
})
一個組件默認可以擁有任意數量的 prop,任何值都可以傳遞給任何 prop。我們能夠在組件實例中訪問這個值,
然后直接傳入值就可以在子組件中使用 message。
<child message="hello!"></child>
Prop 的大小寫
HTML 中的特性名是大小寫不敏感的,所以瀏覽器會把所有大寫字符解釋為小寫字符。這意味著當你使用 DOM 中的模板時,camelCase (駝峰命名法) 的 prop 名需要使用其等價的 kebab-case (短橫線分隔命名) 命名
傳入一個對象的所有屬性
<blog-post v-bind="post"></blog-post>
等價于
<blog-post
v-bind:id="post.id"
v-bind:title="post.title"
></blog-post>
Prop驗證
我們可以為組件的 prop 指定驗證要求
Vue.component('my-component', {
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 ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})
類型列表:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
- 自定義的構造函數
2.2 通過事件向父級組件發送消息
on(eventName)+on(eventName)+emit(eventName) 實現通訊
在父組件中使用 on(eventName)監聽事件,然后在子組件中使用on(eventName)監聽事件,然后在子組件中使用emit(eventName) 觸發事件,這樣就能實現子組件向父組件傳值。
Vue.component('button-counter', {
template: '<button v-on: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
console.log('第'+this.total+'次點擊')
}
}
})
<div id="counter-event-example">
<p>{{ total }}</p>
<button-counter v-on:increment="incrementTotal"></button-counter>
<button-counter v-on:increment="incrementTotal"></button-counter>
</div>
使用事件拋出一個值
有的時候用一個事件來拋出一個特定的值是非常有用的。這時可以使用 $emit 的第二個參數來提供這個值
incrementCounter: function () {
this.counter += 1
this.$emit('increment', this.counter)
}
然后當在父級組件監聽這個事件的時候,我們可以通過 $event 訪問到被拋出的這個值
<button-counter v-on:increment="postFontSize + $event"></button-counter>
或者,如果這個事件處理函數是一個方法:那么這個值將會作為第一個參數傳入這個方法:
<button-counter v-on:increment="incrementTotal"></button-counter>
methods: {
incrementTotal: function (enlargeAmount) {
this.postFontSize += enlargeAmount
}
}
在組件上使用 v-model
組件內input需要滿足條件:
- 將其
value特性綁定到一個名叫value的 prop 上 - 在其
input事件被觸發時,將新的值通過自定義的input事件拋出
Vue.component('custom-input', {
props: ['value'],
template: `
<input
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
`
})
v-model 在組件上的使用
<custom-input v-model="searchText"></custom-input>
<!-- 上面的寫法 等價于 下面的寫法 -->
<custom-input
v-bind:value="searchText"
v-on:input="searchText = $event"
></custom-input>
3 插槽 slot
3.1 通過插槽分發內容
Vue.component('alert-box', {
template: `
<div class="demo-alert-box">
<strong>Error!</strong>
<slot></slot>
</div>
`
})
<alert-box>
Something bad happened.
</alert-box>
·Something bad happened.· 會替換掉 slot標簽
3.2 模板中多個插槽
組件模板
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
調用組件
<base-layout>
<template slot="header">
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<p slot="footer">Here's some contact info</p>
</base-layout>
3.3 插槽默認內容
<button type="submit">
<slot>Submit</slot>
</button>
4. 動態組件
4.1 實現動態組件
在不同組件之間進行動態切換
<component is="組件名" class="tab"></component>
實現選項卡案例
4.2 在動態組件上使用 keep-alive
包裹動態組件時,會緩存不活動的組件實例,而不是銷毀它們
主要用于保留組件狀態或避免重新渲染
<!-- 基本 -->
<keep-alive>
<component :is="view"></component>
</keep-alive>
<!-- 多個條件判斷的子組件 -->
<keep-alive>
<comp-a v-if="a > 1"></comp-a>
<comp-b v-else></comp-b>
</keep-alive>
4.3 綁定組件選項對象
動態組件可以綁定 組件選項對象(有component屬性的對象),而不是已注冊組件名的示例
var tabs = [
{
name: 'Home',
component: {
template: '<div>Home component</div>'
}
},
{
name: 'Posts',
component: {
template: '<div>Posts component</div>'
}
},
{
name: 'Archive',
component: {
template: '<div>Archive component</div>',
}
}
]
new Vue({
el: '#dynamic-component-demo',
data: {
tabs: tabs,
currentTab: tabs[0]
}
})
<component
v-bind:is="currentTab.component"
class="tab"
>
</component>
5 組件的其他特性
5.1 解析 DOM 模板時的注意事項
有些 HTML 元素,諸如 <ul>、<ol>、<table> 和 <select>,對于哪些元素可以出現在其內部是有嚴格限制的。而有些元素,諸如 <li>、<tr> 和 <option>,只能出現在其它某些特定的元素內部。
<table>
<blog-post-row></blog-post-row>
</table>
上面的寫法,渲染效果會不甚理想,可以采用以下寫法
<table>
<tr is="blog-post-row"></tr>
</table>
需要注意的是如果我們從以下來源使用模板的話,這條限制是不存在的:
- 字符串 (例如:template: '...')
- 單文件組件 (.vue)
<script type="text/x-template">
5.2 Prop的一些問題
Prop的屬性名問題
HTML 中的特性名是大小寫不敏感的,所以瀏覽器會把所有大寫字符解釋為小寫字符。這意味著當你使用 DOM 中的模板時,camelCase (駝峰命名法) 的 prop 名需要使用其等價的 kebab-case (短橫線分隔命名) 命名
如果你使用字符串模板,那么這個限制就不存在了。
非Prop屬性
組件上定義的非Prop屬性 會傳遞到 組件模板的根元素上
class 和 style 特性會非常智能,即兩邊的值會被合并起來
對prop重新賦值
子組件中,對prop重新賦值,會報警告
5.3 組件事件的相關問題
將原生事件綁定到組件
想要在一個組件的根元素上直接監聽一個原生事件。這時,你可以使用 v-on 的 .native 修飾符
<base-input v-on:focus.native="onFocus"></base-input>
.sync 修飾符
在有些情況下,我們可能需要對一個 prop 進行“雙向綁定”
推薦以 update:my-prop-name 的模式觸發事件
//子組件中
this.$emit('update:title', newTitle)
<!-- 上級組件 模板中 -->
<text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
></text-document>
以上寫法可以換成下列寫法
<text-document v-bind:title.sync="doc.title"></text-document>
5.4 官方文檔-組件
深入了解組件
網址: https://cn.vuejs.org/v2/guide/components-registration.html

浙公網安備 33010602011771號