組件系統(tǒng)是Vue.js其中一個重要的概念溜在,它提供了一種抽象硬毕,讓我們可以使用獨立可復(fù)用的小組件來構(gòu)建大型應(yīng)用,任意類型的應(yīng)用界面都可以抽象為一個組件樹考蕾。
一耸携、四個核心組成
1、props 2辕翰、自定義事件 event 3、插槽及作用域插槽 slot 4狈谊、組件方法 method喜命。
任何組件均離不開以上4點,我們在開發(fā)過程中河劝,以這4點入手壁榕,封裝我們想要的組件。
以element-ui table組件為例
Table Attributes 指的是 props
props data 顯示的數(shù)據(jù)
Table Events 指的是 自定義事件
自定義事件 selection-change 當(dāng)選擇項發(fā)生變化時會觸發(fā)該事件
Table Slot 插槽
append 插入至表格最后一行之后的內(nèi)容赎瞎,如果需要對表格的內(nèi)容進行無限滾動操作牌里,可能需要用到這個 slot。若表格有合計行务甥,該 slot 會位于合計行之上牡辽。
Table-column Scoped Slot 插槽及作用域插槽 slot
Table-column 是 el-table-column組件,有一個默認插槽 自定義列的內(nèi)容敞临,參數(shù)為 { row, column, $index }
我們通常這樣使用
<el-table-column
fixed="right"
label="操作"
width="100">
<template slot-scope="scope">
<el-button @click="handleClick(scope.row)" type="text" size="small">查看</el-button>
<el-button type="text" size="small">編輯</el-button>
</template>
</el-table-column>
slot-scope="scope" 父組件通過scope訪問子組件作用域态辛。
Table Methods 指的是 組件方法
組件方法 clearSelection 用于多選表格,清空用戶的選擇 挺尿。 組件方法是通過 添加ref 索引奏黑,獲取組件實例后調(diào)用炊邦。this.$refs.組件ref標(biāo)識.組件方法
以上是組件核心概念。
二熟史、組件注冊
1馁害、組件名
組件名應(yīng)該始終是多個單詞的,根組件 App 除外
這樣做可以避免跟現(xiàn)有的以及未來的 HTML 元素相沖突蹂匹,因為所有的 HTML 元素名稱都是單個單詞的碘菜。
單文件組件的文件名應(yīng)該要么始終是單詞大寫開頭 (PascalCase),要么始終是橫線連接 (kebab-case)
混用文件命名方式有的時候會導(dǎo)致大小寫不敏感的文件系統(tǒng)的問題怒详,這也是橫線連接命名同樣完全可取的原因
使用 kebab-case
當(dāng)使用 kebab-case (短橫線分隔命名) 定義一個組件時炉媒,你也必須在引用這個自定義元素時使用 kebab-case,例如 <my-component-name>
Vue.component('my-component-name', { /* ... */ })
使用 PascalCase
當(dāng)使用 PascalCase (駝峰式命名) 定義一個組件時昆烁,你在引用這個自定義元素時兩種命名法都可以使用吊骤。也就是說 <my-component-name> 和 <MyComponentName> 都是可接受的。注意静尼,盡管如此白粉,直接在 DOM (即非字符串的模板) 中使用時只有 kebab-case 是有效的
Vue.component('MyComponentName', { /* ... */ })
2、全局注冊
以上方法都屬于全局注冊, 也就是說它們在注冊之后可以用在任何新創(chuàng)建的 Vue 根實例 (new Vue) 的模板中, 比如
HTML
<div id="app">
<component-a></component-a>
<component-b></component-b>
<component-c></component-c>
</div>
JS
Vue.component('component-a', { /* ... */ })
Vue.component('component-b', { /* ... */ })
Vue.component('component-c', { /* ... */ })
new Vue({ el: '#app' })
在所有子組件中也是如此鼠渺,也就是說這三個組件在各自內(nèi)部也都可以相互使用.
3鸭巴、局部注冊
如果不需要全局注冊,或者是讓組件使用在其它組件內(nèi)拦盹,可以用選項對象的 components 屬性實現(xiàn)局部注冊, 這里不做詳述鹃祖。
三、父子組件通信
在vue組件通信中其中最常見通信方式就是父子組件之中的通信普舆,而父子組件的設(shè)定方式在不同情況下又各有不同恬口。最常見的就是父組件為控制組件子組件為視圖組件。父組件傳遞數(shù)據(jù)給子組件使用沼侣,遇到業(yè)務(wù)邏輯操作時子組件觸發(fā)父組件的自定義事件祖能。無論哪種組織方式父子組件的通信方式都是大同小異
父組件到子組件通訊
父組件到子組件的通訊主要為:子組件接受使用父組件的數(shù)據(jù),這里的數(shù)據(jù)包括屬性和方法(String, Number, Boolean, Object, Array, Function)蛾洛。vue提倡單項數(shù)據(jù)流养铸,因此在通常情況下都是父組件傳遞數(shù)據(jù)給子組件使用,子組件觸發(fā)父組件的事件轧膘,并傳遞給父組件所需要的參數(shù)
通過 props 傳遞數(shù)據(jù) (推薦)
父子通訊中最常見的數(shù)據(jù)傳遞方式就是通過props傳遞數(shù)據(jù)钞螟,就好像方法的傳參一樣,父組件調(diào)用子組件并傳入數(shù)據(jù)谎碍,子組件接受到父組件傳遞的數(shù)據(jù)進行驗證使用
props 可以是數(shù)組或?qū)ο笊冈玻糜诮邮諄碜愿附M件的數(shù)據(jù)。props 可以是簡單的數(shù)組椿浓,或者使用對象作為替代太援,對象允許配置高級選項闽晦,如類型檢測、自定義校驗和設(shè)置默認值
prop 的定義應(yīng)該盡量詳細提岔,至少需要指定其類型
<!-- 父組件 -->
<template>
<div>
<my-child :parentMessage="parentMessage"></my-child>
</div>
</template>
<script>
import MyChild from '@components/common/MyChild'
export default {
components: {
MyChild
},
data() {
return {
parentMessage: "我是來自父組件的消息"
}
}
}
</script>
<!-- 子組件 -->
<template>
<div>
<span>{{ parentMessage }}</span>
</div>
</template>
<script>
export default {
props: {
parentMessage: {
type: String,
default: '默認顯示的信息'
// require: true // 必填
}
}
}
</script>
子組件到父組件通訊
子組件到父組件的通訊主要為父組件如何接受子組件之中的數(shù)據(jù)仙蛉。這里的數(shù)據(jù)包括屬性和方法(String, Number, Boolean, Object, Array, Function)
通過 $emit 傳遞父組件數(shù)據(jù) (推薦)
與父組件到子組件通訊中的$on配套使用,可以向父組件中觸發(fā)的方法傳遞參數(shù)供父組件使用
<!-- 父組件 -->
<template>
<div>
<my-child @childEvent="parentMethod"></my-child>
</div>
</template>
<script>
import MyChild from '@components/common/MyChild'
export default {
components: {
MyChild
},
data() {
return {
parentMessage: '我是來自父組件的消息'
}
},
methods: {
parentMethod({ name, age }) {
console.log(this.parentMessage, name, age)
}
}
}
</script>
<!-- 子組件 -->
<template>
<div>
<h3>子組件</h3>
</div>
</template>
<script>
export default {
mounted() {
this.$emit('childEvent', { name: 'zhangsan', age: 10 })
}
}
</script>
不推薦的通信方式
this.children
this.$refs
四碱蒙、兄弟組件通信
1荠瘪、vuex
Vuex 是一個專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式。它采用集中式存儲管理應(yīng)用的所有組件的狀態(tài)赛惩,它是響應(yīng)式的哀墓,狀態(tài)發(fā)生變化,組件會更新喷兼。
vuex實現(xiàn)兄弟組件通信非常簡單篮绰,組件A引用vuex數(shù)據(jù),組件B通過方法改變vuex數(shù)據(jù)季惯,vuex狀態(tài)是響應(yīng)式的吠各,數(shù)據(jù)放生變化,組件A會更新勉抓。
2贾漏、eventBus又稱為事件總線
在vue中可以使用eventBus來作為溝通橋梁, 就像是所有組件共用相同的事件中心,可以向該中心注冊發(fā)送事件或接收事件藕筋, 所有組件都可以通知其他組件纵散。
初始化
首先需要創(chuàng)建一個事件總線并將其導(dǎo)出, 以便其他模塊可以使用或者監(jiān)聽它。
我們在src/components/目錄下新建文件bus.js隐圾。
import Vue from 'vue'
export default new Vue()
發(fā)送事件
假設(shè)你有兩個兄弟組件: ComA和ComB困食,ComA發(fā)送消息給ComB。
ComA這樣
<template>
<div>
<button @click="sendMsg">給組件B發(fā)送消息</button>
</div>
</template>
<script>
import bus from './bus'
export default {
name: 'comA',
data () {
return {
}
},
methods: {
sendMsg() {
bus.$emit('fromA', {
phone: 13800138000
})
}
}
}
</script>
很顯然翎承,ComA中使用bus.$emit(事件名,數(shù)據(jù));向事件中心注冊發(fā)送事件。
接收事件
ComB接受ComA發(fā)送過來的消息符匾。
<template>
<div>
<p>{{fromA}}</p>
</div>
</template>
<script>
import bus from './bus'
export default {
name: 'comB',
data () {
return {
fromA: '',
}
},
mounted() {
bus.$on('fromA', param => {
this.fromA = param.phone;
})
}
}
</script>
于是叨咖,當(dāng)ComA發(fā)送了一個手機號碼phone給ComB時,comB就會接收并顯示啊胶。
父組件
在父組件中調(diào)用ComA和ComB兩個兄弟組件甸各。
<template>
<div>
<comA></comA>
<comB></comB>
</div>
</template>
<script>
import ComA from './ComA.vue'
import ComB from './ComB.vue'
export default {
components: {
ComA,
ComB
},
}
</script>
五、多層級組件通訊
1焰坪、vuex
2趣倾、eventBus
3、provide / inject
provide 和 inject 主要在開發(fā)高階插件/組件庫時使用某饰。并不推薦用于普通應(yīng)用程序代碼中儒恋。
這對選項需要一起使用善绎,以允許一個祖先組件向其所有子孫后代注入一個依賴,不論組件層次有多深诫尽,并在其上下游關(guān)系成立的時間里始終生效禀酱。
// 父級組件提供 'foo'
var Provider = {
provide: {
foo: 'bar'
},
// ...
}
// 子組件注入 'foo'
var Child = {
inject: ['foo'],
created () {
console.log(this.foo) // => "bar"
}
// ...
}
六、作用域插槽
有時讓插槽內(nèi)容能夠訪問子組件中才有的數(shù)據(jù)是很有用的牧嫉。例如剂跟,設(shè)想一個帶有如下模板的 <current-user> 組件:
<span>
<slot>{{ user.lastName }}</slot>
</span>
我們可能想換掉備用內(nèi)容,用名而非姓來顯示酣藻。如下:
<current-user>
{{ user.firstName }}
</current-user>
然而上述代碼不會正常工作曹洽,因為只有 <current-user> 組件可以訪問到 user 而我們提供的內(nèi)容是在父級渲染的。
為了讓 user 在父級的插槽內(nèi)容中可用辽剧,我們可以將 user 作為 <slot> 元素的一個 attribute 綁定上去:
<span>
<slot v-bind:user="user">
{{ user.lastName }}
</slot>
</span>
綁定在 <slot> 元素上的 attribute 被稱為插槽 prop∷拖現(xiàn)在在父級作用域中,我們可以使用帶值的 v-slot 來定義我們提供的插槽 prop 的名字:
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>
在這個例子中抖仅,我們選擇將包含所有插槽 prop 的對象命名為 slotProps坊夫,但你也可以使用任意你喜歡的名字。
七撤卢、組件的方法
組件方法环凿,就是我們寫組件時,在methods選項里邊定義的一些方法放吩,他通常是對數(shù)據(jù)的CURD智听。
element-ui我們常用的有
this.refs.[treeName].getCheckedKeys() tree 返回目前被選中的節(jié)點的 key 所組成的數(shù)組
八、vue 語法糖
1渡紫、v-model
v-model可以實現(xiàn)數(shù)據(jù)雙向的綁定到推,自動為組件添加了props 名為 value 和 自定義事件 名為 input。
<input type="text" v-model="name">
實際上,上面的代碼是下面代碼的語法糖惕澎。
<input v-bind:value="name" v-on:input="name=$event.target.value"/>
自定義組件的 v-model
一個組件上的 v-model 默認會利用名為 value 的 prop 和名為 input 的事件莉测,但是像單選框、復(fù)選框等類型的輸入控件可能會將 value attribute 用于不同的目的唧喉。model 選項可以用來避免這樣的沖突:
Vue.component('base-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
template: `
<input
type="checkbox"
v-bind:checked="checked"
v-on:change="$emit('change', $event.target.checked)"
>
`
})
現(xiàn)在在這個組件上使用 v-model 的時候:
<base-checkbox v-model="lovingVue"></base-checkbox>
這里的 lovingVue 的值將會傳入這個名為 checked 的 prop捣卤。同時當(dāng) <base-checkbox> 觸發(fā)一個 change 事件并附帶一個新的值的時候,這個 lovingVue 的 property 將會被更新八孝。
2董朝、.sync 修飾符
在有些情況下,我們可能需要對一個 prop 進行“雙向綁定”干跛。不幸的是子姜,真正的雙向綁定會帶來維護上的問題,因為子組件可以變更父組件楼入,且在父組件和子組件都沒有明顯的變更來源哥捕。
這也是為什么我們推薦以 update:myPropName 的模式觸發(fā)事件取而代之牧抽。舉個例子,在一個包含 title prop 的假設(shè)的組件中扭弧,我們可以用以下方法表達對其賦新值的意圖:
this.$emit('update:title', newTitle)
然后父組件可以監(jiān)聽那個事件并根據(jù)需要更新一個本地的數(shù)據(jù) property阎姥。例如:
<text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
></text-document>
為了方便起見,我們?yōu)檫@種模式提供一個縮寫鸽捻,即 .sync 修飾符:
<text-document v-bind:title.sync="doc.title"></text-document>
九呼巴、組件選項推薦順序
<script>
export default {
el: '#app', // 只在由 new 創(chuàng)建的實例中遵守。
// 全局感知
name: 'name', // 組件name
parent: VueInstance, // 指定父實例
// 組件類型
functional: false, // 沒有data 實例 沒有上下文
// 模板修改器
delimiters: ['${', '}'], // 分隔符變成了 ES6 模板字符串的風(fēng)格
comments: false, // 當(dāng)設(shè)為 true 時御蒲,將會保留且渲染模板中的 HTML 注釋衣赶。默認行為是舍棄它們。
// 模板依賴
components: {}, // 子組件
directives: {}, // 自定義指令
filters: {}, // 自定義過濾器
// 組合
extends: CompA, // 擴展另一個組件 和 mixins 類似
mixins: [tableEvents], // 混入選項對象厚满, 混入實例對象可以像正常的實例對象一樣
// 接口
inheritAttrs: true,
model: { // 自定義組件在使用 v-model 時定制 prop 和 event
prop: 'checked',
event: 'change'
},
propsData: { // 只用于 new 創(chuàng)建的實例中府瞄。 創(chuàng)建實例時傳遞 props。
},
// 本地狀態(tài)
data: () => ({ // 本地狀態(tài)
}),
computed: { // 計算屬性
},
// 事件 生命周期鉤子
watch: {
},
// 生命周期鉤子
beforeCreate() {
},
created() {
},
beforeMount() {
},
mounted() {
},
beforeUpdate() {
},
updated() {
},
activated() {
},
deactivated() {
},
beforeDestroy() {
},
destroyed() {
},
// 非響應(yīng)式的屬性 (不依賴響應(yīng)系統(tǒng)的實例屬性)
methods() {
},
// 渲染 (組件輸出的聲明式描述)
template: '<div>demo</div>', // 渲染模板
render: function (createElement) {
return createElement(
'h' + this.level, // 標(biāo)簽名稱
this.$slots.default // 子節(jié)點數(shù)組
)
},
renderError (h, err) { // 只在開發(fā)者環(huán)境下工作碘箍。
return h('pre', { style: { color: 'red' }}, err.stack)
}
}
</script>
十遵馆、組件樣式推薦使用CSS 作用域
<style scoped>
</style>