Vuejs - 組件式開發
初識組件
組件(Component)絕對是 Vue 最強大的功能之一。它可以擴展HTML元素,封裝可復用代碼。從較高層面講,可以理解組件為自定義的HTML元素,Vue 的編譯器為它添加了特殊強大的功能。所有的 Vue 組件同時也都是 Vue 的實例,因此可以接受相同的選項對象(除了一些特有的選項)並提供相同的生命周期函數。
再來回顧下 你也許不知道的Vuejs - 花式渲染目標元素 中的代碼:
1
|
<div id="app1">
|
1
|
Vue.component("helloworld", {
|
上面通過 Vue.component
註冊了一個全局組件,然後在 div#app
元素內通過 <helloworld/>
標簽直接使用。可以看出,這裏就是相當於自定義了一個 HTML 元素 helloworld
,它的功能就是輸出一個內容為 msg
的 h1
標簽。這就是一個基本全局組件的定義方式,當然你也可以註冊為局部組件:
1
|
var app2 = new Vue({
|
無論是全局或者局部註冊組件,它跟上一篇中的指令註冊是非常相似的。局部註冊組件就是在創建 Vue 實例的時候添加一個 components
對象屬性,它的鍵值對就是一個自定義組件,鍵是組件名,值是創建組建的配置對象參數。當然也可以將組件定義放到單獨的文件,然後通過引入的方式,然後添加到components屬性中,這個在單文件組件中會具體講到。
組件間通信
既然說到組件,就不得不說組件間通信了,實際開發中,我們經常需要在不同組件間傳遞/共享數據,所以實現組件間通信是非常重要的。
組件間關系可以總結為 父子組件
和 非父子組件
,自然通信方式也就是這兩種了。
父子組件間通信
如上圖,在 Vue 中,父子組件的關系可以總結為 props向下傳遞,事件向上傳遞
prop
給子組件下發數據,子組件通過 $emit 事件, 給父組件發送數據。先來看個例子:
1
|
<div id="app3">
|
1
|
Vue.component(‘com-input‘, {
|
原理解析:上面的代碼中,先通過
Vue.component
定義了com-input
組件,給它添加了props
屬性,用來接收父級通過屬性傳遞的屬性數據text
,這裏text
是個對象,含有type - 屬性值類型
和default - 默認值
兩個屬性。當然props
也可以為所有從父級接受的屬性數組,有關props
基礎知識請直接閱讀 官方文檔。然後將初始值賦值給了 data 中的msg
,該子組件的模板是個input
,通過v-model
實現input的值
和msg
的雙向綁定,當input值變化時,通過this.$emit(‘change‘, this.msg)
,發出change
事件,同時將當前值作為監聽器回調參數,這樣父級組件就可以通過v-on:change
來監聽此事件,獲取修改後的值,執行相關操作了。
雖然這段代碼同時實現了上述圖片中的 父 -> 子
和 子 -> 父
通信流程,但是代碼還是比較繁瑣的,單純實現單個數據的循環傳遞,就需要父子組件同時監聽改變事件,執行監聽回調函數,是不是太麻煩了。要是能直接修改 props
中的 text
值就好了,實踐證明,這是不行的,因為直接修改,會報下面錯誤(註意只有引入 vue.js
文件才會出現,因為 vue.min.js
文件移除了 [Vue warn]
錯誤提示功能):
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop’s value. Prop being mutated: “text”
這個問題,Vue 作者早就想到了,那就是使用 .sync 修飾符。早在 1.x
版本中此功能是一直存在的,但是作者認為它破壞了 單向數據流 的原則,所以 2.0
發布後,就移除了該修飾符,但是後來發現在實際開發中,有很多相關需求, 於是在 2.3.0+
版本後,又重新引入了 .sync
修飾符,不過內部實現是跟 1.x
版本有區別的,它並沒有破壞 單向數據流 原則,實際上內部就是幫我們實現了父級組件監聽和修改相關屬性值的操作。
使用 .sync
修改後的代碼如下:
1
|
<div id="app4">
|
1
|
Vue.component(‘com-input2‘, {
|
這次我們只是將子組件的 $emit
事件名修改為 update:text
,並刪除了父級組件 v-on:change
監聽和相關監聽回調,並在模板中 v-bind:text
後面添加了 .sync
修飾符,這樣就是實現了相同的功能,代碼確實精簡了很多。實際上 Vue 在編譯含有 .sync
修飾符的 v-bind
指令時,會自動實現監聽 update
事件的相關代碼,也就是:
1
|
<com-input2 v-bind:text.sync="text"/>
|
會被擴展為:
1
|
<com-input2 v-bind:text="text" v-on:update="val => text = val"/>
|
註意:
val => text = val
是箭頭函數,關於箭頭函數的介紹可以看這裏:箭頭函數
這樣一解析就很好理解了,全部是我們上一節講到的內容。
非父子組件間通信
如果是兩個非父子組件,並且有共同的父級組件,那麽它拆解為 子 -> 父 -> 子
的過程,這個就完全可以使用 父子組件間通信
方法實現。如果是多個組件或者不同父組件的組件間通信,這時我們可以借助創建空的 Vue 實例作為事件總線,通過 發布訂閱模式
進行數據傳遞。 代碼如下:
1
|
<div id="app5">
|
1
|
var bus = new Vue()
|
熟悉 發布訂閱模式
的同學,應該很容易理解上面這段代碼,創建的全局空 Vue 實例 bus
就是用來充當中央事件總線,所有的事件都經過它來觸發和傳播。
思路解析:在組件
com-a
中,當input
值發生改變時,通過bus.$emit(‘a-change‘, this.msg)
來觸發修改事件,並將其更新後的值做為參數傳遞,組件com-b
通過bus.$on(‘a-change‘, xxx)
來監聽,進行值更新操作,組件com-b
也是相同原理。
當然在復雜情況下,我們應該考慮使用專門的 狀態管理模式,比如 vuex,這個將在後續的文章中講到。
動態組件
Vue 中還提供了 component
元素,允許我們在實際開發中,通過修改其 is
屬性值,來動態切換組件。這個在某些應用場景非常實用,筆者曾經有個需求就是,需要根據參數 type
來繪制不同類型的圖表,而我的所有圖表類型都已經裝成了一個獨立的組件,所以我只需要依據此特性,通過參數 type
來動態修改元素 component
的屬性 is
為對應的組件名稱即可。
下面來看示例代碼:
1
|
<div id="app6">
|
1
|
var app6 = new Vue({
|
運行上面代碼,點擊改變組件按鈕,就可以輕松的實現組件 com-a
和 com-b
的動態切換了,是不是很酷?趕緊動手嘗試下吧。
Vuejs - 組件式開發