寫在前面
2018 年,Airbnb 放棄了繼續使用 React Native,個中原因主要有兩方面:
- 技術:成熟度、配套設施/類庫建設成本、首屏性能硬傷等沒能很好地解決
- 團隊組織:工程師要求高、跨技術棧/跨團隊調試/測試等也產生了新的問題
實際上,跨端方案遭遇的問題遠不止這些,一些時候 Write Once, Run Everywhere 只是美好愿景
一.技術困境
一言以蔽之,觸碰到能力邊界之前,跨端方案里的一切都是美好的
以 React Native 為例:

Bridge 層通過消息通信將 JAVAScript 世界與 Native 世界聯系起來
Shadow Tree 用來定義 UI 效果及交互功能,Native Modules 提供 Native 功能(比如藍牙),二者之間通過 JSON 消息相互通信
P.S.圖有些舊,但不影響理解原理,更新的 React Native 架構圖見 React Native 架構演進
在這樣的技術架構中,寫的和實際執行的都是 JavaScript,調用由 Native 提供的視圖渲染能力及平臺特定能力,Facebook 稱之為Scripting native 方案,姑且叫做容器化 Native 跨端方案:
將 Native App 改造成標準化的容器,進而允許一套代碼跨多端標準容器運行,如 React Native/Weex、Flutter
(摘自移動端跨平臺技術之下的變與不變)
容器能力在很大程度上決定著開發效率,在容器提供了一致的標準支持范圍內,能夠愉快地一人搞定多端。
然而,一旦觸及能力邊界,就會面臨高成本的多層聯合開發(Native 層、JavaScript 引擎層、特定業務領域層、業務層……),人效上并沒有優勢。另一方面,如何保持多端一致性,也是個極其復雜并且充滿技術挑戰的問題
此外,相關的配套能力也直接關系到開發效率:
- 調試:跨技術棧調試一直是難題,問題排查成本越來越高
- 性能:跨線程、跨頁面耗時分析困難、性能工具鏈缺失
- 工程鏈路:由于技術方案的特殊性,需要量身定制許多配套設施(包括但不限于 IDE、調試、性能、CI/CD、監控),才能將各個環節的成本降下來
總的來說,技術上最大的困境在于:
- 抹不平的多端差異
- 掀不開的 JavaScript 引擎蓋
- 跟不上的配套能力
多端一致性在技術投入上幾乎是無底洞,底層的平臺架構差異(UI 渲染方式、事件機制、系統 API)根深蒂固,以各類跨端方案目前的成熟度僅能覆蓋極其有限的一部分,留有非常大的空白需要通過擴展容器能力來填補,包括通用的基礎能力(如 UI、交互),以及面向業務領域的特定能力(如多媒體、定位)
引入 JavaScript 引擎雖然獲得了動態執行代碼的能力,但也帶來了技術上的不確定性,幾乎無法跟蹤解決 JavaScript 引擎內部的崩潰或異常行為
通用的基礎設施大多無法直接用于跨端場景,邊邊角角的配套能力都需要花力氣建設,而為了滿足業務快速生長,總是優先建設核心關鍵能力,配套支持通常是滯后的,同樣影響著效率
Flutter 能帶來一些不同嗎?

事實上,Flutter(目前看起來)同樣面臨這些技術困境,技術實現的變化并未徹底改變局面
據 2020 Q1 調查結果,Flutter 開發者認為最重要的 6 個問題是:
- 調試錯誤和崩潰
- 測試確保 App 能夠跨多平臺運行
- 選用狀態管理方案
- 理解和處理布局問題(如文本溢出)
- 根據設計稿創建 UI
- 排查特定平臺問題
同時,認為最困難的 6 個問題是:
- 排查特定平臺問題
- 內存問題的診斷和修復
- CPU 使用率問題的診斷和修復
- 接入現有的 Native API
- UI 卡頓問題的診斷和修復
- 開發特定平臺的 Flutter 插件
因此,跨端方案的調試、性能之痛仍在 Flutter 延續,多端差異以及配套能力的困境并沒有改變
二.團隊組織困境
與單端開發模式相比,跨端方案的協作成本更高,體現在:
- 跨團隊
- 鏈路長
- 容器團隊壓力大
- 職責邊界不清晰
跨端方案下,跨團隊協作成為了最主要的協作方式,需求串講、開發、聯調、問題排查等多個環節都需要跨團隊溝通/協作,溝通成本不容忽視
長鏈路意味著技術細節散落在多層,各自只擁有一小部分知識:
表層業務邏輯
-----------------------------
特定業務領域框架
-----------------------------
通用前端框架/類庫
-----------------------------
JavaScript引擎(擴展)
-----------------------------
Native Module | 特定業務領域能力
-----------------------------
Native通用框架
-----------------------------
Native View
-----------------------------
平臺操作系統
由于每個團隊都看不到全景,每一個原因不那么顯而易見的問題就都要一層層向下排查,甚至涉及特定業務領域能力的部分又分為許多層……
另一方面,如此繁多的層次也造成了復雜度堆積,越往下層復雜度越高,因為不確定的可變輸入越多,越難弄明白來龍去脈,排查問題的成本也越高
理想情況下,按漏斗模型逐層過濾,每一層只需要檢查自己的輸入輸出,但滯后的配套能力讓表層業務難以識別出問題所在的范圍,于是容器團隊成為了問題流轉的過濾閥,上接紛繁的 JavaScript 業務,下連復雜的特定領域能力,大量的時間耗費在了弄清楚來龍去脈上,容器能力擴展被迫降速,反復排查已知問題……
業務視角下,對業務之下的層次職責劃分并不十分清楚,因此很容易找錯層/人,產生無效的“重定向”。而容器層同樣也不具備全景視圖,問題流轉軌跡變得相當曲折,溝通成本充斥在各個環節中,制約著開發效率
三.個體困境
對個體而言,面臨的最大困難是跨端方案與 Web 標準存在些許差異,并且這些許差異不像 W3C 標準一樣能寫得清清楚楚:
Weex enables developers to use modern web development skills to build Android, IOS, and Web apps with a single codebase.
也就是說,通用的 Web 經驗不完全適用,學習曲線并不十分友好,例如:
- rem、媒體查詢、scale/zoom等適配經驗都不一定適用
- 減少 DOM 操作、合并 JavaScript 文件、開啟硬件加速等常規優化措施也不一定能產生明顯的性能優化效果
- (像學習 Web 一樣)只了解瀏覽器之上的標準能力是不夠的,想要真正高效地完成業務開發工作,容器原理甚至部分實現細節都要理解
就像有 Native 背景的開發者學習TypeScript一樣,初接觸無師自通,熟悉的Class、Interface、靜態類型用起來游刃有余……然而,熟知 TypeScript 的開發者一定知道個中細節存在著多少奇怪的地方
四.跨端的真正意義是什么?
React Native 最初的出發點是:
希望 Native 開發也能像 Web 一樣 Move fast
快速迭代(Rapid iteration cycle):Web 一天兩版,產品迭代周期更短
快速反饋(Immediate testing feedback):Web 發布立即觸達用戶,A/B test 等實驗結果立等可取,產品演進更快
快速開發(Rapid development velocity):刷新瀏覽器即可生效,不必等待重新編譯 App
黯羽輕揚,公眾號:前端向后React Native 從誕生到現在
因此,從需求角度來看,開發效率是次要的,動態化的靈活性、快速迭代助業務先贏才是其跨端的主要意義,或者說追求的是生產效率,而不僅是開發效率,更短的迭代周期,更快速的觸達用戶都是直接的生產效率進步
然而,在三大困境之下,開發效率實際上也嚴重影響著生產效率,但還不足以抵消快速迭代、動態發布的重大進步,此消彼長也算是一種平衡,一種可接受的妥協
五.在困境中尋找生門

理想情況下,容器應該是趨于標準化的,提供多端一致、豐富穩定的能力支持,之上的業務棧極少觸及容器能力邊界,從而使得容器層能夠不斷優化探索,更好地滿足業務發展的需要
另一方面,跨端方案只是將多端不一致性帶來的復雜度下沉到了容器層,獨立于平臺的語言環境(JavaScript 引擎、Dart 虛擬機等)能夠保證上層業務邏輯的一致性,但容器層仍然需要在多端各自實現一套,如何保證容器能力的多端一致性,仍然是個大問題,也并不比非跨端方案下容易多少
因此,首先要解決容器能力豐富度的問題,將邊界拓寬,從根源上減少問題。轉而集中火力到真正的難題上,攻下最有價值的難點部分。同時通過虛擬架構等方式建立全職能的業務支撐團隊,降低溝通成本:
- 業務要有自研能力:化解下層資源瓶頸,共同豐富容器能力
- 專注必須花大力氣投入的點:調試能力、標準化、性能分析以及持續跟蹤、工程配套設施
- 要有全職能的業務支撐團隊:有能力兜住所有問題,真正提供一攬子解決方案,消除無意義的溝通重定向
其中,業務自研能力先要有標準的擴展方式,要求容器實現上易擴展,業務開發者不需要了解過多細節也能快速進入開發。調試能力在長鏈路的技術棧下至關重要,問題識別成本越低、準確率越高,效率越高,所能釋放出來的資源就越多
從業務開發角度來看,更需要的可能是一層網關,請求過去響應回來,而不是一系列路由表,需要一跳一跳地跟蹤。全職能的業務支撐團隊組成局域網,讓網關之后的流量得以快速流轉,高效協作的同時提升業務開發的幸福感

作者:黯羽輕揚 來源:前端向后