本文寫作時(shí)間為11月份,所以僅供參考其中的思路,如有與最新版本內(nèi)容不符者,以最新版本官方文檔為主;在大概 8 月底(2016年),有幸參與了企鵝 FM 和微云的微信小程序開發(fā),這篇文章是我對(duì) UI 邏輯分 ...
在大概 8 月底(2016年),有幸參與了企鵝 FM 和微云的微信小程序開發(fā),這篇文章是我對(duì) UI 邏輯分離的思考總結(jié),另由于微云的業(yè)務(wù)邏輯代碼實(shí)在太復(fù)雜勒……所以文章中將主要以 FM 為例。
在微云和企鵝 FM 項(xiàng)目中我們都是采用 UI 工程師+前臺(tái)工程師的模式,所以必然出現(xiàn)了我們(總是吐槽的)在日常頁(yè)面開發(fā)中會(huì)采用的方式:

在 html/wxml 結(jié)構(gòu)中用注釋的方式,告訴開發(fā) GG:“當(dāng)出現(xiàn) XXX 情況的時(shí)候,加上 YYY class”,于是業(yè)務(wù)邏輯代碼中,就一定會(huì)摻雜著 UI 邏輯,甚至 hard code 的地方:

FM 中不止一種場(chǎng)景會(huì)觸發(fā)“播放”/”暫停”邏輯的應(yīng)用,也就是有多種可能會(huì)觸發(fā) UI 的變化,那么 UI 邏輯還會(huì)有重復(fù)的地方。萬(wàn)一有一天需要更換或新增或刪除 class 名,就很有可能出錯(cuò)。
如果可以把 UI 邏輯獨(dú)立處理就好了,這是當(dāng)時(shí)我的想法。經(jīng)過(guò)合作的開發(fā) GG 提點(diǎn)之后,由于很多 UI 層的邏輯是跟著業(yè)務(wù)邏輯走的,所以完全剝離 UI 邏輯是不現(xiàn)實(shí)的。強(qiáng)行分離就需要把this 傳來(lái)傳去,在我看來(lái)也不是回事兒。所以 UI 邏輯采用的還是單純的“變量分離”,可以粗暴理解為,把當(dāng)時(shí)寫在注釋里的內(nèi)容,寫到獨(dú)立的 js 文件中。
下面以 FM 為例,來(lái)看看我是怎么做的吧~ FM 中 UI 會(huì)出現(xiàn)變化的是以下幾種場(chǎng)景:

因?yàn)樾〕绦虿恢С?nbsp;background-image ,所有圖片需要通過(guò) <image> 組件現(xiàn)實(shí),圖片的切換可以通過(guò)換不同的 src 值實(shí)現(xiàn)。
播放器有兩種顯示模式:mini 播放器和全屏播放器
這兩種模式是通過(guò)在播放器上切換 .mini class(mini 狀態(tài)需要 .mini )實(shí)現(xiàn)
全屏播放器的播放按鈕有“播放”和“暫停”兩種狀態(tài)(圖片)切換
當(dāng)播放器進(jìn)入全屏模式后,節(jié)目列表將被隱藏;而到 mini 播放器時(shí),節(jié)目列表將重新顯示出來(lái)
列表中的節(jié)目,播放按鈕有“播放”和“暫停”兩種狀態(tài)切換
同 2,通過(guò)切換 src 值實(shí)現(xiàn)(這里應(yīng)該也可以用 wx:if 來(lái)實(shí)現(xiàn))。
項(xiàng)目結(jié)構(gòu)如下,其中在 utils 目錄中的 view.js 是 UI 邏輯部分的代碼:

pages 目錄中的 js 文件將通過(guò) require 引用 view.js,view.js 中的接口分為“通用”和“頁(yè)面使用”這兩個(gè)類型。
module.exports = {// 通用general: {hide: 'hide',show: 'show',getScreenHeight: getScreenHeight},// 播放器頁(yè)面playerView : {class: {mini: 'mini', // 小播放器模式要有這個(gè) classlistItemPlaying: 'playing'},images: {listPlayBtn: '../../images/play/play-list.png',listPauseBtn: '../../images/play/pause-list.png',playerPlayBtn: '../../images/play/play-player.png',playerPauseBtn: '../../images/play/pause-player.png'}}// 其他頁(yè)面如果也有需要,以頁(yè)面為單位添加...}
在上面的代碼中,可以看到 general 里暴露了一個(gè) getScreenHeight 方法,它用于獲取屏幕高度,我們?cè)?nbsp;onLoad 的時(shí)候通過(guò)它,將設(shè)置了背景色的結(jié)構(gòu)的 min-height 屬性值設(shè)為屏幕高。用 js 的原因是,開發(fā)者工具 0.10 把 <page> 的高度 100% 去掉了,所以在 wxss 中就不能設(shè)置 height: 100% 把屏幕高度繼承下來(lái),但我們又要保證在頁(yè)面資源加載出來(lái)以前,用戶看到的就是全屏的暗色背景。
在頁(yè)面使用的接口里就分成了 class 和 images,如果未來(lái)出現(xiàn)更多 UI 變化的場(chǎng)景,可以再通過(guò)變量添加上去,比如 pageView.id。
舉個(gè)超級(jí)簡(jiǎn)單的例子(如下),模擬工作流程:

在 wxss 中定義好控制不同樣式的 class
將需要變化的 class 寫到 view.js 中,并暴露接口
在 wxml 中的對(duì)應(yīng)結(jié)構(gòu)中綁定 event handler
在對(duì)應(yīng)的 page.js 里實(shí)現(xiàn) event handler 的具體內(nèi)容,也就是切換 class 的觸發(fā)條件

老司機(jī)一看就知道是 MVVM 模式。
這樣分離也就是為了 UI 有獨(dú)立的控制器,不至于和業(yè)務(wù)邏輯耦合嚴(yán)重,在頁(yè)面開發(fā)的階段就可以完成 UI 上的變化。之前寫網(wǎng)頁(yè)的時(shí)候,當(dāng)需要 UI 變化的時(shí)候,我們常常會(huì)在頁(yè)面上綁定事件,示意前臺(tái)GG 邏輯是怎么樣的,但是那部分代碼可能并不能直接放入他們的業(yè)務(wù)邏輯中(可能是規(guī)范、邏輯沒(méi)有考慮完整……原因)。從這個(gè)角度上看,小程序反而能給 UI 工程師更多控制 UI 邏輯的能力,確定好代碼規(guī)范和接口,也能方便前臺(tái) GG 直接使用 UI 代碼,專心業(yè)務(wù)邏輯~