1. 程式人生 > >微信小程序組件化(上)

微信小程序組件化(上)

== 什麽 解決方案 chan json 特性 支持 減少 ble

小程序對組件化的“支持”情況

微信小程序(以下簡稱“小程序”,版本)雖然默認定義了很多有用的組件,但是在開發小程序過程中,往往需要自定義業務組件。
而小程序開發者文檔中卻未對自定義組件給出很好的解決方案或示例。

猜其原因可能有兩方面:

  1. 從小程序開放的API來看,它去除了DOM和BOM,視圖與數據層交互采用簡單的單向數據綁定和事件綁的形式。可能其初衷是想降低開發難度和學習門檻,盡量減少概念。
  2. 小程序推出時間不到一年,這些功能可能還在完善中。

自定義組件的難點

微信的組件化,總體而言,目標就是把具有特定功能和樣式的wxml、wxss、js(可選)文件抽取出來,以便不同頁面之間進行復用。
我們從實現上來考慮是否可行:

  • wxss支持文件之間的引用,采用命名隔離的話應該可以支持組件化的需求。即把不同組件拆成不同的wxss,通過一致的命名與組件的試圖、邏輯對應,同時組件樣式選擇器都掛載在跟樣式下。使用時記得引用該wxss就好。當然預編譯語言中所使用的變量、函數什麽的就不要想了~
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// tab component
-
|- tab.wxml
|- tab.js
|- tab.wxss

// tab.wxss
.tab {
...
}
.tab .itme {
...
}
...
  • wxml也支持引用,兩種方式:template和include。區別在於作用域。include相當於把代碼拷貝到當前位置,與頁面共享作用域,而import擁有自己的獨立作用於,一般需要傳入對應的參數。這麽看似乎template更適合,可是template的事件綁定卻仍是和父頁面共享作用域,也就是說數據在template獨立作用域中,事件綁定在頁面內作用域,兩者之間的相互引用就會變得相當尷尬,還不如include順暢。
  • 小程序支持ES6,所以我們可以用ES6的模塊管理方式來引入對應的js文件。
  • 因為json是針對於頁面進行的配置,組件關心的是局部樣式和邏輯,所以組件化的時候我們並不需要它。

因為我們采用了include的方式共享了作用域,在簡單頁面的情況這種方式也不會出現什麽問題,如果變量名或事件名被占用的時候換一個就好了。
但是在頁面引用多個組件的情況下如何保證它們之間不產生沖突?你可能想到了用老辦法命名隔離,組件內部變量和事件添加組件前綴用來和其它組件區分。
OK,在組件名不重復的情況下這是可行的。但是如果一個組件要同時觸發自身內部事件和父頁面事件就不是這麽簡單了。
所以我們需要解決的一個關鍵問題是:組件如何隔離作用域,並暴露屬性或接口(函數)給頁面或其它組件?

組件化解決方案

第三方實現

我們在探索組件化實現方式時,有一個第三方模塊wepy脫穎而出。star數量超過了1k,文檔清晰簡約。
看看它的主要功能:

  1. 將小程序開發模式轉為MVVM方式
  2. 支持類Vue.js 2.x風格的組件開發,包括單文件組件,支持scss、pug等。
  3. 支持加載外部NPM包
  4. 使用babel編譯,支持ES6/ES7特性

MVVM多用於web單頁應用,而小程序明顯不是單頁應用,而且也不支持雙數據綁定,轉為MVVM方式更多只是習慣上的喜好,談不上什麽優點。
小程序也支持ES6特性(以前也支持ES7,不知道為什麽新版本取消了),用ES6特性可以完成外部NPM包引用。

所以最吸引人的功能就是第二點:支持組件化。

拿到官方給的示例wepy-wechat-demo編譯後對比源碼和編譯後的代碼來探究其實現組件化的思路。

整個項目非常清晰簡單,我們以index頁面調用tab組件為例進行分析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// src目錄
-
|- components
- tab.wpy
...
|- pages
- index.wpy
...

// dist目錄
- dist
|- components
- tab.wxss
- tab.js
|- pages
- index.js
- index.json
- index.wxml
- index.wxss

js文件用ES6編寫,通過babel轉成ES5形式,看起來比較費勁,不過幸好我們暫時也不需要分析它。打開index.wxss和tab.wxss可以看到組件樣式的編寫和引用方式如我們所料,[email protected]

1
2
3
4
5
6
7
8
9
10
11
12
// dist/components/tab.wxss
.tab {
...
}
.tab .item {
...
}
...

// dist/pages/index.wxss
@import "./../components/tab.wxss";
...

很奇怪,在編譯好的components文件夾中只能看到組件命名的js和wxss文件,wxml文件消失了~
所以先來看看index.wxml中怎麽體現的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// dist/pages/index.wxml
<view class="tab">
<view class="tab_item tab_message{{$tab$active == 0 ? ‘ active‘ : ‘‘}}" bindtap="$tab$change" data-wepy-params-a="0">
<image class="icon" src="../images/message{{$tab$active == 0 ? ‘_active‘ : ‘‘}}.png"/>
<text class="title">微信</text>
</view>
...
</view>

// src/components/tab.wxml
<template>
<view class="tab">
<view class="tab_item tab_message{{active == 0 ? ‘ active‘ : ‘‘}}" @tap="change(0)">
<image class="icon" src="../images/message{{active == 0 ? ‘_active‘ : ‘‘}}.png"></image>
<text class="title">微信</text>
</view>
...
</view>
</template>

這段編譯後的代碼和tab.wxml源文件基本一致,發生改變的是數據綁定和事件綁定的地方,進行了重命名。
由此可以推斷,是通過wepy的編譯工具wepy-cli,把頁面上的組件引用替換成了源碼並通過修改變命名來隔離組件作用域避免沖突。
而這些重新修改的變量和事件如何起作用?
wepy的文檔中提到組件支持回調和事件冒泡、事件下發,要支持這個特性除了按其要求的格式進行編寫之外,還需要在js中引用一個叫wepy的模塊,如

import wepy from ‘wepy‘

由此可以推斷,wepy-cli編譯的時候進行了模板替換和變量名(事件名)替換,然後wepy模塊來處理替換後的事件調用以及組件之間的通信。

如果仔細翻閱文檔會發現其對組件的支持非常接近Vue.js 2.x,比如computed、watcher屬性。行文此處,給作者由衷地點個贊。

那麽這種方式是否就是最理想的組件化方式,或者說這種方式有什麽缺陷呢?

微信小程序組件化(上)