🎯【深入解析】跨端框架的核心技術到底是什麼?

📌

本文是我在學習多個平台 UI 框架後的一些感觸,受精力和技術水平所限,文中定有不足之處,請各位大佬多多指教

如果你覺得我的文章對你有幫助,在收藏的過程中,一定要記得點贊點在看哦,謝謝你,這對我真的很重要🌟!

一、前端三板斧

正式討論「跨端開發」這個概念前,我們可以先思考一個問題:對大部分前端工作來說,前端主要幹些啥?

我個人認為,無論環境怎麼變,前端基本上就是做三件事情:

Fetch Data、Manage State、Render Page
Fetch Data、Manage State、Render Page
  • fetch data(數據獲取)
  • manage state(狀態管理)
  • render page(頁面渲染)

沒了。

也許有人覺得我說的太片面,其實我們可以理一理。往近了說,現在知識付費搞的如火如荼,動不動就搞個「XXX 源碼解析」,分析一下這些課程的主題和目錄,你就會發現基本都是圍繞着這三個方向展開講的;往遠了說,我們可以分析一下 Web 前端的發展歷程:

  • 1995 年左右,用 HTTP/1.0 拉取數據,用第一版的 JavaScript 管理幾個前端狀態,用裸露的 HTML 標籤展示頁面

  • 2005 年左右,用 HTTP/1.1 和 AJAX 拉取數據,用 JavaScript 做做表單畫畫特效,用 CSS 美化頁面

  • 2010 年左右,用 HTTP/1.1 和 AJAX 拉取數據,用 jQuery 操作 DOM 處理前端邏輯,用 CSS 美化頁面

  • 2015 年左右,隨着 HTML5 標準的推廣和瀏覽器性能的提升,前端開始進入「學不動了」的時代:

    • 在 fetch data 層面,除了 HTTP/1.1 和 AJAX,HTTPS 來了,HTTP/2 來了,WebSocket 也來了
    • 在 manage state 層面,Angular、React 和 Vue 先後出現,從現在看,React 的狀態驅動視圖的理念直接影響了 Flutter 和 SwiftUI 的設計
    • 在 render page 層面,除了傳統的 HTML + CSS,還加入了 CSS3、Canvas 等概念,音視頻功能也得到加強
  • 最近幾年,網絡協議趨於穩定,幾年內也不會有啥大的變動;國內 React 和 Vue 的地位基本穩固,一堆前端盯着 GitHub 進度條等版本更新;render 層出了不少幺蛾子,好不容易擺脫了 IE6,又來了各種小程序,同一套業務邏輯寫好幾遍不經濟也不現實,這時候各種跨端方案就整出來了

經過一番分析,這個三板斧理論看上去已經有些道理了,我們順着這個方向再向底層思考:這三大功能是怎麼實現的?

  • fetch data 方向,最後要靠網絡協議棧把數據發出去,但是讓一個前端直接搞套接字編程是非常不現實的,所以我們需要把網絡操作封裝為庫,讓應用層調用
  • render page 方向,最後是把相關圖元信息通過各種圖形 API(OpenGL/Metal/Vulkan/DirectX)發給 GPU 進行渲染,很多前端的圖形學路程最終都止於一個三角形,用這套技術棧去畫 UI 也極其不現實,更不要說排版系統這種工程量浩大的工作,所以這些活兒都讓相關的渲染引擎做了
  • manage state 方向,你可以用全局變量管理狀態,最後的結局一定被同事打爆,現在主流方案都是採用各種框架和 runtime 進行狀態管理,而這個 runtime 的宿主環境,往往就是某個語言的虛擬機,同時,fetch data 的起點,也是同一個虛擬機
虛擬機 渲染引擎
虛擬機 渲染引擎

經過上面的分析我們可以看出,前端的主要技術核心就兩個:虛擬機渲染引擎,這也意味着,如果我們想要搞跨端開發,就必須得統一虛擬機和渲染引擎

二、虛擬機和渲染引擎

1.網頁:JS Engine + WebKit

前端三劍客
前端三劍客

📌

因為谷歌的 Blink 引擎 fork 自蘋果的 WebKit,後文為了描述方便,統一用 WebKit 代替瀏覽器渲染引擎

網頁是成本最低上手最快的跨端方案了。得益於互聯網開放式理念,網頁天生就是跨端的,無論什麼渲染框架,WebView 都是必不可少的核心組件。

開發人員的接入成本也極低,主要技術就是 Web 開發那一套,前端主要頭疼的是各個渲染引擎的適配問題性能問題

現在主流的 JS Engine 是蘋果的 JavaScriptCore 和谷歌的 V8,主流的渲染引擎是蘋果的 Webkit 和谷歌的 Blink。雖然 W3C 的規範就擺在那裡,各個瀏覽器廠商再根據規範實現瀏覽器,這也是網頁跨端的基礎。問題在於瀏覽器內核實現總有細微差距,部分實現不合規範,部分實現本身就有 Bug,這也是前端擺脫不了適配需求的本質原因。

另一個是性能問題。其實 WebKit 本身的渲染速度還是很快的,但是受限於一些瀏覽器特性,比如說極其複雜極其動態的 CSS 屬性,DOM 樹和 CSSOM 的合併,主線程必須掛起等待 JS 的執行,這些都會大大降低性能,前端搞性能優化,一般得依據這些瀏覽器特性進行減枝處理,但是再怎麼優化,在頁面性能和交互體驗上,和 Native 還是有很大的距離。

2.網頁 PLUS:JS Engine + WebKit + Native 能力

直接拿個 URL 扔到 WebView 里是最簡單的,其實這樣也能解決大部分問題,畢竟前端 90% 的工作都是畫 UI 寫業務邏輯,但是還有 10% 的功能做不到,比如說要和 Native 同步狀態,調用一些系統功能。

要實現客戶端和網頁雙向通訊的話,一般都是藉助 JSBridge 進行通信,《JSBridge 的原理》這篇文章總結的不錯,感興趣的同學可以看一下。

JSBridge 只是解決了 Native 和 Web 的互相調用問題,如果我想藉助 Native 加強 Web 怎麼辦?這時候就有了一些探索:

  • 預熱:提前創建和初始化 WebView,甚至實現 WebView 容器池,減少 WebView 的啟動時間

  • 緩存:把常用的 Web 資源預先存在 Native 本地,然後攔截瀏覽器網絡請求重定向到本地,這樣就可以加快 Web 的資源加載速度(也叫「離線包」方案);

  • 劫持:比如說 Web 對網絡加載的控制力比較弱,部分有能力的廠商會把所有的網絡請求都劫持下來交給 Native 去做,這樣做可以更靈活的管理 Web 請求

  • 替換:替換一般指替換 Web 的 Img 標籤和 Video 標籤,這個最常見的地方就是各大新聞類客戶端。因為新聞的動態性和實時性,新聞都是由各個編輯/自媒體通過後台編輯下發的,這時候要利用 Web 強大的排版功能去顯示文本內容;但是為了加載速度和觀看體驗,圖片和視頻都是 Native 組件替換的

經過上面幾步,網頁的速度基本可以達到秒開的級別,這裏面最典型的就是幾大新聞客戶端,大家可以上手體驗一下。

3.小程序:JS Engine + WebKit

各大小程序平台
各大小程序平台

小程序,國內的特色架構,本質上是微信成為流量黑洞後,想成為流量分發市場管理和分發自己的流量,所以這是個商業味道很重的框架。

小程序在技術上沒什麼特別的創新點,本質上就是閹割版的網頁,所以微信小程序出來後各個流量寡頭都推出了自己的小程序,正如有人吐槽的,小程序的實現方式有 9 種,底層實現多樣化,各個廠實現還沒有統一的標準,最後就是給開發者喂屎,我也沒啥好介紹的,就這樣吧。

4.React Native:JS Engine + Native RenderPipeLine

React Native 和 Hermes
React Native 和 Hermes

React 2013 年發佈,兩年後 React Native 就發佈了,前幾種跨段方案基本都是基於瀏覽器技術的,RN 這個跨段方案的創新性在於它保留了 JS Engine,在渲染引擎這條路上,他沒有自己造輪子,而是復用了現有的 Native 渲染管線。

這樣做的好處在於,保留 JS Engine,可以最大程度的復用 Web 生態,畢竟 GitHub 上輪子最多的語言就是 JavaScript 了;復用 Native RenderPipeLine,好處在於脫離 WebKit 的歷史包袱,相對來說渲染管線更短,性能自然而然就上去了。

那麼問題來了,RN 是如何做到跨端的?這個其實全部仰仗於 React 的 vdom。

vdom

前端社區上有些文章討論 vdom,總會從性能和開發便捷性上切入講解,從純 Web 前端的角度看,這些的確是 vdom 的特點,但是這不是 vdom 真正火起來的原因。vdom 更大的價值在於,人們從 vdom 身上看到跨端開發的希望,所以在 React 出現後 React Native 緊跟着出現是一件非常自然的事情。為什麼這麼說?這個就要先溯源一下 UI 開發的範式。

UI 開發主要有兩大範式:Immediate Mode GUI(立即模式)Retained Mode GUI(保留模式)

簡單來說,IMGUI 每幀都是全量刷新,主要用在實時性很高的領域(遊戲 CAD 等);RMGUI 是最廣泛的 UI 範式,每個組件都被封裝到一個對象里,便於狀態管理和複雜的嵌套布局。無論是網頁、iOS、Android 還是 Qt 等桌面開發領域,都是基於 RMGUI 的。這兩者的具體細節差異,可以看這篇知乎回答和這個 Youtube 視頻

我們再回到 React Native 中,既然 iOS Android 的原生渲染管線都是 RMGUI 範式,那麼總是有相似點的,比如說 UI 都是樹狀嵌套布局,都有事件回調等等。這時候 vdom 的作用就出來了:

vdom 作為一個純對象,可以清晰的提煉出出布局的嵌套結構,而且這個抽象描述是平台無關的,那麼我們就可以利用 JS 生成 vdom,然後將 vdom 映射到 Native 的布局結構上,最終讓 Native 渲染視圖,以達到跨平台開發的目的。

到這裡如果你有些編譯原理的知識,你就會發現 vdom 和 IR 有些類似,同樣都是抽象於平台的中間態,vdom 上接 React 下接 Native RenderPipeLine,IR 上接編譯器前端下接編譯器後端,我們只要關心前半段的邏輯處理,臟活累活都讓後半部分做。

Hermes

2019 年 Facebook 為了優化 React Native 的性能,直接推出了新的 JS Engine——HermesFB 官方博文介紹了很多的優點,我個人認為最大的亮點是加入 AOT,傳統的 JS 加工加載流程是這樣的:

Babel 語法轉換Minify 代碼壓縮install 下載代碼Parse 轉為 ASTCompile 編譯Execute 執行

Hermes 加入 AOT 後,BabelMinifyParseCompile 這些流程全部都在開發者電腦上完成,直接下發位元組碼讓 Hermes 運行就行。

Bytecode precompilation with Hermes
Bytecode precompilation with Hermes

這樣做的好處在於,可以大大縮短 JS 的編譯時間,不信的話大家可以用 Chrome 分析幾個大型網站,JS 的解析加載時間基本占時都是 50% 以上,部分重型網站可能占時 90%,這對桌面應用來說還好,對於電量和 CPU 都要弱上一線的移動平台來說,這些都是妥妥的性能殺手,Hermes 的加入可以大大改善這一情況。

目前 React Native 0.64 也支持 Hermes 了,如果有做 RN 業務的同學可以玩一玩,看看在 iOS 上的性能提升有多大。

5.Flutter: Dart VM + Flutter RnderPipeLine

Flutter 和 Dart
Flutter 和 Dart

Flutter 是最近比較火的一個跨端方案,也有不少人認為這是最終的跨端方案,畢竟桌面軟件時代,最終勝出跨端方案就是 Qt,他們的共同特點就是自帶了一套渲染引擎,可以抹平終端差異。

Flutter 的創造還是很有意思的,這裡有個 Eric 的訪談,視頻中說 Eric 差不多有十幾年的 Web 渲染領域工作經驗,有一次在 Chrome 內部他們做了個實驗,把一些亂七八糟的 Web 規範去掉後,一些基準測試甚至能快 20 倍,因此 Google 內部開始立項,Flutter 的出現了。至於 Flutter 選擇 Dart 的理由,坊間一直傳說 Flutter 開發組隔壁就是 Dart 開發組,離得近就好 PY 交易,反正 Dart 也沒人用,沒啥歷史包袱,可以很好的相應 Flutter 的需求。

Flutter 的架構也是比較清晰的:

  • 虛擬機用的 Dart VM,Dart 同時支持 JIT 和 AOT,可以同時保證開發效率和運行效率
  • 渲染引擎先把 Dart 構建的視圖數據傳遞給 Skia,然後 Skia 加工數據交給 OpenGL/Metal 這兩個圖形 API,最終交給 GPU 渲染,整體上比 WebKit 的渲染流水線清晰不少

從純粹程度上看,Flutter 是做的最徹底的,虛擬機和渲染引擎都沒有用業內的成熟方案,而是自造了一套,好處就是沒啥適配壓力,壞處就是太新了,業務開發時往往會遇到無輪子可用的尷尬狀態,如果谷歌大力推廣,國內大廠持續跟進,前景還是很光明的。

6.其它方向的探索:JS Engine + Flutter RnderPipeLine?

社區里有一種聲音,認為 Flutter 最大的敗筆就是不能用 JavaScript 開發。這時候就會有人想,如果我們把 Web 技術和 Flutter 技術結合起來,用 JS Engine 對接世界上最大最活躍的 JS 社區,用 Flutter 渲染引擎對接高性能渲染體驗,國安中樂,豈不美哉?

豈不美哉?
豈不美哉?

目前來說一些大廠還是做了一些探索,我看了一些分析和項目架構,感覺就是做了個低配版的 React Native,React Native 的現有架構有一個性能瓶頸就是跨語言調用成本比較高,而這些大廠的調用鏈路多達 4 步:JS -> C++ -> Dart -> C++,更加喪心病狂,目前看無論是上手和推廣都是沒有直接用 RN or Flutter 方便。

三、各跨端方案的不足之處

跨端方案不可能只有好處的,各個方案的壞處也是很明顯的,我下面簡單列一下:

  • 網頁:性能是個過去不的坎兒,而且 Apple 明確指出不歡迎 WebView 套殼 APP,有拒審危險
  • 網頁 PLUS:技術投入很高,基本只能大廠玩轉
  • 小程序:對開發者不友好,技術半衰期極短
  • React Native:基本只能畫 UI,一旦做深了,只會 JS 根本解決不了問題,Java OC 都得學,對開發者要求比較高
  • Flutter:Android 支持很好,但 iOS 平台的交互割裂感還是很強的,而且和 RN 問題一樣,一旦做深了,必須學習客戶端開發知識,對開發者要求比較高

總的來說,在犧牲一定用戶體驗的前提下,跨端方案可以提高開發者的開發效率和公司的運行效率,我個人認為,只要某個方案的 ROI 比較高,其實是還是可以投入到生產的。

四、總結

本文到此就結束了,我把各個跨端技術提煉為為虛擬機和渲染引擎技術,然後以這兩個核心技術的角度去拆解各個跨端方案。一旦概念理清,在面對性能調優等技術場景時,就能抓住主要矛盾,更快更好的發現問題,解決問題。


如果你覺得我的文章對你有幫助,在收藏的過程中,一定要記得點贊點在看哦,謝謝你,這對我真的很重要🌟!

最後推薦一波我的微信公眾號:滷蛋實驗室和我的個人博客:supercodepower.com, 目前專註前端技術,對圖形學也有一些微小研究。

也可以加我的微信 egg_labs,歡迎大家來撩。

五、推薦閱讀

【答疑解惑】為什麼你的 Charles 會抓包失敗?

【全網最全】React Native 性能優化指南

【獨家】React Native 版本升級指南

webpack 中那些最易混淆的 5 個知識點