畫圖來說一下 Weex 核心原理

背景

從前,若是咱們打算實現某個需求,一般須要三種程序員(IOS, 安卓,前端)寫三份代碼。這就帶來了很是大的開發成本,因此業界也一直在探索跨平臺方案——從最先的H5, Hybrid 到如今的weex, React Native。這些方案的本質目的都是,一套代碼,多端運行。javascript

H5和Hybrid的發展

早期H5和Hybrid方案的本質是,利用客戶端App的內置瀏覽器(也就是webview)功能,經過開發前端的H5頁面知足跨平臺需求。css

該方案提高開發效率,同時也知足了跨端的需求。但有一個問題就是,前端H5的性能和客戶端的性能相差甚遠。html

weex的發展

因而後來, 業界繼續探索能夠媲美原生體驗app的方案,好比說WEEX 。前端

WEEX依舊採起前端H5頁面進行開發,同時app在終端的運行體驗不輸native app。便可以保證快速響應需求,又能夠保證用戶體驗。vue

那麼WEEX是如何實現的?java

本質來講,WEEX是用客戶端Native 的能力,去作了部分瀏覽器(webview)的工做。node

在2016年2月, WeexSDK 發佈了v0.10.0版本,在這個版本里面,集成了v2 版本的Vue。程序員

爲啥是Vue 2.x 版本呢?web

Vue 2.x加入了 Virtual-DOM 和預編譯器的設計,使得該框架在運行時可以脫離 HTML 和 CSS 解析,只依賴 JavaScript;同時 Virtual-DOM 也使得 Vue 2.x 渲染成原生 UI 成爲了可能。算法

weex 原理探究

咱們先來看一下 weex 的總體框架。

weex 總體架構

從上圖中能夠看到weex的大體工做流程:

  1. 前端開發能夠寫熟悉vue語法的單文件,而後打包成出來一份dist —— JS Bundle,而後部署到服務器上
  2. 客戶端打開某一個頁面,經過網絡下載JS Bundle,而後在客戶端本地執行該JS Bundle
  3. 客戶端提供了JS的執行引擎(JSCore)用於執行遠程加載到JS Bundle
  4. JS執行引擎執行JS Bundle,和瀏覽器的過程相似,JS Bundle 的代碼被執行,生成VNode 樹進行patch,找出最小操做DOM節點的操做,把對DOM節點的操做轉變爲Native DOM API, 調用WXBridge 進行通訊
  5. WXBridge將渲染指令分發到native(Andorid、iOS)渲染引擎,由native渲染引擎完成最終的頁面渲染

看完上述總體流程後,能夠大體理解爲什麼WEEX能夠達到媲美原生的體驗,由於其頁面渲染並非像H5方案同樣使用瀏覽器的渲染能力,而是原生渲染,因此本質上渲染出來的頁面就是一個native頁面。

下面會具體的介紹下面幾個過程:

第一步: 生成 JS bundle

JS bundle 是前端同窗寫好代碼後打包出來的dist.

前端同窗能夠在.vue 的單文件中,寫<template>,<style><script>標籤,而後把這些標籤轉換爲JS Bundle用於部署在服務端,以後客戶端會去請求這些JS Bundle

好比說,下圖中左邊是vue源代碼,右邊是打包出來到JS Bundle

熟悉Vue 原理的同窗清楚,上面右邊其實就是Vue打包生成的render 函數。

JS代碼比較簡單,邏輯就不介紹了。接下來重點介紹,當客戶端獲取到如上圖右側的js bundle後,如何進行加載、渲染以及後續的相關邏輯執行。

第二步: WEEX SDK初始化

weex在真正打開一個頁面以前,會先作一些準備的初始化工做,這一點有一點像微信小程序。在初始化階段,WEEX SDK 會初始化好下面幾樣東西:

  • 初始化 js的執行環境——js Core 或者是 v8
  • 加載weex-vue-framework 的代碼
  • 初始化WXBridge

以下圖所示:

js的執行環境

在初始化階段, WEEX SDK 會準備好一個js的執行環境。由於咱們是要在客戶端跑js 代碼的,因此須要一個js執行環境,這個執行環境相似於瀏覽器的v8 引擎, 在IOS 上,則是客戶端自帶的 js core

這個js執行環境,能夠當作是一個在客戶端上的沙盒,或者是一個虛擬機。

爲了提高性能,js 執行環境只用在初始化的時候初始化一次,以後每一個頁面都無須再初始化了。也就是說無論客戶端打開多少個weex頁面,多個頁面的 JS 都是跑在同一個js執行環境中的。

weex-vue-famework 框架

weex-vue-framework 框架 是什麼呢?

你能夠把 weex-vue-framework 框架當成被改造的Vue.js。語法和內部機制都是同樣的,只不過Vue.js最終建立的是 DOM 元素,而weex-vue-framework則是向原生端發送渲染指令,最終渲染生成的是原生組件。

同時,Weex爲了提升Native的極致性能,作了不少優化的工做。前端優化性能時,會把業務代碼和 vue.js 這類的依賴包分開打包,一個份是業務代碼,一份是打包的框架依賴。

weex 把weex-vue-framework 這類框架依賴內置到了SDK中,客戶端訪問Weex頁面時,只會網絡請求JS Bundle。因爲JSFramework在本地,因此就減小了JS Bundle的體積,每一個JS Bundle均可以減小一部分體積,從而提高了性能。

WXBridge 通訊

WXBridge 是 weex 實現的一種 js 和 客戶端通訊的機制。

js 執行環境和客戶端是隔離的,爲了和外界客戶端的世界通訊,須要有一個通訊的橋樑。weex 實現了 WXBrigde, 主要經過 callJScallNative 兩個核心的方法,實現 js 代碼和客戶端代碼雙向通訊。

在完成了上面的初始化以後,weex已經作好了準備,只等着下載 JS bundle 就可開始渲染頁面了。

第三步:建立 weex 實例

實際上當WEEX SDK獲取到JS Bundle後,第一時間並非立馬渲染頁面,而是先建立WEEX的實例。

每個JS bundle對應一個實例,同時每個實例都有一個instance id

咱們上文中說過,因爲全部的js bundle都是放入到同一個JS執行引擎中執行,那麼當js執行引擎經過WXBridge將相關渲染指令傳出的時候,須要經過instance id才能知道該指定要傳遞給哪一個weex實例

在建立實例完成後,接下來纔是真正將js bundle交給js執行引擎執行。

第四步 執行 JS bundle

在實例建立完成後,接下來就是執行JS bundle 了。JS bundle 的結果是生成Virtual DOM ,而後去patch 新舊 Vnode 樹,根據diff 算法找出最佳的DOM操做,惟一和瀏覽器不一樣的是,調用的是 Native app api ,而不是瀏覽器裏面對DOM節點增刪改查的操做。

VNode

bundle.js會執行new Vue()建立一個vue組件,並經過其render函數建立VNode節點,即virtual dom節點 , 像下面這樣:

{
  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!'
    }]
  }]
}
複製代碼

patch

生成了VNode以後,接下來須要將VNode同步到真實的Dom之上,該過程在Vue.js中被稱爲patchpatch會比較新舊VNode之間的差別,最小化操做集。最後再將Virual Dom總體更新到真實Dom之上。

在執行 patch 以前的過程都是 Web 和 Weex 通用的,後面的流程就不同了,由於客戶端沒有對 DOM 增刪改查的API,因此這些更新的操做,須要通過weex-vue-framework的處理,通通映射爲客戶端的Native DOM API

第五步 發送渲染指令

weex終端的執行引擎在執行到Native DOM API後,WXBridgeNative DOM API轉化爲Platform API

Platform API 是 Weex SDK 中原生模塊提供的,不是 js 中方法,也不是瀏覽器中的接口,是 Weex 封裝的一系列方法。

客戶端 和 前端h5的不一樣

本人是一枚前端同窗,不太瞭解客戶端的頁面是如何寫出來的,問了客戶端的大佬後,瞭解以下:

對於前端同窗來講,寫一個相似上面的框內帶文字的效果很是簡單:

.text {
    // css 樣式
}

 哈哈哈哈 
複製代碼

只須要 html + css 就能夠實現。

對於客戶端的同窗,則須要寫很是多的代碼來實現:

  • 用邏輯代碼寫一個框(樣式,大小,位置……)
  • 用邏輯代碼寫一行文字(樣式,大小,位置……)
  • 用邏輯代碼把兩個合起來

因此,weex 會把上面一些系列複雜的代碼封裝好一個個現成的方法。

第六步 渲染引擎

原生渲染器接收上層傳來的渲染指令,而且逐步將其渲染成原生組件。

這樣,咱們在js中的<div>, <p> 標籤,就一一對應到了客戶端的原生標籤。

這個過程不是分階段一個一個執行的,而是能夠實現「流式」渲染的,有可能第一個<div>的原生組件還沒渲染好,<text>的渲染指令又發過來了。當一個頁面特別大時,能看到一塊一塊的內容逐漸渲染出來的過。

總結

經過前文的介紹,相信你們對WEEX有了一個初步的系統認識。簡單來講,WEEX放棄了傳統的Webview,而是搭建了一個native化的瀏覽器,由於用native的方式實現了一個瀏覽器的大部分核心組成成分:

  • JS 執行引擎
  • 渲染引擎
  • DOM樹管理
  • 網絡請求,持久層存儲
  • 等等能力.

另外爲了保證整個SDK的運行效率,SDK維護了三個線程:

  • bridge線程:完成js到native之間的通訊
  • dom線程:完成dom結構的構建
  • 渲染線程:完成UI渲染,也就是UI線

以上就是WEEX SDK的大體框架和核心邏輯

相關文章
相關標籤/搜索