前端開發:如何正確地跨端?

簡介: 面對多種多樣的跨端訴求,有哪些跨端方案?跨端的本質是什麼?做爲業務技術開發者,應該怎麼作?本文分享阿里巴巴ICBU技術部在跨端開發上的一些思考,介紹了當前主流的跨端方案,以及跨端開發的經驗心得。前端

跨端

Write once, run everywhere。

咱們都據說過這句經典的宣傳用語,後來咱們都知道,沒有什麼東西是能夠真正 run everywhere 的,充其量也只能作到 debug everywhere。編程

而當咱們談論一次編寫多端運行時,顯然不可能真的指跨一切全部端,大多數狀況下你不會須要在電腦和手環上同步開發一個功能。小程序

  • 跨 PC 和無線端。
  • 跨多 Native 平臺:例如跨 Android 和 iOS,甚至跨 Windows。
  • 跨投放 APP:隨着超級 APP 愈來愈多,不少業務須要在多個 APP 中投放同一個頁面。
  • 跨 Web 和 APP:Web 在不少狀況下仍然是不可避免的,咱們的頁面可能須要分享、SEO 或者投放到 M 站上等等,這時候就須要同時能在 Web 和 APP 內運行。
  • 跨 Web、多小程序、QuickApp 等:其實原本相似跨 APP,可是奈何小程序自己是各家控制的封閉生態,故而有了開發一次適配到多種封閉生態的訴求。
  • 其餘端的跨端訴求:例如跨 POS 機,手錶等。

與咱們多種多樣的跨端訴求相對應的,是百花齊放的跨端方案。瀏覽器

百花齊放的跨端方案

以 Web 爲基礎的 H5 Hybrid 方案

這類方案最爲直接,簡單來講就是用網頁來跨端。因爲咱們絕大多數端上(甚至包括封閉的小程序生態)都支持 Webview,因此只要開發網頁而後投放到多個端便可,在桌面端對應的方案就是 Electron。緩存

爲何不直接全用 Web?性能優化

從開發成本低、標準統1、生態繁榮上來講,H5 Hybrid 方案基本是不二之選。然而這種方案難以免在性能和體驗上存在差距。Web 的生態繁榮來自於其良好的歷史兼容性,也意味着沉重的歷史包袱。框架

  • W3C 標準做爲開放技術標準,歷史包袱多,邏輯複雜。
  • Web 標準在設計上不是 Design for Performance 的,致使不少地方難以進一步改善,例如 JS 執行和 Layout、渲染互斥沒法並行,致使過長的 JS 執行任務會執行正常的渲染致使卡頓。
  • Web 的標準化在推動上也比較慢,新的能力可能要比較長的時間才能使用。

React-Native/Weex 類方案

在移動平臺上尤爲是早期 WebView 的性能體驗很是糟糕,前面咱們也提到這種差距主要來自於 Web 生態自己沉重的歷史負擔。編程語言

而 React-Native/Weex 這類方案經過儘量的取長補短,經過結合 Web 的生態和 Native 的組件,讓 JS 執行代碼後用 Native 的組件進行渲染。因爲拋棄了 Web 的歷史包袱,這類方案能夠作一些大刀闊斧的改動。工具

例如 RN 就以下圖中,把 JS 執行、佈局(Yoga)和渲染(Native 組件)放在三個進程分開執行,避免了 JS 執行復雜任務時界面卡頓。經過拋棄 CSS 中的大量標準,只支持部分 flex 佈局能力來減小布局和渲染的複雜度。佈局

這種方案一樣存在一些缺陷:

  • iOS/Android 雙端自己不一致的組件和佈局機制,讓雙端一致性難以獲得保障。
  • 依賴於 Native 機制也讓一些 CSS 屬性實現起來比較困難,例如老大難的 z-index 問題。

而最麻煩的一點在於,這套方案意味着很是高的維護支持成本。

  • 借用了 Web 的生態但並不徹底是 Web 生態,不少地方不一致,最多見的吐槽就是慣用的 CSS 佈局方式沒法使用。
  • 相比於瀏覽器新增一個傳感器 API 都要配套完善的 devtool,這類方案大部分狀況下的開發體驗保障能夠說是刀耕火種(下圖爲 Chrome 的方向傳感器 API 的 devtool)。

在 WebView 性能差距逐漸縮小的今天,維護這一套複雜方案的 ROI 是否值得,須要根據咱們場景的具體訴求考量。

Flutter

Flutter 要解決的問題和上面的方案不一樣,徹底不打算繼續在 Web 生態上借力,從設計之初也並無把 Web 生態考慮進去。相比於 RN 依賴 Native View 渲染,Flutter 則是自繪的組件,直接經過 Skia 繪製到屏幕上。

因爲能夠徹底發揮 GPU 的能力,也不須要去 Native 繞一圈。Flutter 理論上能作到更好的性能和兩端一致性,這一意味着理論上將來可能基於 Flutter 的 JS 動態化方案可以在樣式上支持的比 WEEX 更好。

從前端的視角看仍然更像是一個 Native 開發方案而非跨端方案(雖然實際上是跨 Android/iOS 的)。目前最主要的問題是 Flutter for Web 從技術原理上來講離生產可用可能還很是遙遠。除此以外動態化能力的確實也會讓部分場景不適用。

研發框架 for 小程序

小程序是被創造出來的問題,各家小程序出於商業上的考量主動在 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 & H5 Hybrid

WebView 多是衆多容器中最爲特殊的一個,雖然很難知足部分場景對於性能和體驗的極致要求,可是會是最穩定、長期存在且獲得支持的方案。

從開發效率和將來長期的維護演變來看,在可以知足性能體驗要求的前提下,Web 方案仍然是最優先應該考慮的。

同時,在 APP 的 WebView 容器上咱們能作更多的工做,例如經過容器來提供一些端內的能力,結合 Native 能力實現的並行數據加載,頁面保活等等。

基礎建設

不管採用何種跨端方案,在哪一個容器中,性能、穩定性、效能都是繞不開的三駕馬車。

性能

對於不一樣的方案每每存在不一樣的性能方案,上面咱們也提到在小程序的運行時方案中就會有減小編譯模板產出的字段這樣的優化。然而,其實除了這種特定方案的優化外,大部分優化手段是大同小異的:離線緩存、數據預取、快照、SSR、NSR 等等方案。

對於不一樣的端和容器,對於性能問題的度量和發現也應該是一致的,咱們須要對頁面在不一樣端的性能究竟如何有明確的感知和橫向對比。

性能的端側建設(端能力、具體到某一個端的性能測算方案、性能打點等)可能須要根據不一樣的端、不一樣的跨端方案而不一樣。但性能的基礎建設(首屏標準、數據分析、基礎優化能力)在跨端中應該是相對穩定的。

在端側能力方面,ICBU 早期在 WEEX 性能優化時引入了並行加載的能力,經過 wh_prefetch 協議來使用容器的並行加載能力。然後在新的容器(WebView、瀏覽器)中,雖然底層能力存在差別,但仍然識別相同的協議。

在數據採集和分析方面,咱們經過統一跨端基礎庫,讓不一樣端不一樣技術方案能夠在一樣的標準下分析、度量和對比。

穩定性建設

在無線端咱們經常把性能 & 穩定性並稱爲 「高可用」,穩定性主要涵蓋的範圍包括灰度能力、業務監控、報警、錯誤監控、白屏檢測等等。

這些能力相比起來對具體端和跨端方案的依賴更少,除了在端側的數據採集邏輯稍有區別外其餘建設部分相對也是比較穩定的。集團針對這些場景也存在一些跨端可用的方案、例如 iTrace 等。

工程基建

對於不一樣場景的跨端,雖然在方案上存在必定的差別,可是咱們的工程基建是能夠保持統一

  • 容器層提供統一的 API 和文檔能力
  • 統一的研發流程
  • 研發工具提供統一的抓包、debug 能力等
  • 一致的工具庫等等

這樣,當有新的容器或者方案出現時,咱們只須要按照相應的能力進行對齊,就能讓咱們上層的業務代碼和開發體驗維持相對穩定的狀態。

業務邏輯跨端

相對來講,咱們會發如今多種跨端方案的演化中,如何渲染、如何佈局等 UI 層面的變化要遠遠大於業務邏輯層面。甚至是小程序和 Flutter,其大體的開發範式都沒有發生太大的改變。例如 Flutter 開發範式和 React 很是類似,一樣是聲明式 UI,一樣存在 VirtualDOM。

考慮到 SEO 和性能等問題和 Flutter 自己基於 Skia 的渲染模式,Flutter for Web 在至關長一段時間內可能都不會是一個生產環境可用的方案。

而在統一了業務邏輯代碼的組織方式後,咱們能夠經過 Hooks for ALL 的方案讓 Flutter 和 Web 端能夠共享一份基於 Hooks 的業務邏輯代碼。

有時候你不須要真的 run everywhere,可以提升效能、保持一致就已經達到目的了。

視圖層

目前來看視圖層的跨端仍然充滿了變數,在咱們的業務邏輯層跨端作的足夠原子化後,也許咱們部分交互邏輯不是特別重的視圖層可以經過 DX + 綁定原子化邏輯 + 數據參數的方式覆蓋更多的跨端場景。從而同時知足性能、效能方面的訴求。

然而對於通用場景的視圖跨端,仍然沒有銀彈。

總結

整體來講,跨端處於且將長期處於多方案並存且不斷變化的狀態。除了針對新的變化新的場景選擇或創造合適的方案,咱們更要作好這種動態變化中長治久安的部分:

  • H5 Hybrid。
  • 性能、穩定性、效能三駕馬車的統一性和延續性。
  • 在不強求 write once 的場景下,考慮比 UI 跨端更簡單的業務邏輯跨端。

做者:開發者小助手_LS
原文連接本文爲阿里雲原創內容,未經容許不得轉載

相關文章
相關標籤/搜索