假如 Web 當初不支持動態化
本文轉載自微信公眾號「前端向后」,作者黯羽輕揚。轉載本文請聯系前端向后公眾號。
楔子
Web 生而具有極其靈活的動態化基礎能力,諸如:
- 動態插入script標簽執行任意腳本邏輯
- 動態插入style標簽引入任何 CSS 樣式規則
- 通過iframe標簽嵌入整站
- 以上標簽均可直接加載網絡資源
- 承載這些內容的 Web 頁面部署在遠程服務器,可隨時動態更新,并且能立即生效
一直以來的探索和實踐似乎只是在不斷地發掘動態化能力的工程價值,為其尋找更合適的應用場景,比如早期的frameset,如今的微前端/微應用
而移動端正好相反,生而具有許多靈活性限制:
- 原生不支持動態執行邏輯代碼
- 構成移動應用程序的關鍵資源大都要打入安裝包中(動態庫例外)
- 應用程序安裝在用戶設備上,安裝包更新需經應用商店審核,用戶重新安裝才能生效
移動業務的發展不斷地對動態化能力提出更高的要求,但苦于缺少動態化的基礎能力,所以一直在探索更靈活的技術方案,像早期的熱修復/熱更新,到如今的小程序
實際上,二者在動態化技術能力上所要解決的工程問題是一致的,比如動態加載依賴庫、視圖組件、甚至整個應用。所以不妨開個腦洞,假定 Web 不支持動態化,以 Native 的業務訴求來推演 Web 動態化技術的發展軌跡
伊始:原生 WebAssembly
- 0061 736d 0100 0000 0187 8080 8000 0160
- 027f 7f01 7f03 8280 8080 0001 0004 8480
- 8080 0001 7000 0005 8380 8080 0001 0001
- 0681 8080 8000 0007 9080 8080 0002 066d
- 656d 6f72 7902 0003 6763 6400 000a ab80
- 8080 0001 a580 8080 0001 017f 0240 2000
- 450d 0003 4020 0120 0022 026f 2100 2002
- 2101 2000 0d00 0b20 020f 0b20 010b
從前,Web 應用程序只能被打包成這種wasm的二進制格式,發布到各大瀏覽器應用商店。期間,不僅要等待數天的審核,通過之后還要等用戶主動安裝更新,等到新版本真正“生效”(覆蓋大多數用戶),可能已經是數月之后了
版本更迭慢,無論是戰略性的重要功能還是十萬火急的問題修復,都無法及時觸達用戶。即便線上著火了,最快速的救火方案也要幾天甚至幾周之后才能起到作用
為了能夠更快地修復問題、降低風險,熱修復方案的探索就此展開
浪花:為熱修復引入腳本語言 JavaScript
熱修復意味著要加載并運行(安裝包之外的)邏輯代碼,所以有人直接從 WebAssembly 模塊加載機制入手,研究出了一些 Hook 方案,能夠動態地換掉某些模塊/文件
也有人沿著這個方向走得更遠,權衡時效性、性能、兼容性與穩定性,通過編譯插樁、工程配套設施、運行時框架等手段解決了模塊依賴、版本管理、差量更新等問題,將應用程序的各個功能模塊插件化
還有人另辟蹊徑,引入輕量級的腳本語言運行時(如 JavaScript 引擎),并在瀏覽器原生 WebAssembly 與 JavaScript 世界之間架起一座橋梁,允許通過 JavaScript 調用原生的系統平臺能力,從而擴展出了動態化的基礎能力
動態化漾起了一道波紋,緊接著是呼嘯而來的動態更新浪潮
海嘯:基于 JavaScript 的動態更新
往動態化方向邁出第一步之后,離全面動態化的大好前景也就一步之遙了:
Any application that can be written in JavaScript, will eventually be written in JavaScript. —— Jeff Atwood
(摘自The Principle of Least Power)
全面動態化意味著要:
- 將應用程序中所有能夠動態化的部分全都遷由 JavaScript 實現
- 將龐大的 JavaScript 代碼按功能模塊組織起來,并管理好功能模塊之間的依賴關系
從而實現以功能模塊為單位的快速迭代,相當于將熱修復技術應用到問題修復之外的需求迭代上,既不用發版,免去了審核周期,也不需要等待用戶主動安裝,新功能得以動態發布并迅速覆蓋到活躍用戶
堤壩:容器概念形成
隨著動態化程度的不斷提升,JavaScript 在應用程序中的占比越來越高,最終僅剩余無法動態化(或沒有必要動態化)的部分仍由 WebAssembly 實現,包括:
- 系統平臺能力橋接
- 基礎 UI 控件、交互能力
- 視圖層框架(歷史棧管理、生命周期支持等)
- 特定業務領域能力(例如多媒體內容生產、IM SDK 等)
- 通信機制(廣播、狀態共享等)
這些部分形成了容器(原生外殼),相當于運行在瀏覽器中的一個動態化運行時,在容器圈定的能力范圍內,業務能夠充分利用動態優勢,實現快速修復、快速發布、快速觸達、快速迭代
但隨容器概念一同出現的,除了賦能業務跑得更快之外,還有動態業務與容器之間的依賴問題:
- 如何解除二者之間的強耦合,如路由、混合視圖容器等場景?
- 如何識別出二者之間的依賴關系?
- 如何保障依賴關系是可控的,比如禁止將依賴新能力的動態業務發布到舊容器中?
通過工程配套設施將依賴管束起來之后,接下來的首要問題是想辦法保證動態業務所依賴的底層容器的可靠性
邊界:HTML、JavaScript、CSS 構成容器標準
隔離變化的慣用手段是加一層抽象,將變化的部分置于抽象層之下:
- BOM API:對系統平臺、視圖層框架能力以及通信機制的抽象
- Native Module API:對特定業務領域能力的抽象
- DOM API:對基礎視圖渲染能力的抽象
- JS API:對 JavaScript 運行時的抽象
- CSS:對樣式、布局能力的抽象
- HTML:對基礎 UI 控件、交互能力的抽象
抽象出的這些標準確立了穩固的容器邊界,邊界之內,動態業務能夠肆意發揮,邊界之下,容器同樣能夠不斷精進、豐富容器能力,將邊界拓寬。同時,具有標準定義的 API 能夠以結構化的形式維護起來,對于開發體驗大有裨益
云海:瀏覽器支持加載網絡資源
另一方面,在標準化的過程中,一些動態化業務實踐也沉淀到了容器之中,例如:
- 動態腳本:script支持加載網絡資源
- 動態樣式:style支持加載網絡資源
- 動態路由:瀏覽器支持直接通過 URL 載入、或通過iframe嵌入網絡應用程序
雖然從熱修復開始就能夠從CDN拉取 JS 文件,運行時動態解釋執行了,但容器標準不僅對這種方式提供了便捷支持,還將動態化的基礎能力從邏輯擴大到了視圖、樣式、靜態資源等等
至此,動態化最關鍵的基礎能力已經完備了。遷至 JavaScript 的功能模塊甚至能夠進一步部署到云端,實現離線集成、在線托管兩種模式的靈活切換
一色:同步、異步模式切換自如
完備的動態化基礎能力解鎖了許多新玩法,例如:
- 模塊化(加載器)
- 代碼拆分
- SSR
- Hydration
- lazy 組件
- Suspense
將業務模塊(bundle)進一步拆分成功能模塊(chunk),并將非核心模塊異步出去,實現動態按需加載,例如第三方 JS SDK、jQuery 插件、以及分享/評論/城市選擇等重磅組件
對于內容呈現的偏靜態場景,還可以通過 SSR 在服務端完成(大部分)頁面渲染工作,加快首屏內容展現
另一方面,Hydration、lazy 組件、Suspense 等運行時特性使得在線的動態部分能夠與離線的非動態部分充分融合,實現更細粒度的業務動態化,讓在線托管真正成為一種部署選項
與此同時,動態業務自身的組件化程度也在不斷加深,前端開發的核心工作從頁面、模塊開發轉向了組件、編排邏輯開發
流云:數據驅動的前端應用程序
組件體系趨向成熟之后,一個由來已久的概念終于徹底浮出水面——數據驅動
從前后端分層的數據協議,逐漸演變成數據驅動,這里的數據包括 3 部分:
- 后端業務域數據
- 前端狀態數據
- (基于后端業務域數據的)前端衍生數據
將這些數據填入業務組件,即可渲染出完整的功能模塊(無論是在客戶端還是服務端),再將其放置到視圖容器中合適的坑位里,就完成了一次組件級的“發布”過程
這種模式涉及 5 個重要環節:
- 業務數據(包括后端業務域數據和前端衍生數據)的生產
- 業務組件(包括前端狀態數據)的生產和維護
- 組件的渲染(業務數據 + 業務組件 = 功能模塊)
- 坑位的生產
- 功能模塊的投放
其中,業務組件、坑位是進一步動態化的關鍵,可分為 4 個階段:
- 一個蘿卜一個坑:靜態業務組件 + 靜態坑位
- 一個蘿卜到處扔:靜態業務組件 + 動態坑位
- 多個蘿卜輪番扔:動態業務組件 + 靜態坑位
- 多個蘿卜到處扔:動態業務組件 + 動態坑位
要達到多個蘿卜到處扔的組件級動態化終極目標,就要求能夠動態發布業務組件、動態發布坑位
交融:動態業務組件 + 動態坑位
從端和云的視角來看,業務組件也可以看作數據(云)的一部分,相比之下坑位與端的關聯更為緊密,而動態化的唯一手段就是將端側的東西搬到云上去,所以要解決的關鍵問題是如何實現坑位的動態化
有 2 個思路:
- 干掉坑位的概念:將坑位的概念從組件級擴展到頁面級,一個頁面容器(一個 URL)即一個坑位
- 將坑位組件化:提供標準的坑位組件,就像iframe頁面是一種天然的動態坑位,可打開一個新的頁面容器加載任意 URL
對于除頁面之外的其它布局容器,如對話框、消息條、Banner 位、腰封等等,可以將坑位標準化成容器組件,與業務組件一并動態發布,將坑位的租賃關系維護在服務端,作為數據驅動的數據之一
至此,前后端分層的界限幾經重新定義,終于迎來了 JSP/PHP 融合數據與模板的黃金年代……
原文鏈接:https://mp.weixin.qq.com/s/MssledyYt_2gqZ5Q7xgjFg