首頁>技術>

寫在前面

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 APIUI 卡頓問題的診斷和修復開發特定平臺的 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

因此,從需求角度來看,開發效率是次要的,動態化的靈活性、快速迭代助業務先贏才是其跨端的主要意義,或者說追求的是生產效率,而不僅是開發效率,更短的迭代週期,更快速的觸達使用者都是直接的生產效率進步

然而,在三大困境之下,開發效率實際上也嚴重影響著生產效率,但還不足以抵消快速迭代、動態釋出的重大進步,此消彼長也算是一種平衡,一種可接受的妥協

理想情況下,容器應該是趨於標準化的,提供多端一致、豐富穩定的能力支援,之上的業務棧極少觸及容器能力邊界,從而使得容器層能夠不斷優化探索,更好地滿足業務發展的需要

另一方面,跨端方案只是將多端不一致性帶來的複雜度下沉到了容器層,獨立於平臺的語言環境(JavaScript 引擎、Dart 虛擬機器等)能夠保證上層業務邏輯的一致性,但容器層仍然需要在多端各自實現一套,如何保證容器能力的多端一致性,仍然是個大問題,也並不比非跨端方案下容易多少

因此,首先要解決容器能力豐富度的問題,將邊界拓寬,從根源上減少問題。轉而集中火力到真正的難題上,攻下最有價值的難點部分。同時通過虛擬架構等方式建立全職能的業務支撐團隊,降低溝通成本:

業務要有自研能力:化解下層資源瓶頸,共同豐富容器能力專注必須花大力氣投入的點:除錯能力、標準化、效能分析以及持續跟蹤、工程配套設施要有全職能的業務支撐團隊:有能力兜住所有問題,真正提供一攬子解決方案,消除無意義的溝通重定向

其中,業務自研能力先要有標準的擴充套件方式,要求容器實現上易擴充套件,業務開發者不需要了解過多細節也能快速進入開發。除錯能力在長鏈路的技術棧下至關重要,問題識別成本越低、準確率越高,效率越高,所能釋放出來的資源就越多

從業務開發角度來看,更需要的可能是一層閘道器,請求過去響應回來,而不是一系列路由表,需要一跳一跳地跟蹤。全職能的業務支撐團隊組成區域網,讓閘道器之後的流量得以快速流轉,高效協作的同時提升業務開發的幸福感

最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • Angular 父子Component的資料繫結實現