簡介: 面對多種多樣的跨端訴求,有哪些跨端方案?跨端的本質是什麼?做爲業務技術開發者,應該怎麼作?本文分享阿里巴巴ICBU技術部在跨端開發上的一些思考,介紹了當前主流的跨端方案,以及跨端開發的經驗心得。前端
Write once, run everywhere。
咱們都據說過這句經典的宣傳用語,後來咱們都知道,沒有什麼東西是能夠真正 run everywhere 的,充其量也只能作到 debug everywhere。編程
而當咱們談論一次編寫多端運行時,顯然不可能真的指跨一切全部端,大多數狀況下你不會須要在電腦和手環上同步開發一個功能。小程序
與咱們多種多樣的跨端訴求相對應的,是百花齊放的跨端方案。瀏覽器
這類方案最爲直接,簡單來講就是用網頁來跨端。因爲咱們絕大多數端上(甚至包括封閉的小程序生態)都支持 Webview,因此只要開發網頁而後投放到多個端便可,在桌面端對應的方案就是 Electron。緩存
爲何不直接全用 Web?性能優化
從開發成本低、標準統1、生態繁榮上來講,H5 Hybrid 方案基本是不二之選。然而這種方案難以免在性能和體驗上存在差距。Web 的生態繁榮來自於其良好的歷史兼容性,也意味着沉重的歷史包袱。框架
在移動平臺上尤爲是早期 WebView 的性能體驗很是糟糕,前面咱們也提到這種差距主要來自於 Web 生態自己沉重的歷史負擔。編程語言
而 React-Native/Weex 這類方案經過儘量的取長補短,經過結合 Web 的生態和 Native 的組件,讓 JS 執行代碼後用 Native 的組件進行渲染。因爲拋棄了 Web 的歷史包袱,這類方案能夠作一些大刀闊斧的改動。工具
例如 RN 就以下圖中,把 JS 執行、佈局(Yoga)和渲染(Native 組件)放在三個進程分開執行,避免了 JS 執行復雜任務時界面卡頓。經過拋棄 CSS 中的大量標準,只支持部分 flex 佈局能力來減小布局和渲染的複雜度。佈局
這種方案一樣存在一些缺陷:
而最麻煩的一點在於,這套方案意味着很是高的維護支持成本。
在 WebView 性能差距逐漸縮小的今天,維護這一套複雜方案的 ROI 是否值得,須要根據咱們場景的具體訴求考量。
Flutter 要解決的問題和上面的方案不一樣,徹底不打算繼續在 Web 生態上借力,從設計之初也並無把 Web 生態考慮進去。相比於 RN 依賴 Native View 渲染,Flutter 則是自繪的組件,直接經過 Skia 繪製到屏幕上。
因爲能夠徹底發揮 GPU 的能力,也不須要去 Native 繞一圈。Flutter 理論上能作到更好的性能和兩端一致性,這一意味着理論上將來可能基於 Flutter 的 JS 動態化方案可以在樣式上支持的比 WEEX 更好。
從前端的視角看仍然更像是一個 Native 開發方案而非跨端方案(雖然實際上是跨 Android/iOS 的)。目前最主要的問題是 Flutter for Web 從技術原理上來講離生產可用可能還很是遙遠。除此以外動態化能力的確實也會讓部分場景不適用。
小程序是被創造出來的問題,各家小程序出於商業上的考量主動在 Web 生態的基礎上構造了相對封閉的生態。致使和 Web 生態格格不入。然而有多端小程序投放,或者同時投放小程序和 Web 端的場景難以接受使用。
因爲小程序的端封閉且不受控,要解決小程序的跨端問題每每只能從研發框架層面出發。
比較知名的編譯時方案是 Taro,大體的原理能夠解釋爲將 JSX 編譯到小程序的 WXML/WXSS/JS 上,而這類框架的實現原理其實並不是真的是一個 React 或者類 React 框架,而是把看起來像是 JSX 的模板經過靜態編譯的方式翻譯成小程序自身的模板。
這樣作的限制很是明顯,那就是 JSX 是 JavaScript 的拓展語言(React Blog 寫的是 is a syntax extension to JavaScript),而小程序所採用的 WXML 倒是一個表達能力很是受限的模板語言,咱們不可能完成從一個通用編程語言到模板語言的編譯。
而靜態編譯類框架爲了作到這一點,採起的方式就是限制開發者的寫法,這也是爲何 taro 對 JSX 的寫法作出了諸多限制。這一點直接致使了無窮盡的維護成本和嚴重受損的開發體驗,然後 taro/next 也轉向了運行時方案 + 靜態編譯優化的結合。
不謙虛的說,針對小程序的運行時方案應該是最先我在寫下 remax 第一個 issue 時[1]提出的。
經過 React Reconciler(相似於 Rax Driver)咱們可讓運行在小程序容器中的 React 不去直接操做 DOM,而是把操做的數據經過 setData 傳遞給小程序的 View 層映射到最後的界面上。
雖然 Remax、Rax 運行時、Taro Next 等幾種方案不盡相同,可是思路大同小異,就是利用小程序模板必定程度上的動態化能力 + 類 React 框架的 VirtualDOM 來進行渲染。固然這種作法相對於小程序原生的渲染方式存在必定的性能損耗。
remax 的支付寶端性能測試
在部分場景下,這種損耗是值得的。這些運行時框架也都在陸續經過容許關掉編譯產生的模板中的不用的屬性、部分靜態編譯、虛擬列表等方式來改進性能。
固然了,最後內嵌 Webview 仍然是一個方案。
上面介紹的都是針對某些具體的場景的一些解決方案,然而對於業務技術團隊來講,跨端的本質是提效。針對新的變化提出新的方案是一方面,更重要的如何讓這種提效真的長治久安,讓咱們的提效不會變成從一個新方案跳到另一個新方案。
讓咱們從新看上面這張圖,能夠肯定的是,跨端的訴求和與之對應的方案仍然會處於頻繁的變化中,也不會出現一個解決全部跨端問題的方案。而其中相對不變的部分是值得咱們爲了長治久安必需要投入的。
WebView 多是衆多容器中最爲特殊的一個,雖然很難知足部分場景對於性能和體驗的極致要求,可是會是最穩定、長期存在且獲得支持的方案。
從開發效率和將來長期的維護演變來看,在可以知足性能體驗要求的前提下,Web 方案仍然是最優先應該考慮的。
同時,在 APP 的 WebView 容器上咱們能作更多的工做,例如經過容器來提供一些端內的能力,結合 Native 能力實現的並行數據加載,頁面保活等等。
不管採用何種跨端方案,在哪一個容器中,性能、穩定性、效能都是繞不開的三駕馬車。
對於不一樣的方案每每存在不一樣的性能方案,上面咱們也提到在小程序的運行時方案中就會有減小編譯模板產出的字段這樣的優化。然而,其實除了這種特定方案的優化外,大部分優化手段是大同小異的:離線緩存、數據預取、快照、SSR、NSR 等等方案。
對於不一樣的端和容器,對於性能問題的度量和發現也應該是一致的,咱們須要對頁面在不一樣端的性能究竟如何有明確的感知和橫向對比。
性能的端側建設(端能力、具體到某一個端的性能測算方案、性能打點等)可能須要根據不一樣的端、不一樣的跨端方案而不一樣。但性能的基礎建設(首屏標準、數據分析、基礎優化能力)在跨端中應該是相對穩定的。
在端側能力方面,ICBU 早期在 WEEX 性能優化時引入了並行加載的能力,經過 wh_prefetch 協議來使用容器的並行加載能力。然後在新的容器(WebView、瀏覽器)中,雖然底層能力存在差別,但仍然識別相同的協議。
在數據採集和分析方面,咱們經過統一跨端基礎庫,讓不一樣端不一樣技術方案能夠在一樣的標準下分析、度量和對比。
在無線端咱們經常把性能 & 穩定性並稱爲 「高可用」,穩定性主要涵蓋的範圍包括灰度能力、業務監控、報警、錯誤監控、白屏檢測等等。
這些能力相比起來對具體端和跨端方案的依賴更少,除了在端側的數據採集邏輯稍有區別外其餘建設部分相對也是比較穩定的。集團針對這些場景也存在一些跨端可用的方案、例如 iTrace 等。
對於不一樣場景的跨端,雖然在方案上存在必定的差別,可是咱們的工程基建是能夠保持統一
這樣,當有新的容器或者方案出現時,咱們只須要按照相應的能力進行對齊,就能讓咱們上層的業務代碼和開發體驗維持相對穩定的狀態。
相對來講,咱們會發如今多種跨端方案的演化中,如何渲染、如何佈局等 UI 層面的變化要遠遠大於業務邏輯層面。甚至是小程序和 Flutter,其大體的開發範式都沒有發生太大的改變。例如 Flutter 開發範式和 React 很是類似,一樣是聲明式 UI,一樣存在 VirtualDOM。
考慮到 SEO 和性能等問題和 Flutter 自己基於 Skia 的渲染模式,Flutter for Web 在至關長一段時間內可能都不會是一個生產環境可用的方案。
而在統一了業務邏輯代碼的組織方式後,咱們能夠經過 Hooks for ALL 的方案讓 Flutter 和 Web 端能夠共享一份基於 Hooks 的業務邏輯代碼。
有時候你不須要真的 run everywhere,可以提升效能、保持一致就已經達到目的了。
目前來看視圖層的跨端仍然充滿了變數,在咱們的業務邏輯層跨端作的足夠原子化後,也許咱們部分交互邏輯不是特別重的視圖層可以經過 DX + 綁定原子化邏輯 + 數據參數的方式覆蓋更多的跨端場景。從而同時知足性能、效能方面的訴求。
然而對於通用場景的視圖跨端,仍然沒有銀彈。
整體來講,跨端處於且將長期處於多方案並存且不斷變化的狀態。除了針對新的變化新的場景選擇或創造合適的方案,咱們更要作好這種動態變化中長治久安的部分:
做者:開發者小助手_LS
原文連接本文爲阿里雲原創內容,未經容許不得轉載