傳統(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)頁面卡頓窖维,加載慢等問題。
在這里我還是要推薦下我自己建的web前端開發(fā)學(xué)習(xí)群:617327703妙痹,群里都是學(xué)web前端開發(fā)的铸史,如果你正在學(xué)習(xí)前端 ,小編歡迎你加入怯伊,今天分享的這個案例已經(jīng)上傳到群文件琳轿,大家都是軟件開發(fā)黨,不定期分享干貨(只有前端軟件開發(fā)相關(guān)的),包括我自己整理的一份2018最新的前端進(jìn)階資料和高級開發(fā)教程崭篡,歡迎進(jìn)階中和進(jì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框架原理。
1 整體架構(gòu)
首先還是再來看下WEEX開發(fā)的整體架構(gòu):
可以看到在JS-Native Bridge
將渲染指令發(fā)送給Android或者iOS渲染引擎之前杀怠,我們的業(yè)務(wù)代碼運行在JSCore/v8
的執(zhí)行引擎之中椰憋,而在該執(zhí)行引擎之中除了執(zhí)行業(yè)務(wù)JSBundle,還運行著JS Framework赔退,JS Framework
不僅提供了jsbundle必要的運行時環(huán)境橙依,同時還提供了與native通信的接口。
而這個JS Framework就是我們今天介紹的重點硕旗。
這是前端框架的主要架構(gòu):
FRONTEND FRAMEWORK/DSL:這是WEEX的開發(fā)框架窗骑,目前WEEX主要是使用Vue.js進(jìn)行開發(fā)
WEEX-VUE-LOADER:前端編譯器,將vue文件編譯成es5代碼
WEEX-VUE-FRAMWORK:WEEX核心框架漆枚,主要負(fù)責(zé)將virtual dom轉(zhuǎn)換成weex的native dom api
WEEX-RUNTIME:負(fù)責(zé)與native渲染引擎對接创译,將native dom api轉(zhuǎn)換成對應(yīng)平臺(Android、iOS)的platform api,然后傳遞給native渲染引擎墙基,由后者負(fù)責(zé)渲染工作软族。
2?Vue.js
首先來看下前端開發(fā)框架Vue.js,?Vue.js目前與React、?Angular?構(gòu)成了三大最流行的前端開發(fā)框架碘橘,Vue.js具有組件化互订、virtual dom以及MVVM三大特性,還學(xué)習(xí)React的Redux痘拆,引入了狀態(tài)管理模塊Vuex仰禽。同時相比起React
主要基于JSX,Vue.js的開發(fā)模式更加清晰,簡單吐葵,上手速度更快规揪。.vue文件通常可以分為三部分:?温峭、
和?猛铅,是必須要有的,其他可選凤藏。其中?中的代碼會保留或者被轉(zhuǎn)換成 ES5 的語法奸忽;
中的 CSS 在 Weex 平臺上會被轉(zhuǎn)換成 JSON 格式的樣式聲明,放到組件的定義中去揖庄;會被編譯生成組件定義中 render 函數(shù)栗菜,可以理解為 render 函數(shù)的語法糖。下文是一個簡單的.vue
文件:
3?WEEX-VUE-LOADER
由于.vue文件并不是標(biāo)準(zhǔn)的JavaScript代碼蹄梢,該代碼不能直接被JS執(zhí)行引擎識別疙筹,所以在編譯過程中需要將.vue
文件轉(zhuǎn)化成標(biāo)準(zhǔn)的es5代碼。而負(fù)責(zé)完成這一操作的就是WEEX-VUE-LOADER禁炒。
4?WEEX-VUE-FRAMEWORK?&?WEEX-RUNTIME
完成編譯后而咆,輸出的bundle.js即可被JS執(zhí)行引擎所識別,可以執(zhí)行其邏輯了幕袱。那么接下來就來看下bundle.js的執(zhí)行過程暴备。
4.1 初始化
首先來看下初始化過程。
圖中畫出了 Weex SDK 的部分內(nèi)容凹蜂。其中?weex-vue-framework和?Vue.js是對等的馍驯,語法和內(nèi)部機(jī)制都是一樣的,只不過?Vue.js?最終創(chuàng)建的是 DOM 元素玛痊,而?weex-vue-framework則是向原生端發(fā)送渲染指令汰瘫,最終渲染生成的是原生組件。Weex Runtime 用來對接上層前端框架( Vue.js )并且負(fù)責(zé)和原生端之間的通信擂煞。Render Engine 就是針對各個端開發(fā)的原生渲染器混弥,包含了 Weex 內(nèi)置組件和模塊的實現(xiàn),可擴(kuò)展对省。
4.2 創(chuàng)建組件
weex接收到bundle.js之后蝗拿,首先檢查其合法性,如果發(fā)現(xiàn)用的是Vue版本(weex早期版本使用.we語法)蒿涎,就會調(diào)用weex-vue-framework中提供的createInstance創(chuàng)建實例哀托。一個bundle.js對應(yīng)一個weex實例,且每一個實例都有唯一的instanceId劳秋,與之對應(yīng)仓手。實例與實例之間相互隔離胖齐,互不干擾。
在創(chuàng)建實例的過程中嗽冒,bundle.js會執(zhí)行new Vue()創(chuàng)建一個vue組件呀伙,并通過其render
函數(shù)創(chuàng)建VNode節(jié)點,即virtual dom節(jié)點添坊。第二節(jié)中的示例代碼會最終生成類似如下的vnode節(jié)點:
4.3 patch
生成了VNode之后剿另,接下來需要將VNode同步到真實的Dom之上,該過程在Vue.js
中被稱為patch贬蛙,patch會比較新舊VNode之間的差異雨女,最小化操作集。最后再將Virual Dom整體更新到真實Dom之上速客。在執(zhí)行 patch 之前的過程都是 Web 和 Weex 通用的戚篙,所以文件格式五鲫、打包編譯過程溺职、模板指令、組件的生命周期位喂、數(shù)據(jù)綁定等上層語法都是一致的浪耘。
然而由于目標(biāo)執(zhí)行環(huán)境不同(瀏覽器和 Weex 容器),在渲染真實 UI 的時候調(diào)用的接口也不同塑崖。
在vue.js內(nèi)部七冲,weex平臺和web平臺的patch方法有所不同。簡單來講规婆,在web平臺澜躺,patch需要將Virtual Dom利用標(biāo)準(zhǔn)Dom API更新到真實Dom即可。接下來的UI更新就交給瀏覽器即可抒蚜。
但是由于在weex平臺下掘鄙,最終的UI渲染是由native渲染引擎執(zhí)行的,native渲染引擎不能識別Dom API嗡髓,所以在weex平臺下操漠,需要將Virtual DOM轉(zhuǎn)化成native 渲染引擎可以識別的Native DOM API。
4.4 發(fā)送渲染指令
在上層前端框架調(diào)用了 Weex 平臺提供的 Native DOM API 之后饿这,Weex Runtime 會構(gòu)建一個用于渲染的節(jié)點樹浊伙,并將操作轉(zhuǎn)換成渲染指令發(fā)送給客戶端。
回顧文中提到的 例子长捧,上層框架調(diào)用了 Weex Runtime 中?createBody嚣鄙、createElement、appendChild這三個接口串结,簡單構(gòu)建了一個用于渲染的節(jié)點樹哑子,最終生成了兩條渲染指令廓八。
Platform API 指的是原生環(huán)境提供的 API,這些 API 是 Weex SDK 中原生模塊提供的赵抢,不是 js 中方法剧蹂,也不是瀏覽器中的接口,是 Weex 內(nèi)部不同模塊之間的約定烦却。
目前來說渲染指令是基于 JSON 描述的宠叼,具體格式大致如下所示:
4.5 渲染原生組件
接下來就是WEEX NATIVE SDK的工作了,具體邏輯在上一篇博客已經(jīng)詳細(xì)說明了其爵,此處僅簡單說明冒冬。
原生渲染器接收上層傳來的渲染指令,并且逐步將其渲染成原生組件摩渺。
渲染指令分很多類简烤,文章中提到的兩個都是用來創(chuàng)建節(jié)點的,其他還有?moveElement?摇幻、updateAttrs?横侦、addEvent等各種指令。原生渲染器先是解析渲染指令的描述绰姻,然后分發(fā)給不同的模塊枉侧。關(guān)于 UI 繪制的指令都屬于?"dom"模塊中,在 SDK 內(nèi)部有組件的實現(xiàn)狂芋,其他還有一些無界面的功能模塊榨馁,如 stream 、navigator 等模塊帜矾,也可以通過發(fā)送指令的方式調(diào)用翼虫。
這個例子里,第一個?createBody的指令就創(chuàng)建了一個?
上述過程不是分階段一個一個執(zhí)行的次慢,而是可以實現(xiàn)“流式”渲染的,有可能第一個?
的渲染指令又發(fā)過來了迫像。當(dāng)一個頁面特別大時,能看到一塊一塊的內(nèi)容逐漸渲染出來的過程瞳遍。
5 總結(jié)
還有很多實現(xiàn)細(xì)節(jié)文中并未展開闻妓,有興趣的朋友可以留言大家一起討論下,另外文章中有錯誤的地方也請大家指正掠械!