用WEB技术栈开发NATIVE应用(二):WEEX 前端SDK原理详解
生活随笔
收集整理的這篇文章主要介紹了
用WEB技术栈开发NATIVE应用(二):WEEX 前端SDK原理详解
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
摘要: WEEX依舊采取傳統的web開發技術棧進行開發,同時app在終端的運行體驗不輸native app。其同時解決了開發效率、發版速度以及用戶體驗三個核心問題。那么WEEX是如何實現的?目前WEEX已經完全開源,并捐給Apache基金會,我們可以通過分析其源碼來一探究竟。
點此查看原文:http://click.aliyun.com/m/43048/
作者:阿里-移動云-大前端團隊
傳統的移動端開發,一個完整的業務需要維護三份終端代碼:Android、iOS、H5,這帶來了極大的開發成本以及維護成本。尤其是對處于業務初創期需要快速試錯的業務以及需要支持定期運營活動的業務。所以業界也一直在探索跨平臺方案,旨在通過一套代碼完成各個終端的業務邏輯。相關方案經過不斷演化,從早期的H5、Hybrid到如今的Cloud Native(云原生),在開發效率和用戶體驗上都在一點點逼近最初的設想。 早期H5和Hybrid方案的核心是利用終端的內置瀏覽器(webview)功能,通過開發web應用滿足跨平臺需求。該方案可以解決跨平臺問題,同時可以提升發版效率。但其最大的弊端在于用戶體驗相較于native開發的app存在較大差距,經常出現頁面卡頓,加載慢等問題。
于是后來業界開始探索依舊利用web技術棧開發出媲美原生體驗app的方案,于是以WEEX為代表云原生開發框架開始出現。所謂云原生(Cloud Native)指可以通過云端快速發布(與遠程web應用發布流程類似),同時還可以達到媲美原生App體驗的方案。WEEX依舊采取傳統的web開發技術棧進行開發,同時app在終端的運行體驗不輸native app。其同時解決了開發效率、發版速度以及用戶體驗三個核心問題。那么WEEX是如何實現的?目前WEEX已經完全開源,并捐給Apache基金會,我們可以通過分析其源碼來一探究竟。
WEEX框架主要分為兩部分: 1.前端JavaScript框架 2.Native SDK 在上一篇博客中,我們介紹了Native SDK的原理,本文主要介紹其前端JavaScript框架原理。
1 整體架構
首先還是再來看下WEEX開發的整體架構: 可以看到在JS-Native Bridge將渲染指令發送給Android或者iOS渲染引擎之前,我們的業務代碼運行在JSCore/v8的執行引擎之中,而在該執行引擎之中除了執行業務JSBundle,還運行著JS Framework,JS Framework不僅提供了jsbundle必要的運行時環境,同時還提供了與native通信的接口。 而這個JS Framework就是我們今天介紹的重點。 這是前端框架的主要架構: FRONTEND FRAMEWORK/DSL:這是WEEX的開發框架,目前WEEX主要是使用Vue.js進行開發 WEEX-VUE-LOADER:前端編譯器,將vue文件編譯成es5代碼 WEEX-VUE-FRAMWORK:WEEX核心框架,主要負責將virtual dom轉換成weex的native dom api WEEX-RUNTIME:負責與native渲染引擎對接,將native dom api轉換成對應平臺(Android、iOS)的platform api,然后傳遞給native渲染引擎,由后者負責渲染工作。
2 Vue.js
首先來看下前端開發框架Vue.js,Vue.js目前與React 、 Angular構成了三大最流行的前端開發框架,Vue.js具有組件化、virtual dom以及MVVM三大特性,還學習React的Redux,引入了狀態管理模塊Vuex。同時相比起React主要基于JSX,Vue.js的開發模式更加清晰,簡單,上手速度更快。.vue 文件通??梢苑譃槿糠?#xff1a;<template> 、<style> 和 <script>,<template> 是必須要有的,其他可選。其中 <script>中的代碼會保留或者被轉換成 ES5 的語法;<style> 中的 CSS 在 Weex 平臺上會被轉換成 JSON 格式的樣式聲明,放到組件的定義中去;<template> 會被編譯生成組件定義中 render 函數,可以理解為 render 函數的語法糖。下文是一個簡單的.vue文件: <template> <div style="justify-content:center;"> <text class="freestyle">Hello World!</text> </div></template><style scoped> .freestyle { text-align: center; font-size: 200px; }</style>
3 WEEX-VUE-LOADER
由于.vue文件并不是標準的JavaScript代碼,該代碼不能直接被JS執行引擎識別,所以在編譯過程中需要將.vue文件轉化成標準的es5代碼。而負責完成這一操作的就是WEEX-VUE-LOADER。
4 WEEX-VUE-FRAMEWORK&WEEX-RUNTIME
完成編譯后,輸出的bundle.js即可被JS執行引擎所識別,可以執行其邏輯了。那么接下來就來看下bundle.js的執行過程。
4.1 初始化
首先來看下初始化過程。 圖中畫出了 Weex SDK 的部分內容。其中 weex-vue-framework 和 Vue.js 是對等的,語法和內部機制都是一樣的,只不過 Vue.js最終創建的是 DOM 元素,而 weex-vue-framework 則是向原生端發送渲染指令,最終渲染生成的是原生組件。Weex Runtime 用來對接上層前端框架( Vue.js )并且負責和原生端之間的通信。Render Engine 就是針對各個端開發的原生渲染器,包含了 Weex 內置組件和模塊的實現,可擴展。
4.2 創建組件
weex接收到bundle.js之后,首先檢查其合法性,如果發現用的是Vue版本(weex早期版本使用.we語法),就會調用weex-vue-framework中提供的createInstance創建實例。一個bundle.js對應一個weex實例,且每一個實例都有唯一的instanceId,與之對應。實例與實例之間相互隔離,互不干擾。 在創建實例的過程中,bundle.js會執行new Vue()創建一個vue組件,并通過其render函數創建VNode節點,即virtual dom節點。第二節中的示例代碼會最終生成類似如下的vnode節點: { tag: 'div', data: { staticStyle: { justifyContent: 'center' } }, children: [{ tag: 'text', data: { staticClass: 'freestyle' }, context: { $options: { style: { freestyle: { textAlign: 'center', fontSize: 200 } } } }, children: [{ tag: '', text: 'Hello World!' }] }]}
4.3 patch
生成了VNode之后,接下來需要將VNode同步到真實的Dom之上,該過程在Vue.js中被稱為patch,patch會比較新舊VNode之間的差異,最小化操作集。最后再將Virual Dom整體更新到真實Dom之上。在執行 patch 之前的過程都是 Web 和 Weex 通用的,所以文件格式、打包編譯過程、模板指令、組件的生命周期、數據綁定等上層語法都是一致的。
然而由于目標執行環境不同(瀏覽器和 Weex 容器),在渲染真實 UI 的時候調用的接口也不同。 在vue.js內部,weex平臺和web平臺的patch方法有所不同。簡單來講,在web平臺,patch需要將Virtual Dom利用標準Dom API更新到真實Dom即可。接下來的UI更新就交給瀏覽器即可。
但是由于在weex平臺下,最終的UI渲染是由native渲染引擎執行的,native渲染引擎不能識別Dom API,所以在weex平臺下,需要將Virtual DOM轉化成native 渲染引擎可以識別的Native DOM API。
4.3 發送渲染指令
在上層前端框架調用了 Weex 平臺提供的 Native DOM API 之后,Weex Runtime 會構建一個用于渲染的節點樹,并將操作轉換成渲染指令發送給客戶端。
回顧文中提到的 例子,上層框架調用了 Weex Runtime 中 createBody 、createElement 、appendChild 這三個接口,簡單構建了一個用于渲染的節點樹,最終生成了兩條渲染指令。
Platform API 指的是原生環境提供的 API,這些 API 是 Weex SDK 中原生模塊提供的,不是 js 中方法,也不是瀏覽器中的接口,是 Weex 內部不同模塊之間的約定。
目前來說渲染指令是基于 JSON 描述的,具體格式大致如下所示: { module: 'dom', method: 'createBody', args: [{ ref: '_root', type: 'div', style: { justifyContent: 'center' } }]}
{ module: 'dom', method: 'addElement', args: ['_root', { ref: '2', type: 'text', attr: { value: 'Hello World!' }, style: { textAlign: 'center', fontSize: 200 } }]}
4.4 渲染原生組件
接下來就是WEEX NATIVE SDK的工作了,具體邏輯在上一篇博客已經詳細說明了,此處僅簡單說明。 原生渲染器接收上層傳來的渲染指令,并且逐步將其渲染成原生組件。
渲染指令分很多類,文章中提到的兩個都是用來創建節點的,其他還有 moveElement 、updateAttrs 、addEvent 等各種指令。原生渲染器先是解析渲染指令的描述,然后分發給不同的模塊。關于 UI 繪制的指令都屬于 "dom" 模塊中,在 SDK 內部有組件的實現,其他還有一些無界面的功能模塊,如 stream 、navigator 等模塊,也可以通過發送指令的方式調用。
這個例子里,第一個 createBody 的指令就創建了一個 <div> 的原生組件,同時也將樣式應用到了改組件上。第二個 addElement指令向 <div> 中添加一個 <text> 組件,同時也聲明了組件的樣式和屬性值。 上述過程不是分階段一個一個執行的,而是可以實現“流式”渲染的,有可能第一個 <div> 的原生組件還沒渲染好,<text> 的渲染指令又發過來了。當一個頁面特別大時,能看到一塊一塊的內容逐漸渲染出來的過程。
5 總結
通過本文以及上一篇:WEEX NATIVE SDK原理詳解,總算是將weex的大致原理介紹清楚了,當然還有很多實現細節文中并未展開,有興趣的朋友可以留言大家一起討論下,另外文章中有錯誤的地方也請大家指正!
識別以下二維碼,干貨
點此查看原文:http://click.aliyun.com/m/43048/
作者:阿里-移動云-大前端團隊
傳統的移動端開發,一個完整的業務需要維護三份終端代碼:Android、iOS、H5,這帶來了極大的開發成本以及維護成本。尤其是對處于業務初創期需要快速試錯的業務以及需要支持定期運營活動的業務。所以業界也一直在探索跨平臺方案,旨在通過一套代碼完成各個終端的業務邏輯。相關方案經過不斷演化,從早期的H5、Hybrid到如今的Cloud Native(云原生),在開發效率和用戶體驗上都在一點點逼近最初的設想。 早期H5和Hybrid方案的核心是利用終端的內置瀏覽器(webview)功能,通過開發web應用滿足跨平臺需求。該方案可以解決跨平臺問題,同時可以提升發版效率。但其最大的弊端在于用戶體驗相較于native開發的app存在較大差距,經常出現頁面卡頓,加載慢等問題。
于是后來業界開始探索依舊利用web技術棧開發出媲美原生體驗app的方案,于是以WEEX為代表云原生開發框架開始出現。所謂云原生(Cloud Native)指可以通過云端快速發布(與遠程web應用發布流程類似),同時還可以達到媲美原生App體驗的方案。WEEX依舊采取傳統的web開發技術棧進行開發,同時app在終端的運行體驗不輸native app。其同時解決了開發效率、發版速度以及用戶體驗三個核心問題。那么WEEX是如何實現的?目前WEEX已經完全開源,并捐給Apache基金會,我們可以通過分析其源碼來一探究竟。
WEEX框架主要分為兩部分: 1.前端JavaScript框架 2.Native SDK 在上一篇博客中,我們介紹了Native SDK的原理,本文主要介紹其前端JavaScript框架原理。
1 整體架構
首先還是再來看下WEEX開發的整體架構: 可以看到在JS-Native Bridge將渲染指令發送給Android或者iOS渲染引擎之前,我們的業務代碼運行在JSCore/v8的執行引擎之中,而在該執行引擎之中除了執行業務JSBundle,還運行著JS Framework,JS Framework不僅提供了jsbundle必要的運行時環境,同時還提供了與native通信的接口。 而這個JS Framework就是我們今天介紹的重點。 這是前端框架的主要架構: FRONTEND FRAMEWORK/DSL:這是WEEX的開發框架,目前WEEX主要是使用Vue.js進行開發 WEEX-VUE-LOADER:前端編譯器,將vue文件編譯成es5代碼 WEEX-VUE-FRAMWORK:WEEX核心框架,主要負責將virtual dom轉換成weex的native dom api WEEX-RUNTIME:負責與native渲染引擎對接,將native dom api轉換成對應平臺(Android、iOS)的platform api,然后傳遞給native渲染引擎,由后者負責渲染工作。
2 Vue.js
首先來看下前端開發框架Vue.js,Vue.js目前與React 、 Angular構成了三大最流行的前端開發框架,Vue.js具有組件化、virtual dom以及MVVM三大特性,還學習React的Redux,引入了狀態管理模塊Vuex。同時相比起React主要基于JSX,Vue.js的開發模式更加清晰,簡單,上手速度更快。.vue 文件通??梢苑譃槿糠?#xff1a;<template> 、<style> 和 <script>,<template> 是必須要有的,其他可選。其中 <script>中的代碼會保留或者被轉換成 ES5 的語法;<style> 中的 CSS 在 Weex 平臺上會被轉換成 JSON 格式的樣式聲明,放到組件的定義中去;<template> 會被編譯生成組件定義中 render 函數,可以理解為 render 函數的語法糖。下文是一個簡單的.vue文件: <template> <div style="justify-content:center;"> <text class="freestyle">Hello World!</text> </div></template><style scoped> .freestyle { text-align: center; font-size: 200px; }</style>
3 WEEX-VUE-LOADER
由于.vue文件并不是標準的JavaScript代碼,該代碼不能直接被JS執行引擎識別,所以在編譯過程中需要將.vue文件轉化成標準的es5代碼。而負責完成這一操作的就是WEEX-VUE-LOADER。
4 WEEX-VUE-FRAMEWORK&WEEX-RUNTIME
完成編譯后,輸出的bundle.js即可被JS執行引擎所識別,可以執行其邏輯了。那么接下來就來看下bundle.js的執行過程。
4.1 初始化
首先來看下初始化過程。 圖中畫出了 Weex SDK 的部分內容。其中 weex-vue-framework 和 Vue.js 是對等的,語法和內部機制都是一樣的,只不過 Vue.js最終創建的是 DOM 元素,而 weex-vue-framework 則是向原生端發送渲染指令,最終渲染生成的是原生組件。Weex Runtime 用來對接上層前端框架( Vue.js )并且負責和原生端之間的通信。Render Engine 就是針對各個端開發的原生渲染器,包含了 Weex 內置組件和模塊的實現,可擴展。
4.2 創建組件
weex接收到bundle.js之后,首先檢查其合法性,如果發現用的是Vue版本(weex早期版本使用.we語法),就會調用weex-vue-framework中提供的createInstance創建實例。一個bundle.js對應一個weex實例,且每一個實例都有唯一的instanceId,與之對應。實例與實例之間相互隔離,互不干擾。 在創建實例的過程中,bundle.js會執行new Vue()創建一個vue組件,并通過其render函數創建VNode節點,即virtual dom節點。第二節中的示例代碼會最終生成類似如下的vnode節點: { tag: 'div', data: { staticStyle: { justifyContent: 'center' } }, children: [{ tag: 'text', data: { staticClass: 'freestyle' }, context: { $options: { style: { freestyle: { textAlign: 'center', fontSize: 200 } } } }, children: [{ tag: '', text: 'Hello World!' }] }]}
4.3 patch
生成了VNode之后,接下來需要將VNode同步到真實的Dom之上,該過程在Vue.js中被稱為patch,patch會比較新舊VNode之間的差異,最小化操作集。最后再將Virual Dom整體更新到真實Dom之上。在執行 patch 之前的過程都是 Web 和 Weex 通用的,所以文件格式、打包編譯過程、模板指令、組件的生命周期、數據綁定等上層語法都是一致的。
然而由于目標執行環境不同(瀏覽器和 Weex 容器),在渲染真實 UI 的時候調用的接口也不同。 在vue.js內部,weex平臺和web平臺的patch方法有所不同。簡單來講,在web平臺,patch需要將Virtual Dom利用標準Dom API更新到真實Dom即可。接下來的UI更新就交給瀏覽器即可。
但是由于在weex平臺下,最終的UI渲染是由native渲染引擎執行的,native渲染引擎不能識別Dom API,所以在weex平臺下,需要將Virtual DOM轉化成native 渲染引擎可以識別的Native DOM API。
4.3 發送渲染指令
在上層前端框架調用了 Weex 平臺提供的 Native DOM API 之后,Weex Runtime 會構建一個用于渲染的節點樹,并將操作轉換成渲染指令發送給客戶端。
回顧文中提到的 例子,上層框架調用了 Weex Runtime 中 createBody 、createElement 、appendChild 這三個接口,簡單構建了一個用于渲染的節點樹,最終生成了兩條渲染指令。
Platform API 指的是原生環境提供的 API,這些 API 是 Weex SDK 中原生模塊提供的,不是 js 中方法,也不是瀏覽器中的接口,是 Weex 內部不同模塊之間的約定。
目前來說渲染指令是基于 JSON 描述的,具體格式大致如下所示: { module: 'dom', method: 'createBody', args: [{ ref: '_root', type: 'div', style: { justifyContent: 'center' } }]}
{ module: 'dom', method: 'addElement', args: ['_root', { ref: '2', type: 'text', attr: { value: 'Hello World!' }, style: { textAlign: 'center', fontSize: 200 } }]}
4.4 渲染原生組件
接下來就是WEEX NATIVE SDK的工作了,具體邏輯在上一篇博客已經詳細說明了,此處僅簡單說明。 原生渲染器接收上層傳來的渲染指令,并且逐步將其渲染成原生組件。
渲染指令分很多類,文章中提到的兩個都是用來創建節點的,其他還有 moveElement 、updateAttrs 、addEvent 等各種指令。原生渲染器先是解析渲染指令的描述,然后分發給不同的模塊。關于 UI 繪制的指令都屬于 "dom" 模塊中,在 SDK 內部有組件的實現,其他還有一些無界面的功能模塊,如 stream 、navigator 等模塊,也可以通過發送指令的方式調用。
這個例子里,第一個 createBody 的指令就創建了一個 <div> 的原生組件,同時也將樣式應用到了改組件上。第二個 addElement指令向 <div> 中添加一個 <text> 組件,同時也聲明了組件的樣式和屬性值。 上述過程不是分階段一個一個執行的,而是可以實現“流式”渲染的,有可能第一個 <div> 的原生組件還沒渲染好,<text> 的渲染指令又發過來了。當一個頁面特別大時,能看到一塊一塊的內容逐漸渲染出來的過程。
5 總結
通過本文以及上一篇:WEEX NATIVE SDK原理詳解,總算是將weex的大致原理介紹清楚了,當然還有很多實現細節文中并未展開,有興趣的朋友可以留言大家一起討論下,另外文章中有錯誤的地方也請大家指正!
識別以下二維碼,干貨
總結
以上是生活随笔為你收集整理的用WEB技术栈开发NATIVE应用(二):WEEX 前端SDK原理详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深度学习的关键术语
- 下一篇: 注释那些事儿:前端代码质量系列文章(一)