作者:馬倩
轉(zhuǎn)發(fā)鏈接:
https://mp.weixin.qq.com/s/LlZB6ZC5NkxnBj-C5iZNnA
Kbone原理解析
官方介紹
“Kbone 是一個(gè)致力于微信小程序和 Web 端同構(gòu)的解決方案。”
Web端框架基本原理
首先我們來(lái)看下普通Web端框架,以Vue框架為例,一份Vue模板對(duì)應(yīng)一個(gè)組件,在代碼構(gòu)建階段編譯成調(diào)用Dom接口的JS函數(shù),執(zhí)行此JS函數(shù)就會(huì)創(chuàng)建出組件對(duì)應(yīng)的Dom樹(shù),從而渲染到瀏覽器頁(yè)面上。

然而,小程序是雙線程的,并沒(méi)有Dom樹(shù)的概念,邏輯層和視圖層完全分離,邏輯層是一個(gè)純粹的JSCore,開(kāi)發(fā)者可以編寫(xiě)JS腳本,但是無(wú)法直接調(diào)用Dom/Bom的api, 沒(méi)有任何瀏覽器相關(guān)的實(shí)現(xiàn)。
在小程序中,視圖層和邏輯層的交互是通過(guò)數(shù)據(jù)和時(shí)間驅(qū)動(dòng)的。
因此,要實(shí)現(xiàn)跨端同構(gòu),問(wèn)題是:怎么將web端代碼轉(zhuǎn)為小程序代碼?
業(yè)界常規(guī)做法
目前業(yè)界流行的第三方跨端框架們,常規(guī)做法都是:靜態(tài)編譯兼容。

原理是把代碼語(yǔ)法分析一遍,然后將其中的模板部分翻譯成對(duì)應(yīng)的跨端需求的模板(微信小程序、支付寶小程序、H5、App等)。
靜態(tài)編譯最大的局限性是無(wú)法保證轉(zhuǎn)換的完整性,因?yàn)閂ue模板和WXML模板的語(yǔ)法并不是直接對(duì)等的,Vue的特性設(shè)計(jì)也和小程序的設(shè)計(jì)無(wú)法劃等號(hào),這自然就導(dǎo)致了部分Vue特性的丟失。
比如像Vue中的v-html指令、ref獲取Dom節(jié)點(diǎn)、過(guò)濾器等就通通用不了。
除了Vue自身的特性外,一些原本依賴Dom/Bom接口的Vue插件也無(wú)法使用,例如Vue-Router。
Kbone的做法
Kbone是通過(guò)提供 適配器 的方式來(lái)實(shí)現(xiàn)同構(gòu),即運(yùn)行時(shí)兼容,而非靜態(tài)編譯。
Kbone的適配器核心包含兩個(gè)部分:
miniprogram-render:仿造Dom/Bom接口,構(gòu)造仿造Dom樹(shù);
miniprogram-element: 監(jiān)聽(tīng)仿造Dom樹(shù)變化,渲染到頁(yè)面,同時(shí)監(jiān)聽(tīng)用戶行為,觸發(fā)事件。
仿造Dom樹(shù)和瀏覽器的運(yùn)行時(shí)對(duì)比:
仿造Dom樹(shù):
利用內(nèi)置組件和自定義組件的自引用來(lái)進(jìn)行遞歸,創(chuàng)建組件樹(shù)。
如圖,自定義custom-dom為遞歸自引用組件:

遞歸的終止條件是遇到特定節(jié)點(diǎn)、文本節(jié)點(diǎn)或者children空節(jié)點(diǎn)。
在創(chuàng)建出組件樹(shù)后,將Dom節(jié)點(diǎn)和自定義組件實(shí)例進(jìn)行綁定,以便后續(xù)的Dom更新和操作。

kbone這里還對(duì)節(jié)點(diǎn)數(shù)進(jìn)行了優(yōu)化:
如果一個(gè)dom節(jié)點(diǎn)對(duì)應(yīng)一個(gè)自定義組件的話,就會(huì)創(chuàng)建很多自定義組件,這樣會(huì)很浪費(fèi)開(kāi)銷,這里做了子樹(shù)的合并,也就是說(shuō)3層才創(chuàng)建一個(gè)自定義組件,節(jié)省開(kāi)銷。
優(yōu)化前:17個(gè)dom=17個(gè)自定義組件;優(yōu)化后:17個(gè)dom=4個(gè)自定義組件,藍(lán)色那個(gè)是單節(jié)點(diǎn),會(huì)合并到上面的樹(shù);

dom 子數(shù)作為自定義組件渲染的層級(jí)數(shù)是可以通過(guò)配置傳入,理論上層級(jí)越多,使用自定義組件數(shù)量越少,性能也就越好。
一顆很大的 Dom 樹(shù),一次性 setData 到視圖層,可能會(huì)超過(guò) setData 的大小限制(1024kB),拆分成多顆子 Dom 樹(shù),然后分批的 setData 到視圖層,可以節(jié)省開(kāi)銷。
事件監(jiān)聽(tīng)
小程序的事件是視圖層到邏輯層的通訊方式,事件綁定在組件上,當(dāng)被觸發(fā)時(shí),就會(huì)執(zhí)行邏輯層中對(duì)應(yīng)的事件處理函數(shù)。
小程序的捕獲冒泡是在視圖層view端,因此邏輯層在整個(gè)捕獲冒泡流程中各個(gè)節(jié)點(diǎn)接收到的事件不是同一個(gè)對(duì)象,小程序事件的捕獲冒泡和阻止冒泡等操作必須在WXML模板中聲明,無(wú)法使用接口實(shí)現(xiàn)。

為了能夠讓web端和小程序端的事件系統(tǒng)行為一致,kbone除了仿造了一份Dom樹(shù)外,也把整個(gè)事件系統(tǒng)仿造了一份,即在仿造Dom樹(shù)上進(jìn)行捕獲冒泡。
當(dāng)自定義組件監(jiān)聽(tīng)到用戶的操作后,就將事件發(fā)往仿造Dom樹(shù),后續(xù)自定義組件監(jiān)聽(tīng)到的同一個(gè)事件的冒泡就直接忽略。
當(dāng)觸發(fā)該節(jié)點(diǎn),仿造Dom樹(shù)接收到事件后,再進(jìn)行捕獲和冒泡,讓事件在各個(gè)節(jié)點(diǎn)觸發(fā)。

Kbone的優(yōu)勢(shì)
- 支持多個(gè)前端框架:Vue、React、Preact 等
- 支持更為完整的前端框架特性:Vue 中的 v-html 指令、Vue-router 插件等
- 提供了常用的 dom/bom 接口
- 可以使用小程序本身的特性:live-player 內(nèi)置組件、分包功能等
- 提供一些 Dom 擴(kuò)展接口:getComputedStyle 接口等
Kbone實(shí)踐
腳手架kbone-cli
官方已經(jīng)提供了kbone-cli可以用來(lái)快速開(kāi)發(fā):
用npm全局安裝kbone-cli

可以根據(jù)自己的技術(shù)棧選擇不同的開(kāi)發(fā)模板:React/Vue/Omi/Preact

然后就可以愉快的進(jìn)行開(kāi)發(fā)啦~

生成的demo項(xiàng)目結(jié)構(gòu)如下:

demo中包含了多頁(yè)跳轉(zhuǎn)、vue-router、vuex等的使用示例,以及mp-webpack-plugin的配置示例。
對(duì)于多頁(yè)面的應(yīng)用,在 Web 端可以直接通過(guò) a 標(biāo)簽或者 location 對(duì)象進(jìn)行跳轉(zhuǎn),但是在小程序中則行不通。同時(shí) Web 端的頁(yè)面 url 實(shí)現(xiàn)和小程序頁(yè)面路由也是完全不一樣的。
Demo實(shí)例對(duì)比

其中,有一部分兩端差異的業(yè)務(wù)邏輯功能,也給出了3種不同的解決方案:
利用vue-improve-loader,在構(gòu)建時(shí)對(duì)dom樹(shù)節(jié)點(diǎn)進(jìn)行刪減,在需要提出的節(jié)點(diǎn)加上check-reduce屬性 利用reduce-loader,將業(yè)務(wù)中不需要被打包的代碼進(jìn)行去除,使用行內(nèi)loader和環(huán)境變量來(lái)判斷 使用樣式隱藏,即設(shè)置不需要顯示的節(jié)點(diǎn)樣式為 display:none
其他問(wèn)題
在實(shí)際開(kāi)發(fā)中,還會(huì)碰到一些細(xì)節(jié),例如:
- 多頁(yè)面開(kāi)發(fā):修改webpack和mp-webpack-plugin配置
- 小程序內(nèi)置組件:部分用html標(biāo)簽代替,其他用wx-component + behavior標(biāo)簽
- 小程序自定義組件:修改mp-webpack-plugin配置,補(bǔ)充wxCustomComponents字段,將自定義組件放入組件根目錄,使用自定義組件
- 自定義app.js和app.wxss:監(jiān)聽(tīng)app的生命周期,修改webpack配置補(bǔ)充app.js的構(gòu)建入口,修改插件配置的generate.app字段,補(bǔ)充app.js
- 擴(kuò)展dom/bom對(duì)象和API:使用 window.$$extend追加方法
- 代碼優(yōu)化:用reduce-loader做體積精簡(jiǎn),dom樹(shù)精簡(jiǎn)用vue-improve-loader
- 區(qū)分環(huán)境實(shí)現(xiàn)不同功能:process.env.isMiniprogram
更新迭代
kbone由于目前在快速發(fā)展期,更新迭代非常迅速,以下特性是對(duì)比了8月份的版本和11月份版本,可以看出已經(jīng)解決了近2/3的問(wèn)題。

小程序技術(shù)選型
詳細(xì)了解了kbone之后,我們來(lái)分析下小程序技術(shù)框架到底應(yīng)該怎么選?
kbone & 小程序原生
- 已有web版,需要小程序版:kbone
- 跨平臺(tái)需求(web + 小程序):kbone
- 對(duì)性能特別苛刻 or 追求穩(wěn)定 or 要用最新功能:小程序原生
- 頁(yè)面節(jié)點(diǎn)數(shù)量特別多( 1000 節(jié)點(diǎn)以上),且對(duì)渲染性能要求高:靜態(tài)模板轉(zhuǎn)義方案(第三方框架:mpvue/taro等)
第三方框架
- MpVue :不推薦再用了,坑越來(lái)越多,內(nèi)部也表示之后不會(huì)投入太多維護(hù)
- WePY 1.7.x :不推薦再用了:1.7.x 的版本在最初的設(shè)計(jì)上的缺陷導(dǎo)致遺留了很多比較嚴(yán)重的問(wèn)題
- WePY 2.0:現(xiàn)在還是 alpha 階段,內(nèi)外部有一些小程序在跑,體驗(yàn)和反饋還可以。但依然 issue 比較多。害怕踩坑的也不推薦使用
- Taro: 也還是有不少問(wèn)題,但相對(duì)來(lái)說(shuō)應(yīng)該是比 mpVue 和 WePY 更穩(wěn)定一點(diǎn)
- Uni-app:mpvue的衍生版,跨端 (官方示例有6段) 支持的很好,在H5端實(shí)現(xiàn)了一套微信模擬器,可以嘗試,是目前唯一支持app端的商用方案,有獨(dú)立的編輯器HBuildX
- Chameleon: 統(tǒng)一的多態(tài)協(xié)議,漸進(jìn)式跨端,提供腳手架命令工具,規(guī)劃比較宏大
- Omi :基于Taro完成的框架,kbone有支持omi的模板
- Nanachi: 基于react的編譯期間的轉(zhuǎn)義框架
總結(jié)
沒(méi)有跨端需求,只需要微信小程序 ==> 小程序原生
web端轉(zhuǎn)小程序 or 兩端 or 想要嘗鮮 ==> kbone
多端 or Vue 技術(shù)棧 ==> uni-app
多端 or React 技術(shù)棧 ==> taro
不介意學(xué)習(xí)新技術(shù)了 ==> wepy 2.0 or chaemeleon
寫(xiě)在最后
小程序在非常快速的更新迭代,就算是原生框架也還是有一些坑的,因此沒(méi)有哪種框架是百分之百完美,需要根據(jù)業(yè)務(wù)具體需求以及自身技術(shù)棧偏好來(lái)進(jìn)行選擇。
如果是你,你會(huì)怎么選擇?歡迎你在留言區(qū)給出答案。