小程式自定義元件深度剖析
從小程式基礎庫版本 1.6.3 開始,小程式支援簡潔的元件化程式設計。檢視自己使用的小程式基礎庫版本,可以通過在開發者工具右側點選詳情檢視:
最基本的元件
小程式的元件,其實就是一個目錄,該目錄需要包含4個檔案:
- xxx.json
- xxx.wxml
- xxx.wxss
- xxx.js
宣告一個元件
首先需要在 json
檔案中進行自定義元件宣告(將 component
欄位設為 true
可這一組檔案設為自定義元件)
其次,在要引入元件的頁面的json檔案內,進行引用宣告
這樣,在主頁面就可以使用了。
相比於vue的元件引入,小程式的方案更簡潔。vue元件引入是需要 import 之後,同時在 components 裡面註冊,而小程式的元件只需要在 .json 裡面註冊,就可以在 wxml 裡面使用。
使用slot
和vue 相同,小程式也有slot概念。
單一slot
在元件模板中可以提供一個 <slot>
節點,用於承載元件引用時提供的子節點。
多個slot
如果需要在元件內使用多個slot, 需要在元件js中宣告啟用:
使用:
Component構造器
剛才我們說了,一個元件內應該包括js, wxml, wxss, json 四個檔案。wxml 相當於是 HTML,wxss 相當於是 css, 那麼js 裡面應該寫什麼呢?
微信官方提供的案例中:
裡面呼叫了一個Component構造器。Component構造器可用於定義元件,呼叫Component構造器時可以指定元件的屬性、資料、方法等。具體 Component裡面可以放什麼東西,如下所示:
元件與資料通訊
元件化必然要涉及到資料的通訊,為了解決資料在元件間的維護問題,vue, react,angular 有不同的解決方案。而小程式的解決方案則簡潔很多。
主頁面傳入資料到元件
properties相當於vue的props,是傳入外部資料的入口。
注意: 傳入的資料,不管是簡單資料型別,還是引用型別,都如同值複製一樣(和紅寶書裡面描述js函式引數傳入是值複製還不一樣,紅寶書裡面的意思是:簡單資料型別直接複製數值,引用型別複製引用,也就是說在函式內修改引數物件的屬性,會影響到函式外物件的屬性)。
如果是Vue的props, 則可以通過 .sync 來同步,而在小程式子元件裡面,呼叫this.setData()修改父元件內的資料,不會影響到父元件裡面的資料, 也就是說,子元件property的修改,彷彿和父元件沒有任何關係。那麼,如果是在子元件內修改父元件的資料,甚至是修改兄弟元件內的資料,有沒有簡單的方法呢?下面會有講到
元件傳出資料到主頁面
和vue類似,元件間互動的主要形式是自定義事件。
元件通過 this.triggerEvent()
觸發自定義事件,主頁面在元件上 bind:component_method="main_page_mehod"
來接收自定義事件。
其中,this.triggerEvent()
方法接收自定義事件名稱外,還接收兩個物件,eventDetail
和 eventOptions
。
元件之間資料通訊
和vue提出的vuex的解決方案不同,小程式的元件間的通訊簡單小巧。你可以和主頁面與元件通訊一樣,使用自定義事件來進行通訊,當然更簡單方便的方法,是使用小程式提供的relations.
relations 是Component 建構函式中的一個屬性,只要兩個元件的relations 屬性產生關聯,他們兩個之間就可以捕獲到對方,並且可以相互訪問,修改對方的屬性,如同修改自己的屬性一樣。
比如說,有兩個元件如程式碼所示:
他們之間的關係如下圖所示:
兩個元件捕獲到對方元件的例項,是通過 this.getRelationNodes('./path_to_a')方法。既然獲取到了對方元件的例項,那麼就可以訪問到對方元件上的data, 也可以設定對方元件上的data, 但是不能呼叫對方元件上的方法。
注意:1. 主頁面使用元件的時候,不能有數字,比如說 <component_sub1> 或 <component_sub_1>,可以在主頁面的json 裡面設定一個新名字
2. relations 裡面的路徑,比如說這裡:
是對方元件真實的相對路徑,而不是元件間的邏輯路徑。
3. 如果relations 沒有關聯,那麼 this.getRelationNodes 是獲取不到對方元件的
4. 本元件無法獲取本元件的例項,使用this.getRelatonsNodes('./ path_to_self ') 會返回一個null
4. type 可以選擇的 parent
、 child
、 ancestor
、 descendant
現在我們已經可以做到了兩個元件之間的資料傳遞,那麼如何在多個元件間傳遞資料呢?
如上圖所示,同級的元件b 和同級的元件c , b 和 c 之間不可以直接獲取,b可以獲取到a, c 也可以獲取到a,而a可以直接獲取到 b 和 c。所以,如果想獲取到兄弟元素,需要先獲取到祖先節點,然後再通過祖先節點獲取兄弟節點
我在元件b
裡面,我需要先找到祖先元件a
的例項,然後用祖先元件a
的例項的getRelationNodes
方法獲取到元件c
的例項。
看見沒?恐怕我們又要寫一大堆重複性的程式碼了。
幸好,微信小程式還提供了behavior 屬性, 這個屬性相當於 mixin,很容易理解的,是提高程式碼複用性的一種方法。
思路:
假設目前有三個元件,元件a, 元件b, 元件c, 其中元件b和元件c是兄弟元件,組建a是b和c的兄弟元件。為了減少程式碼的重複性,我們把獲取父元件的方法,和獲取兄弟元件的方法封裝一下,封裝在 behavior 的 methods 中。只要是引入該behavior的元件,都可以便捷的呼叫方法。
實現:
新建一個behavior檔案,命名無所謂,比如說relation_behavior.js
然後在元件b, 和 元件c 上引入該behavior,並且呼叫方法,獲取父元件和兄弟元件的例項
同時需要注意,c和b兩個元件,從relations屬性的角度來說,是a的後代元件。
但是元件b和元件c 所處的作用域, 都是主頁面的作用域,傳入的property都是主頁面的property,這樣就保證了元件資料的靈活性。relations 像一個隱形的鏈子一樣把一堆元件關聯起來,關聯起來的元件可以相互訪問,修改對方的資料,但是每一個元件都可以從外界獨立的獲取資料。
看了這麼多理論的東西,還是需要一個具體的場景來應用。
比如說,我們有個一個分享記錄圖片心情的頁面,當用戶點選【點贊】的按鈕時候,該心情的記錄 點贊按鈕會變紅,下面的一欄位置會多出點贊人的名字。
如果不通過元件化,很可能的做法是 修改一個點贊按鈕,然後遍歷資料更新資料,最後所有記錄列表的狀態都會被重新渲染一遍。
如果是通過元件化拆分:把點讚的按鈕封裝為 元件b
, 下面點贊人的框封裝為元件c,
每一個心情記錄都是一個元件a
下面是程式碼實現
這樣,元件b 可以修改元件c中的資料。同時,元件b 和 元件c 又可以通過 properties 和 事件系統,和主頁面保持獨立的資料通訊。