1. 程式人生 > >小程式自定義元件深度剖析

小程式自定義元件深度剖析

從小程式基礎庫版本 1.6.3 開始,小程式支援簡潔的元件化程式設計。檢視自己使用的小程式基礎庫版本,可以通過在開發者工具右側點選詳情檢視:

最基本的元件

小程式的元件,其實就是一個目錄,該目錄需要包含4個檔案:

  1. xxx.json
  2. xxx.wxml
  3. xxx.wxss
  4. 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 和 事件系統,和主頁面保持獨立的資料通訊。