這一講我將從整體上梳理前端開發的演進歷史,并從渲染方案架構升級的案例出發,帶你了解現代化開發的方向。這部分內容并不涉及具體技術細節,更多的是作為本專欄的導讀,帶你體會現代化前端架構和基建的背景以及目前前端開發的大環境。
前端技術發展軌跡
過去十多年,前端技術發展日新月異,互聯網風口也從 PC 時代過渡到移動時代甚至智能時代。其間,前端崗位從無到有,再到如今扮演了至關重要的角色。相應地,前端基建和架構也慢慢浮出水面,呈現百花齊放的場景,技術環節自然也愈發復雜。
我們先從前端的技術發展軌跡說起,如下圖所示:

前端技術的發展軌跡圖
在靜態網頁 + 后端 MVC 技術架構時期,嚴格來說,并沒有專職前端工程師的職位。Web 工程師主要集中在后端方向,通過 Model 模型層進行數據的存儲和讀取、Controller 控制層對數據進行處理并實現業務邏輯需求,最終在 View 視圖層展示數據。這時候,每次請求都對應了一個靜態頁面的生成過程,我們把這種技術時代稱為 Web1.0。
接著,隨著2005 年 Ajax 技術的出現,標志了 Web1.0 到 Web2.0 的重要演進。此時,出現了真正意義上的前后端分離概念,這也使得前端工程師開始占據開發崗位的一席之地。前端通過 Ajax 技術獲取數據,進行頁面的展現和交互,而后端往往通過 Restful 接口,和前端進行協作。這個時期,前端需要大量地處理數據,因此前端 MVC 框架得到了發展。
比如,早期極具代表性的 Backbone.js 框架,架構風格非常明顯,我們可以看一下如下代碼:
```js
var M = Backbone.Model.extend({
defaults: {name: "lucas"} ,
initialize : function(){
this.on("change", function(){
console.log("change")
})
}
})
var model = new M()
```
這里的Backbone.Model實際上不僅包含了數據{name: "lucas"},其實也包含了數據變更時的監聽事件。對應 View 層代碼:
```js
var V = Backbone.View.extend({
initialize: function() {
this.listenTo(this.model, "change", this.show)
},
show: funtion(model) {
$("#id").append(this.model.name)
}
})
var m= new M()
var v = new V({model: m})
m.set("name", "hi")
```
Backbone.js 的出現是革命性的。但是以上述代碼為例,如果業務足夠復雜的話,上述狀態機一般的代碼就會成為負擔,代碼量也變得非常臃腫,難以維護。
隨著前端處理數據理念的革新,一種更新潮的 MVVM(View + ViewModel + Model)模式框架就出現了,MVVM 和 MVC 最大的區別在于:MVVM 采用雙向綁定(Data Binding)或自動渲染更新。
也就是說,View 層的變動,可以自動反映在 ViewModel 層。Angular 和 Vue 都采用這種模式。雖然 React 官方聲稱自己只是一個 View 層類庫,但是 React 搭配數據狀態管理生態,也符合 MVVM 模式。當然 React 并不是雙向綁定風格的解決方案,自動渲染更新也代表了一種潮流和方向。
整體看來,架構層面 MVC 風格向 MVVM 風格的演進,不僅簡化了數據與視圖的依賴,還解決了數據頻繁更新的問題。再加上虛擬 DOM 理念,為開發者屏蔽了 DOM 操作,業界框架方案逐漸穩定,這種低耦合模式也代表了現代化的設計理念。
這個時期,前后端分離技術發展到了頂峰,前端框架也互相學習借鑒,直到如今的Vue/React/Angular 三足鼎立的局面。
這個時代的穩定性一直持續到 Node.js 的崛起,隨著 Node.js 的出現,穩固的技術體系瞬間被打破。通過 Node.js,除了前端工具鏈、工程化得以發展,前端也實現 BFF(Backend For Frontend)層,這樣的架構設計好處顯而易見:
+ 前端工程師可以自行編寫后端服務,實現數據的適配,應用場景包括接口的整合編排、字段裁剪;
+ 前端工程師可以實現SSR(服務端渲染直出)技術,達到提升首屏性能以及 SEO 友好的目的;
+ 前端工程師可以實現各種后端領域服務。
為了“緊跟技術潮流”的發展,Vue 和 React 等當紅框架依靠虛擬 DOM 技術,推出同構方案。SSR 架構模式橫空出世,成了前端技術演進的新方向。
但是 Node.js 技術不是銀彈,SSR 架構也不是毫無成本。前端工程師落地 Node.js 技術,就要關心服務器的運維、部署、發布、監控。有沒有一種“just work”的技術,使得我們能夠更輕松地專注前端業務代碼的開發,直接上手 Node.js 呢?
為了解決上述問題,Serverless 理念應運而生。簡單來說,我們可以將服務器的運維功能都交給 Serverless 平臺進行管理,研發人員只需要專注于實現云函數即可完成功能開發。
你看,短短十多年,前端技術發展和演進史已經非常精彩。其實這段演進當中,也有諸多值得關注的里程碑和代表技術理念,比如以下幾點。

1. 以 GraphQL 技術為代表的數據源聚合和字段裁剪方案
2. 以組件化架構為代表的 UI 搭建技術,在 UI 搭建技術里面,我們也可以總結出一個微觀技術方向:
+ 以原子組件為基準的組件化方案(Ant Design、Element)
+ 以模板庫為代表(Ant Design Pro)的一體化組件化方案
+ 以 No code/Low code 為代表的配置化解決方案
+ 以機器學習智能化為代表的搭建方案(設計圖 → 代碼直出)
3. 以微前端為代表的、前端應用聚合為單體應用的工程方案
4. 以 PWA、小程序、快應用等為代表的平臺化方案
5. 以 PhoneGap → Ionic → React Native → Flutter 等演進方向為代表的移動端跨端方案
總之,前端技術發展從沒有一刻停歇,而在技術架構演進的過程中,需要前端開發者不斷保持進步和學習。其中,對于基礎建設和架構設計的學習,將會是最核心、最重要的學習方向和目標。
下面,我們簡單了解一下現代技術架構。
現代化的前端技術架構解讀
一方面,前端領域的現代技術架構,永遠無法脫離應用終端和宿主。這其中:前端不再局限于 PC 和移動智能手機端,智能手表、眼鏡會是新的平臺方向,同時文件系統、相機、PWA 和硬件傳感器等新型 API 都已經應用在 Web 前端當中。
第二方面,現代 JavaScript 也發展成為一種真正成熟的語言,并且還將會持續引入新的特性和功能。同時TypeScript,甚至 Elm、PureScript 和 ReasonML 將會得到更多關注。因此,一套現代化的前端方案,必然要處理語言的發展和宿主的碎片化、滯后性這一矛盾,也必然會有一個更厚重的編譯。
第三方面,網絡基礎設施永遠都在變得更快、更穩定,流媒體和視頻點播成為日常,前端的用戶體驗和富媒體技術愈發成為應用的關鍵。
基于上述背景,現代化前端技術架構的特點呼之欲出:
+ 組件化是基本 UI 架構;
+ 依托于 SSR 同構技術以及心智負擔的最小化,框架層面提供的虛擬 DOM 會成為生態標配;
+ 數據狀態管理方案將會以職責單一、minimal necessary 為目標,以組合性、函數式為理念,而不以雙向數據流和單向數據流的區分為重點;
+ 前端向傳統后端領域進軍是必然,一個 CSR/SSR 可切換的協作方案可以把前端優勢特點放大到最大。
總之,基礎建設和工程化建設、代碼設計和架構之道,也將圍繞以上幾個特點給出答案。我們的課程也會圍繞這些方向展開。
從 CSR → SSR → NSR → ESR 渲染方案演進看前端架構演進方向
上面我們從宏觀的角度闡述了前端技術架構方向。這一部分,我會以前端渲染架構為例,從真實的技術環節、更立體地說明架構演進。我將以 CSR → SSR → NSR → ESR 方案來進行講解。
CSR:Client Side Rendering,瀏覽器端渲染也許是大家最為熟悉的渲染架構。這種渲染架構很好理解,如下圖所示:

CSR 渲染架構圖(圖片來源:https://developers.google.com/web/updates/2019/02/rendering-on-the-web)
CSR 渲染架構的特點非常明顯:
+ 實現了前后端架構分離,實現了前后端職責分離;
+ TTFB 時間最小,但由于客戶端和服務端會有多次交互(獲取靜態資源、獲取數據)才能進行渲染,實際首屏效果以及 FCP/FMP 時間不夠理想。

CSR 渲染時序圖(圖片來源:https://developers.google.com/web/updates/2019/02/rendering-on-the-web)
我們可以通過代碼分離等技術彌補實際內容渲染的滯后,但從渲染架構上講,CSR 卻有著基因上的弊端。
SSR:Server Side Rendering,在服務端完成頁面模板、數據預取、填充,并且在服務端就可以將完整的 HTML 內容返回給瀏覽器。如下圖:

SSR 渲染架構圖(圖片來源:https://developers.google.com/web/updates/2019/02/rendering-on-the-web)

SSR 渲染時序圖(圖片來源:https://developers.google.com/web/updates/2019/02/rendering-on-the-web)
實際上,SSR 還涉及更多內容:我們在服務端預取了數據,并返回了數據和 HTML 內容。理想情況下,不需要在客戶端再次請求數據,而是直接消費數據即可。因此我們可以將 SSR 和 CSR 相結合,即實現一個基于 hydration(注水) 的 SSR 和 CSR 結合方案。
先來解釋一下 hydration,這個概念和同構應用中數據的獲取和消費有關。在服務器端渲染時,首先服務端請求接口拿到數據,處理并準備好數據狀態(如果使用 Redux,就進行 store 的更新)。
為了減少客戶端的請求,我們需要保留住這個狀態。一般做法是在服務器端返回 HTML 字符串的時候,將數據 JSON.stringify 一并返回,這個過程,叫作脫水(dehydrate);在客戶端,就不再需要進行數據的請求了,可以直接使用服務端下發下來的數據,這個過程叫注水(hydrate)。
用代碼來表示,我們將數據放到 window 變量上:
```js
ctx.body = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<script>
window.context = {
initialState: ${JSON.stringify(store.getState())}
}
</script>
<div id="app">
// ...
</div>
</body>
</html>`
```
對應客戶端:
```js
export const getClientStore = () => {
const defaultState = JSON.parse(window.context.state)
return createStore(reducer, defaultState, applyMiddleware(thunk))
}
ReactDOM.hydrate(<App date={getClientStore().getState()} />, document.getElementById('root'))
```
而基于 hydration 的 SSR 方案,如下圖代碼:

我們可以將上述渲染架構方案用下面這張圖來總結:

SSR 渲染架構方案圖(圖片來源:https://developers.google.com/web/updates/2019/02/rendering-on-the-web)
其實,如果將性能優化做到極致,SSR 還可以發展為:Streaming server rendering(流式 SSR 渲染)或 Progressive Rehydration(漸進式 SSR 渲染)。
流式 SSR 渲染,允許服務端通過 stream 的方式向瀏覽器發送 HTML 內容。在 React 中,我們可以使用renderToNodeStream()方法來完成流式 SSR 渲染。
漸進式 SSR 渲染可以允許在 hydrating 沒有完全結束前,部分已經渲染并注水完成的頁面內容,可以優先完成交互響應。React 專門將Partial Hydration開了一個 PR 來討論。
在 SSR 技術下,還有類似 Bigpipe 的 Partial Rehydration 技術以及借助 Service Worker 完成的 Trisomorphic Rendering 技術,這里我們不再一一討論。
說完 SSR,我們再來看一些更新潮的渲染技術:NSR 和 ESR 渲染方案最近幾年也正在逐漸落地實施。
NSR:Native Side Rendering,這是一種在 hybrid 中特有的渲染技術。簡單說就是通過 Native 渲染生成 HTML 數據,并且緩存在客戶端。這樣一來,對于一個 hybrid WebView 的用戶訪問,會優先從離線包中加載離線頁面模板,再通過前端 Ajax/或客戶端能力請求數據,最終完成頁面完整的展示。
這樣做的好處顯而易見:我們將服務器的渲染工作放在了一個個獨立的移動設備中,并借助離線存儲技術,實現了頁面的預加載,同時又不會增加額外的服務器壓力。
ESR:Edge Side Rendering,邊緣渲染則更加激進。ESR 其實借助了最近幾年較火的“邊緣計算”能力。
邊緣計算,是指在靠近物或數據源頭的一側,采用網絡、計算、存儲、應用核心能力為一體的開放平臺,就近提供最近端服務。其應用程序在邊緣側發起,產生更快的網絡服務響應,滿足行業在實時業務、應用智能、安全與隱私保護等方面的基本需求。邊緣計算處于物理實體和工業連接之間,或處于物理實體的頂端。而云端計算,仍然可以訪問邊緣計算的歷史數據。
ESR 渲染利用了 CDN 能力。ESR會在 CDN 上緩存頁面的靜態部分,這樣在用戶訪問頁面時,可以快速返回給用戶靜態內容,同時在 CDN 節點上也發起動態部分內容請求,在動態內容獲取之后,利用流的方式,繼續返回給用戶。該項技術在阿里中已經有了試水,但真正更廣泛地落地和實施,有待后續驗證和觀察。總之借助邊緣計算能力,前端渲染架構的想象空間會被無限放大。
總結
這一講我們縱覽了近十多年的前端技術發展以及相關技術方案的演進過程,并以渲染架構為例,重點剖析了從傳統 CSR 到 SSR、NSR 再到 ESR 的思路。這一系列發展過程有的以基礎設施(比如網絡發展)為紅利,有的以語言或框架演進為背書。