用WEB技術(shù)棧開發(fā)NATIVE應(yīng)用:WEEX SDK原理詳解

摘要: WEEX依舊采取傳統(tǒng)的web開發(fā)技術(shù)棧進(jìn)行開發(fā)黍特,同時app在終端的運行體驗不輸native app。其同時解決了開發(fā)效率衡奥、發(fā)版速度以及用戶體驗三個核心問題。那么WEEX是如何實現(xiàn)的苹祟?目前WEEX已經(jīng)完全開源,并捐給Apache基金會评雌,我們可以通過分析其源碼來一探究竟苔咪。

點此查看原文:https://yq.aliyun.com/articles/376633?spm=a2c41.11181499.0.0

作者:阿里-移動云-大前端團(tuán)隊

傳統(tǒng)的移動端開發(fā),一個完整的業(yè)務(wù)需要維護(hù)三份終端代碼:Android柳骄、iOS、H5箕般,這帶來了極大的開發(fā)成本以及維護(hù)成本耐薯。尤其是對處于業(yè)務(wù)初創(chuàng)期需要快速試錯的業(yè)務(wù)以及需要支持定期運營活動的業(yè)務(wù)。所以業(yè)界也一直在探索跨平臺方案丝里,旨在通過一套代碼完成各個終端的業(yè)務(wù)邏輯曲初。相關(guān)方案經(jīng)過不斷演化,從早期的H5杯聚、Hybrid到如今的Cloud Native(云原生)臼婆,在開發(fā)效率和用戶體驗上都在一點點逼近最初的設(shè)想。

早期H5和Hybrid方案的核心是利用終端的內(nèi)置瀏覽器(webview)功能幌绍,通過開發(fā)web應(yīng)用滿足跨平臺需求颁褂。該方案可以解決跨平臺問題,同時可以提升發(fā)版效率傀广。但其最大的弊端在于用戶體驗相較于native開發(fā)的app存在較大差距颁独,經(jīng)常出現(xiàn)頁面卡頓,加載慢等問題伪冰。

于是后來業(yè)界開始探索依舊利用web技術(shù)棧開發(fā)出媲美原生體驗app的方案誓酒,于是以WEEX為代表云原生開發(fā)框架開始出現(xiàn)。所謂云原生(Cloud Native)指可以通過云端快速發(fā)布(與遠(yuǎn)程web應(yīng)用發(fā)布流程類似)贮聂,同時還可以達(dá)到媲美原生App體驗的方案靠柑。WEEX依舊采取傳統(tǒng)的web開發(fā)技術(shù)棧進(jìn)行開發(fā)寨辩,同時app在終端的運行體驗不輸native app。其同時解決了開發(fā)效率歼冰、發(fā)版速度以及用戶體驗三個核心問題靡狞。那么WEEX是如何實現(xiàn)的?目前WEEX已經(jīng)完全開源停巷,并捐給Apache基金會耍攘,我們可以通過分析其源碼來一探究竟。

WEEX框架主要分為兩部分:

前端JavaScript框架?

Native SDK

本文主要探討Native SDK的核心原理畔勤,其前端JavaScript框架會在后續(xù)的文章中進(jìn)行介紹蕾各。

1 整體架構(gòu)

首先來看下WEEX開發(fā)的整體架構(gòu):

從上圖中可以看到weex的大致工作流程:

研發(fā)人員利用web技術(shù)棧開發(fā)weex file,打包成JS Bundle庆揪,然后部署到服務(wù)器上?

終端通過網(wǎng)絡(luò)獲取JS Bundle式曲,然后在本地執(zhí)行該JS Bundle

終端上提供了JS的執(zhí)行引擎(JSCore)用于執(zhí)行遠(yuǎn)程加載到JS Bundle

JS執(zhí)行引擎執(zhí)行JS Bundle,并將相關(guān)渲染指令以及其他需要利用native能力的指令通過JS-Native Bridge透出

JS-Native Bridge將渲染指令分發(fā)到native(Andorid缸榛、iOS)渲染引擎吝羞,由native渲染引擎完成最終的頁面渲染

看完上述整體架構(gòu)后,可以大致理解為何WEEX可以達(dá)到媲美原生的體驗内颗,因為其頁面渲染并不是像H5方案一樣接入瀏覽器的渲染能力钧排,而是原生渲染,所以本質(zhì)上渲染出來的頁面就是一個native頁面均澳。

接下來我們再來將端上的模塊進(jìn)行詳細(xì)的拆分:

如上圖所示恨溜,WEEX NATIVE SDK大致可以分為如下幾個層級:

JS執(zhí)行層:

JS執(zhí)行引擎:JSCore,解釋并執(zhí)行JS Bundle?

main.js:提供WEEX runtime找前,SDK初始化糟袁,JS Core會首先加載main.js,為js bundle提供weex runtime?

Bridge層:提供JS和Native的雙向通信能力?

Dom層:維護(hù)頁面Dom結(jié)構(gòu)?

Render層:完成頁面渲染?

native組件庫:本地UI組件庫,每一個組件對應(yīng)一個html標(biāo)簽躺盛,所以當(dāng)我們在weex開發(fā)過程中使用到的各種標(biāo)簽:div项戴、text、image等等槽惫,最終都被轉(zhuǎn)化成為了一個native的控件?

module manager周叮、module庫:功能模塊管理層?

WXSDKManger、WXSDKEngine:SDK全局環(huán)境維護(hù)?

WXSDKInstance:weex 實例躯枢,一個js bundle對應(yīng)一個weex實例

2 WEEX SDK初始化

有了上述大致架構(gòu)和功能劃分后则吟,我們以一個實際的例子來分析WEEX NATIVE SDK的運行邏輯。首先來看下WEEX SDK在初始化階段都做了哪些準(zhǔn)備工作锄蹂。

這里以Andorid代碼為例進(jìn)行分析:WEEX的初始化通常放在Application中氓仲,其初簡化的初始化邏輯入如下:

publicclassWXApplicationextendsApplication{@OverridepublicvoidonCreate() {super.onCreate();? ? ? ? initWeex();? ? ? ? ......? ? }privatevoidinitWeex() {// 自定義相關(guān)配置InitConfig config=newInitConfig.Builder()? ? ? ? ? ? ? ? .setImgAdapter(newImageAdapter())// 自定義圖片適配器.build();? ? ? ? WXSDKEngine.initialize(this,config);// register moduletry{? ? ? ? ? ? WXSDKEngine.registerModule("testmodule", TestModule.class);// 注冊自定義模塊WXSDKEngine.registerModule("event", WXEventModule.class);? ? ? ? ? ? WXSDKEngine.registerComponent("richtext", RichText.class);// 注冊自定義UI組件......? ? ? ? }catch(WXException e) {? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? }}

從代碼中可以看到,weex的初始化比較簡單,主要完成兩件事:

完成初始化配置:比如指定相關(guān)適配器敬扛,比如圖片請求適配器?

注冊自定義的UI組件和功能模塊

剩下的事情都交給WEEX SDK來完成了晰洒,那么接下來就來看下WEEX SDK都做了些什么?

具體代碼在WXSDKEngine.doInitInternal:

privatestaticvoiddoInitInternal(finalApplication application,finalInitConfig config){? ? WXEnvironment.sApplication = application;? ? WXEnvironment.JsFrameworkInit =false;? ? WXBridgeManager.getInstance().post(newRunnable() {@Overridepublicvoidrun() {longstart = System.currentTimeMillis();? ? ? ? WXSDKManager sm = WXSDKManager.getInstance();? ? ? ? sm.onSDKEngineInitialize();if(config !=null) {? ? ? ? ? sm.setInitConfig(config);if(config.getDebugAdapter()!=null){? ? ? ? ? ? config.getDebugAdapter().initDebug(application);? ? ? ? ? }? ? ? ? }? ? ? ? WXSoInstallMgrSdk.init(application,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? sm.getIWXSoLoaderAdapter(),? ? ? ? ? ? ? ? ? ? ? ? ? ? ? sm.getWXStatisticsListener());booleanisSoInitSuccess = WXSoInstallMgrSdk.initSo(V8_SO_NAME,1, config!=null?config.getUtAdapter():null);if(!isSoInitSuccess) {return;? ? ? ? }? ? ? ? sm.initScriptsFramework(config!=null?config.getFramework():null);? ? ? ? WXEnvironment.sSDKInitExecuteTime = System.currentTimeMillis() - start;? ? ? ? WXLogUtils.renderPerformanceLog("SDKInitExecuteTime", WXEnvironment.sSDKInitExecuteTime);? ? ? }? ? });? ? register();? }

這是WEEX SDK的初始化邏輯啥箭,其主要做了以下幾件事:

初始化WXBridge谍珊,同時啟動WXBridge線程,待接收指令急侥。WXBridge在Android的實現(xiàn)本質(zhì)上是一個基于HandlerThread的異步任務(wù)處理線程

initSo:加載so文件砌滞,即JS執(zhí)行引擎

initScriptsFramework:加載SDK中的main.js,完成weex runtime的初始化

register:注冊SDK自帶的UI組件和功能模塊

3 頁面渲染

WEEX SDK在完成了初始化之后,即可開始渲染頁面了坏怪。接下來我們以如下這JS代碼為例贝润,來介紹頁面的渲染邏輯:

JS代碼比較簡單,邏輯就不介紹了铝宵。接下來重點介紹打掘,當(dāng)終端獲取到如上圖右側(cè)的js bundle后,如何進(jìn)行加載鹏秋、渲染以及后續(xù)的相關(guān)邏輯執(zhí)行尊蚁。

3.1 weex實例創(chuàng)建

實際上當(dāng)WEEX SDK獲取到JS Bundle后,第一時間并不是立馬渲染頁面侣夷,而是先創(chuàng)建WEEX的實例:

這幅時序圖中有兩個主要邏輯:

創(chuàng)建createInstance:創(chuàng)建一個weex實例横朋,每一個JS bundle對應(yīng)一個實例,同時每一個實例都有一個instance id百拓。由于所有的js bundle都是放入到同一個JS執(zhí)行引擎中執(zhí)行叶撒,那么當(dāng)js執(zhí)行引擎通過WXBridge將相關(guān)渲染指令傳出的時候,需要通過instance id才能知道該指定要傳遞給哪個weex實例

execJs:在創(chuàng)建實例完成后耐版,接下來才是真正將js bundle交給js執(zhí)行引擎執(zhí)行

3.2 頁面渲染

在實例創(chuàng)建完成后,接下來就是頁面渲染了压汪。首先來看下頁面渲染的整體流程:

js bundle涉及dom操作的執(zhí)行都會被weex-vue-framework轉(zhuǎn)化成native dom api, 前端框架vue是基于virtual dom api,而weex的前端框架:weex-vue-framework的核心邏輯就是將vue的virtual-dom轉(zhuǎn)換成Native DOM API?

weex終端的執(zhí)行引擎在執(zhí)行到Native DOM API后粪牲,則會將其轉(zhuǎn)化為Platform API,說白了就是通過WXBridge將Native DOM API以約定的方式轉(zhuǎn)發(fā)給native渲染引擎,完成頁面渲染?

可以看到止剖,在js執(zhí)行引擎創(chuàng)建好weex實例后腺阳,會執(zhí)行對應(yīng)的JS Bundle,并在執(zhí)行到platform api的時候?qū)⑵渫ㄟ^wxbridge穿香,發(fā)送給DomManager亭引。相關(guān)代碼可參考:com.taobao.weex.bridge.WXBridge

3.2.1 createbody

一個頁面的DOM結(jié)構(gòu)最外層是body,所以創(chuàng)建頁面一開始就是createbody皮获,整個create body的過程大致可以分為以下幾個步驟:

WXBridge將create body指令發(fā)送給WXDom模塊焙蚓。WXDom是另一個異步線程,負(fù)責(zé)維護(hù)頁面的Dom樹

WXDom創(chuàng)建一個新的dom樹,同時創(chuàng)建body節(jié)點

WXDom將create body指令傳遞給WXRenderManager渲染引擎购公,渲染引擎主要完成如下幾件事:

初始化一個組件實例萌京,稱為mGodComponent?

generateComponentTree:由于一個WEEX頁面就是由多個UI組件(Component)構(gòu)成的一棵樹,所以渲染引擎會初始化組件樹?

創(chuàng)建view

3.3.2 addElement

創(chuàng)建完body后宏浩,需要在body中添加一個text組件知残,指向該操作的Native DOM API為addElement,其具體操作為:

WXDomManager:更新本地dom樹,添加text節(jié)點?

WXRenderManager:本地渲染引擎添加相關(guān)組件:

從已注冊的組件中找到text對應(yīng)的組件比庄,并實例化?

將初始化完成的text組件添加到body所對應(yīng)的view之上?

給text組件設(shè)定布局求妹、添加監(jiān)聽事件?

加入數(shù)據(jù)綁定?

在此一個帶有一個text標(biāo)簽的簡單頁面才算是渲染完成。值得一提的是佳窑,在WXRenderManager創(chuàng)建組件時制恍,需要在本地已注冊的組件中需要標(biāo)簽對應(yīng)的組件,此處標(biāo)簽對應(yīng)的組件為com.taobao.weex.ui.component.WXText华嘹,其本質(zhì)上是一個TextView吧趣。從這里可以發(fā)現(xiàn),其實我們在JS Bundle中指定的各種標(biāo)簽耙厚,其實都最終被轉(zhuǎn)化為了一個native的控件强挫。這也就是為什么用WEEX開發(fā)出來的app,本質(zhì)上還是一個Native App薛躬。

其他的對應(yīng)關(guān)系還有:

div 對應(yīng)WXDiv?

image 對應(yīng)WXImage?

list對應(yīng)WXListComponent?

a對應(yīng)WXA?

……?

4 總結(jié)?

通過前文的介紹俯渤,相信大家對WEEX有了一個初步的系統(tǒng)認(rèn)識。簡單來說型宝,WEEX放棄了傳統(tǒng)的Webview八匠,而是搭建了一個native化的瀏覽器,因為用native的方式實現(xiàn)了一個瀏覽器的大部分核心組成成分:

JS 執(zhí)行引擎?

渲染引擎?

DOM樹管理?

網(wǎng)絡(luò)請求趴酣,持久層存儲等等能力?

另外為了保證整個SDK的運行效率梨树,SDK維護(hù)了三個線程:

bridge線程:完成js到native之間的通信?

dom線程:完成dom結(jié)構(gòu)的構(gòu)建?

渲染線程:完成UI渲染,也就是UI線程

以上就是WEEX SDK的大致框架和核心邏輯岖寞,篇幅有限抡四,無法面面俱到,只是希望通過該文想大家展示W(wǎng)EEX基于WEB技術(shù)棧開發(fā)native app的原理仗谆。文章內(nèi)容如有偏頗指巡,歡迎大家指正。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末隶垮,一起剝皮案震驚了整個濱河市藻雪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌狸吞,老刑警劉巖勉耀,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件指煎,死亡現(xiàn)場離奇詭異,居然都是意外死亡瑰排,警方通過查閱死者的電腦和手機(jī)贯要,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來椭住,“玉大人崇渗,你說我怎么就攤上這事【┲#” “怎么了宅广?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長些举。 經(jīng)常有香客問我跟狱,道長,這世上最難降的妖魔是什么户魏? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任驶臊,我火速辦了婚禮,結(jié)果婚禮上叼丑,老公的妹妹穿的比我還像新娘关翎。我一直安慰自己,他們只是感情好鸠信,可當(dāng)我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布纵寝。 她就那樣靜靜地躺著,像睡著了一般星立。 火紅的嫁衣襯著肌膚如雪爽茴。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天绰垂,我揣著相機(jī)與錄音室奏,去河邊找鬼。 笑死劲装,一個胖子當(dāng)著我的面吹牛窍奋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播酱畅,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼江场!你這毒婦竟也來了纺酸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤址否,失蹤者是張志新(化名)和其女友劉穎餐蔬,沒想到半個月后碎紊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡樊诺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年仗考,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片词爬。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡秃嗜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出顿膨,到底是詐尸還是另有隱情锅锨,我是刑警寧澤,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布恋沃,位于F島的核電站必搞,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏囊咏。R本人自食惡果不足惜恕洲,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望梅割。 院中可真熱鬧霜第,春花似錦、人聲如沸炮捧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽咆课。三九已至末誓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間书蚪,已是汗流浹背喇澡。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留殊校,地道東北人晴玖。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像为流,于是被迫代替她去往敵國和親呕屎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內(nèi)容