Modern.js:Hello world!

 
 
 
 

概要

​7月,字節跳動 Web Infra 做過一次主題爲《邁入現代 Web 開發(字節跳動的現代 Web 開發實踐)》[1]的分享,在分享中我們梳理了「傳統前端技術棧」的典型組成部分,展示了其中每個部分都存在的瓶頸問題。並介紹了在這些問題的驅動下,業x x x x界正在發生從「傳統 Web 開發範式」到「現代 Web 開發範式」的「範式轉移」。在這個分享的最後預告了 Modern.js 的開源項目。

10 月 27-28 日的稀土開發者大會[2]上,字節跳動 Web Infra 正式發起 Modern.js[3] 開源項目。在專場分享《介紹  Modern.js —— 現代 Web 工程體系》中,第一部分先介紹了業界和字節內部的前端開發、Web 開發在發生哪些影響深遠的變革,從這些變革的角度,展示了基於 Modern.js 的現代 Web 開發。

這些變革包括:

「更多「前端開發者」成爲「應用開發者 / 產品開發者」。」

先討論了什麼根本因素在驅動這種轉變,"Frontend Focused" 的意義,指出服務器端開發門檻不斷降低的長期趨勢、原有基建的缺陷,用 Modern.js 演示了「一體化、無服務器化的全棧開發」、「以客戶端爲中心的 Web 開發」。

「從「前後端分離」到「前後端一體化」。」

分析了「前後端分離」產生的兩種前端項目,爲什麼其中一種是「假分離」,另一種「不完整」,用 Modern.js 演示了「前後端一體化」在哪些地方帶來改變。

「Meta」 「Framework 取代傳統「前端三劍客」。」

分析了四代「前端三劍客」,以及每一代都被下一代的成員「吞併」的規律,結合字節內部的真實案例,講解了 Meta Framework 的角色。

「形成基於「前端技術」的成熟 GUI 軟件研發體系。」

先明確了「前端技術」的定義, 結合 Modern.js 的功能和設計,討論瞭如何實現「充分抽象」,才能解決 DX 和 UX 的矛盾。

「智能化、平臺化、低碼化。」

接下來第二部分系統介紹 Modern.js 的六大要素,包括:

「普及:現代 Web 開發範式。」

回顧了這種範式的 9 大主要特徵。

「核心:現代 Web 應用(「MWA」)。」

從 Universal App、一體化、應用架構、Runtime API 這四個角度來了解 MWA。

在應用架構部分介紹了 Modern.js 中 Model 的設計和背景。

「內置:前端工程最佳實踐。」

列舉了幾個典型的最佳實踐,包括 Post-Webpack Era 的新工具趨勢、Modern.js 的 Unbundled 開發,Modern.js 推薦的「CSS 三劍客」,Modern.js 微前端項目跟直接使用 Garfish 的微前端項目對比、模塊工程方案和 Monorepo 工程方案中的最佳實踐。

「包含:Web 開發全流程。」

演示了 Modern.js 在「編碼」環節的微生成器功能、在「調試環節」的微前端調試。

「提供:工程標準體系。」

「鼓勵:定製工程方案。」

末尾介紹了除已經發布的開源項目,還有哪些對現代 Web 開發者有幫助的事情在發起和推進中。也介紹了 Modern.js 當前高優的社區計劃。

 

歡迎大家掃碼入羣,一起討論交流

分享實錄

大家好,我是來自字節跳動 Web Infra 的宋振偉,在字節跳動,我們部門負責打造和發展「Web 技術中臺」和「前端研發體系」。

今年7月,我們做過一次主題是《字節跳動的現代 Web 開發實踐》[1]的分享,在分享中我們梳理了「傳統前端技術棧」的典型組成部分,展示了其中每個部分都存在的瓶頸問題。

也介紹了在這些問題的驅動下,業界正在發生從「傳統 Web 開發範式」到「現代 Web 開發範式」的「範式轉移」。

在這個分享的最後也預告了 Modern.js 開源項目。

昨天上午的主題演講[6]中,字節跳動正式發佈了 Modern.js[7]。今天的專場分享,我想結合字節內部的變革和實踐,介紹基於 Modern.js 的現代 Web 開發,和所帶來的實際效果。

議程

今天的分享可以分成三個部分。

昨天的主題演講有說到,整個業界和字節內部的前端開發、Web 開發,都在發生着影響深遠的變革,我們首先從這些變革的角度,看下基於 Modern.js 的現代 Web 開發是什麼樣子,有什麼區別。

然後,我們整體看下 Modern.js 有哪些要素和收益。

最後再看下除了已經發布的開源項目,還有哪些對現代 Web 開發者有幫助的事情在發起和推進中。

我們先來看第一部分「現代 Web 開發」

一、基於 Modern.js 的現代 Web 開發

1.1 更多「前端開發者」成爲「應用開發者 / 產品開發者」

可以從這五個方面的變革,來展示「現代 Web 開發」是什麼樣子。

這五個變革之間是承前啓後的關係。首先最根本的推動力,不是來自技術側,不是前端開發者一廂情願的發展自己主觀的技術偏好,而是在互聯網和 IT 行業、市場需求、用戶產品這一側的大趨勢,需要更多「前端開發者」成爲「應用開發者」或「產品開發者」,鼓勵和倒逼着技術領域,不斷產生更有利於這種需求的技術形態和基礎設施。

當傳統技術範式遇到瓶頸,不再能進一步適應需求,就會發生「範式轉移」,出現從一開始就針對這種需求重新設計的、新一代的技術範式。

這種轉變推動前端技術領域出現了「從分離到一體化」、新一代「前端三劍客」的變革。

這種變革帶來的新一代技術標準和基礎設施,開始形成完全基於「前端技術」的成熟 GUI 軟件研發體系,並且進一步朝着平臺化、低碼化的方向發展。

1.1.1 "Frontend Focused" 的意義

我們剛纔一直在說「前端」,「前端」這個概念似乎一直是隻有開發者才關心的技術細節,但最近幾年,卻變成了商業領域、投資機構也都很關心的事情,全球市場上湧現出越來越多的新一代雲平臺和研發工具產品,多數都涉及前端研發特有的需求和模式,其中還有很多像 Vercel 這樣明確「專注於前端」的產品。

就像幻燈片上這張圖,雲計算和研發產品最初是從最接近機器的底層開始發展,從虛擬化,到容器編排,到基於容器技術的各種平臺化、服務化的研發工具形態,這個階段是後端技術主導的,整個趨勢是越來越向上層發展,越來越接近市場和商業價值最終所在的地方——也就是面向用戶的產品,因此必然會發展到前端技術主導的抽象層,讓應用開發和產品開發能更專注於用戶需求,而越來越不需要關心服務器端的複雜性和專業技術細節。

所以市場需求會趨向於推動應用開發方式往「專注於前端」的方向的發展,專注於前端就是專注於用戶,而專注於用戶是多數企業、產品的根本利益所在

1.1.2 最大的開發者羣體

另一方面,從進入移動互聯網時代開始就不斷大幅增加的應用開發需求,現在不但沒減弱,反而還在加強,比如幻燈片上 IDC 的預測,要滿足這麼龐大的應用開發需求,傳統開發方式和人才儲備是很不夠的,需要讓儘可能多的開發者能獨立、完整的開發這些應用,而前端技術棧的開發者,正是最大的開發者羣體和技術社區。

所以在用戶、產品、市場這一側,一直有趨勢和壓力,需要更多「前端開發者」成爲「應用開發者」或「產品開發者」,鼓勵和倒逼着技術領域,不斷產生更有利於這種需求的技術形態和基礎設施。

1.1.3 服務器端開發門檻不斷降低

在這種客觀趨勢的推動下,基於 Web 技術的應用開發中,服務器端的佔比和門檻一直在不斷下降,國內大廠的中臺建設,提供了大量不跟特定客戶端捆綁、專注於數據需求和底層業務邏輯的 API,讓產品開發更聚焦在上層的客戶端業務邏輯。還有 BaaS 和基於雲函數的後端雲 Serverless,也進一步降低服務器端的門檻,讓前端開發者能更獨立的、端到端的完成產品開發。

但要進一步降低門檻,提高效率,這些基礎設施的一個缺陷就暴露出來,就是他們都把應用依賴的 API,放在應用項目之外維護,跟前端研發是割裂的。

還有一個缺陷是它們不解決 API 之外的服務器端需求,比如路由、SSR 等。

有一個字節內部的典型例子,前端開發者在自己實現的 SSR 項目中,始終用 HTTP 方式請求外網域名的 API 來獲取數據,導致 SSR 頻繁超時,HTML 響應慢嚴重影響用戶體驗。可以給前端開發者做培訓,讓他們具備足夠的服務器端開發思維和知識,知道要在 SSR 環節切換成內網的請求方式,還要考慮緩存機制等,但更根本的解決方法是屏蔽這種服務器端問題和實現細節,自動處理這些問題。

1.1.4 一體化、無服務器化的全棧開發

因此服務器端門檻不斷降低的趨勢,自然會發展到「一體化、無服務器化的做全棧開發」的階段,讓前端開發者直接開發 「接近純前端的項目」而不是 「Node.js 框架項目」,感覺就像沒有服務器一樣。

幻燈片上是這次分享中第一個 Modern.js 的 demo, 左側是一個 Modern.js 應用項目的完整目錄結構,src/ 目錄下的應用主體,可以像調用普通函數文件一樣訪問 api/ 目錄下的 BFF 函數,不需要了解網絡細節,Modern.js 會自動基於 BFF 函數的路徑、參數等自動生成 REST API,在 CSR 過程中自動請求。

接下來我們在 package.json 的 modernConfig 配置裏啓用 SSR、「差異化分發」和「自動 Polyfill」,可以看到這些功能不用增加代碼邏輯,只需要靜態開關配置。

構建後,幻燈片左側可以看到產物裏的 HTML 和 JS 都有 es6 和 es5 兩個版本,用戶訪問時應用的時候,Modern.js 的 Web Server 會根據瀏覽器 UA 選擇分發 es6 版本還是 es5 版本,也就是「差異化分發」。

右邊上面的圖是現代瀏覽器的訪問結果,不會返回任何 polyfill 代碼, 下面的圖是低版本瀏覽器的訪問結果, Web Server 會自動提供這個 UA 需要的 polyfill 代碼。

可以看到 Modern.js 不但支持一體化的開發 BFF,也滿足 BFF 之外的服務器端需求,儘可能自動利用自帶的 web server 去做性能優化和提供產品級的兼容性,同時開發體驗仍然是無服務器化的

之前我們啓用了 SSR, 左側圖上高亮的 HTML 片段,已經包含了通過 BFF 請求到的數據,會根據應用運行的方式自動選擇最高效的請求方式。

這種自動優化不會阻礙開發者對技術細節的掌控,右邊這張圖展示了 BFF 函數也會生成標準的 REST API,可以手動調用。

1.1.5 以客戶端爲中心的 Web 開發

這種一體化、無服務器化的全棧開發進一步發展,自然會得到一種客戶端爲中心的 Web 開發方式。

比如在傳統 Web 開發中,要實現常見的權限識別和重定向,除了前端頁面的邏輯,還需要在服務器端的路由中,添加實現跳轉的業務邏輯。比如圖上,在訪問 home 頁面的時候根據 cookie 的值決定要不要重定向到登陸頁面。

同樣的需求,在以客戶端爲中心的 Web 開發方式中,可以一體化的在客戶端代碼裏實現,比如前面已經啓用 SSR 的 Modern.js 項目,只需要添加 Redirect 組件,就可以實現和剛纔完全相同的權限識別和重定向效果,訪問頁面時會根據 cookie 決定要不要返回 302 狀態碼。整個實現過程是客戶端思維的。

以客戶端爲中心,不代表不能掌控服務器端,不能直接寫服務器端業務邏輯。

如果已經習慣 Node.js 框架的開發方式,可以 server 目錄的鉤子文件裏,對框架自帶的 Web Server 添加自定義邏輯,比如自由添加中間件,可以在這個局部,用自己熟悉的傳統 Web 開發方式實現權限識別和重定向。

1.2 從「前後端分離」到「前後端一體化」

Web 項目的技術棧也在轉變,相當於是先發展出「前後端分離」,然後又用新方式迴歸了 「前後端一體化」

1.2.1 「前後端分離」

以前的 Web 開發就像圖上這個 Ruby on Rails 項目,圖中粉色的前端代碼,「寄居」在圖中綠色的後端 Web 框架項目中的,前端和後端會互相干擾互相拖累,做工程建設也比較麻煩。

之後 Web 開發普遍轉變到「前後端分離」的模式,分離後的前端項目和後端項目,都傾向特定的類型。

後端項目不傾向包含 Web 的功能,而這時的前端項目可以歸納爲兩種類型。

MERN 這種項目類型相當於又回到了分離前的狀態,整個項目是基於 Node.js 框架的,前端被嵌在裏面。這種結果其實反映出「前後端分離」實現的更多是分工上的分離,而不是技術架構上的分離,在技術架構上仍然沒有擺脫以服務器端框架爲中心的 Web 開發。

從 MERN 項目的結構可以看到,它不但是假分離,而且也不算一體化,React 代表的前端部分和 Node.js 框架代表的後端部分,在項目裏是涇渭分明的,沒有真正融合到一起去。

使用 Node.js 框架的項目,多數屬於這個類型。

「前後端分離」模式中另一種前端項目類型,我們稱作「老一代 JAMstack」,這種類型沒有假分離問題,就是純粹的前端項目。可以實現 SPA 和 MPA,也能基於編譯工具實現 SSG(靜態網站生成)。靠靜態託管來運行,鼓勵在 CSR 中調用 API 滿足動態的應用需求。

「老一代 JAMstack」最大的問題是,雖然分離成了獨立的項目,卻不足以承擔完整的應用開發,只能產出靜態文件,依靠外部的 Web 服務器去運行,無法實現 SSR,三大組成部分裏的 API,也需要在項目之外,用雲函數、獨立後端項目等方式來實現,不能跟着項目一起迭代。

用 CRA 或直接用 webpack 搭建的項目,多數屬於這個類型。

1.2.2「前後端一體化」

在前面說的需要更多「前端開發者」成爲「應用開發者」的背景下,新一代的 JAMstack 項目用「客戶端爲中心」的「前後端一體化」方式,解決了上面說的問題

新一代 JAMstack 的三大組成部分雖然沒變,對應的內容卻有很大變化,JS 部分更加函數化和組件化、以 JS 爲中心,HTML 可以完全不在項目中出現,自動生成。BFF API 變成項目自包含。和之前簡單的靜態託管相比,基於前端 Serverless 平臺可以實現 SSR、SPR 等動態能力,即使是靜態頁面,也可以獲得很多好處,比如前面展示的 「差異化分發」。

圖上是用 Modern.js 的 demo 來展示新一代 JAMstack 項目。在開發中只需要聚焦在 JS 代碼上,不論是 SPA 還是 MPA,HTML 都是自動生成的。不論是 SSR 渲染的代碼,還是 API 邏輯,構建之後按照規範輸出到 dist 下的不同目錄,構建產物規範是 Serverless 友好的,支持把 Web、SSR、BFF 等拆分成不同服務器。

前面提到 Modern.js 傾向於 JS 爲中心、自動生成 HTML。但不阻礙開發者自己掌控 HTML。圖上是 Modern.js 渲染 HTML 的默認模板。

一體化 BFF 的調用,在前面的例子演示過,這裏可以看到 BFF 函數的文件路徑是有約定的,可以實現任意設計的 REST API。

構建產物會針對 BFF、Web、SSR 分別生成獨立可運行的 Server,這是對前端 Serverless 平臺更友好的,Serverless 平臺可以自主選擇讓 BFF、Web、SSR 用不同方式在獨立進程中運行,不會互相干擾。比如在 SSR 環節遇到 app 代碼的的內存泄露導致 SSR 超時,Web Server 不受影響,可以自動降級到 CSR 模式,返回靜態的 HTML 給用戶作爲兜底,用戶的 HTML 請求始終不會超時或掛掉。

對於 SSR,同樣可以前後端一體化的開發,圖裏高亮的 useLoader 函數中的代碼,同時適用於 SSR 和 CSR,如果這個 Loader 在 SSR 中已經預加載,CSR 就會自動跳過,否則會執行。

SPR 相當於有緩存機制的 SSR,在 Modern.js 裏也可以一體化的開發,只要使用這個預渲染組件。

SSG 實際上就是在編譯時運行的 SSR,在 Modern.js 裏只要配置 SSG 路由,就會自動啓用這種編譯邏輯,給路由生成靜態 HTML。CSR、SSR、SSG 都是用同一份代碼。

1.3 新一代「前端三劍客」和 Meta Framework

除了在技術棧層面向「前後端一體化」轉變,在工程層面,傳統的「前端三劍客」也在轉變成元框架這種新的工程基建。

1.3.1  傳統「前端三劍客」

先來回顧下傳統的「前端三劍客」,第 1 代和第 2 代如圖上所示,也被大家熟知。而第 3 代「前端三劍客」由視圖框架、Node.js 命令行、Node.js 框架三個方向組成。

其中 Node.js 命令行代表了工程化,其中最典型的是像 Webpack 這樣的打包工具,以及 Babel、PostCSS 這樣的編譯工具。

視圖框架和 Node.js 框架很好理解,就是之前討論的 MERN 項目中的前端和後端部分

1.3.2  第 4 代「前端三劍客」

隨着現代 Web 開發範式的發展,第4代「前端三劍客」的輪廓已經越來越明顯,由元框架、前端 PaaS、低代碼三個方向組成。其中低代碼方向在昨天晚上稀土大會的低碼專場已經介紹過,而 Modern.js 就屬於元框架這個方向。

從這張圖可以清楚的看到,每一代前端三劍客中,都有一個方向,把上一代前端三劍客完整包含在自己裏面,變成不需要太關心的底層,讓自己取代他們成爲前端開發的新地基。

第 3 代中的視圖框架,就扮演這樣的角色,把第二代的 HTML、CSS、JS 封裝在自己裏面,而第 4 代中的元框架,又對視圖框架、Node.js 框架、Node.js 命令行做了整合和抽象,成爲前端開發和工程建設性起點,元框架扮演了過去 Webpack、React 扮演的角色

這張 JS 框架的 S 曲線圖,也能體現這種轉變。在左邊這個時期,發展的前沿、開發的起點,都是 React、Vue、Svelte 這樣的視圖框架,新的視圖框架項目也層出不窮。現在已進入右邊這個時期,前沿收斂到基於 React 發展更上層的元框架。

Modern.js 作爲現代 Web 工程體系,是由元框架組成的,提供三大工程類型,鼓勵開發者基於工程類型建設自己的業務工程方案。

以字節內部的「火山引擎子應用工程方案」爲例,初始化的目錄結構沒有什麼變化,只在配置中默認加載了自己的框架插件,插件中通過 server 提供的 hook 修改渲染後的 HTML ,在原來的 HTML 上套了層殼,也就是右下角截圖中火山引擎統一的頂欄和左側導航欄。

這樣建設出來的工程方案,既能滿足垂直場景的需求或自己的偏好,又能保持跟三大工程類型的兼容,自動獲得 Modern.js 的能力和收益

1.4 基於「前端技術」的成熟 GUI 軟件研發體系

在「前端開發者」成爲「應用開發者」的大背景下,技術棧、工程基建的發展,開始形成基於「前端技術」的成熟 GUI 軟件研發體系。

1.4.1  什麼是「前端技術」

先明確一下我們一直說的前端技術,不是指做 UI 的技術,而是由 Web 原生語言、Web Runtime、Web 技術生態組成的技術棧,不是隻在瀏覽器裏纔有前端技術,而是有 Web Runtime、有 Web 語言的地方,就有前端技術

1.4.2  DX 和 UX 同樣重要

傳統前端開發不是成熟的軟件研發體系,缺乏足夠的抽象和基建,導致 DX 和 UX 始終存在矛盾,此消彼漲。以往的產品開發中,習慣更重視 UX,這有兩方面的原因,一來是因爲產品是由產品主導,因此更重視 UX, 不會過多關注開發者體驗,再就是缺乏足夠的抽象和基建,導致 DX 和 UX 之間必須犧牲一個。

在新一代更成熟的研發體系的支持下,已經可以實現 DX 和 UX 的同時最大化了,也從「更重視 UX」轉變爲「DX 優先」的方式

要實現 DX 和 UX 的同時最大化,需要充分的抽象。比如前面提到的 Modern.js 的這個例子,項目裏只有三個文件,就具備全面的能力,包括自動 Polyfill、差異化分發、SSR 等,既具備產品級的 UX,有保持了 DX 的簡單、開箱即用等

1.4.3  充分抽象

\

要實現充分抽象,需要讓項目從基於「庫、工具」發展成基於「框架」,這兩者的區別在圖上表現的比較好。藍底白邊部分是項目開發者自己寫的代碼。左邊是傳統前端項目,由開發者手寫整個應用,把庫和工具當做積木來組裝,填補項目裏的空白。而右邊是 Modern.js 項目,整個應用是框架本身,開發者手寫的代碼,是按照框架的要求填充到框架預留的位置上

要實現充分抽象,也需要在儘可能多的環節實現最大化的抽象,圖上體現了 Modern.js 除了像常規的框架一樣,在運行時和編譯時做抽象,也會在 IDE 編寫代碼的環節,和部署產物的環節,引入最大化的抽象

\

要實現充分抽象,也需要解決前端模板的問題,Modern.js 把各種研發場景、項目類型,收斂和標準化成了始終固定的三個工程類型,其中「應用」工程方案,也就是 MWA,支持所有需要部署和運行的項目,「模塊」工程方案支持所有需要實現代碼複用的項目

1.5 智能化、平臺化、低碼化

Modern.js 代表的現代 Web 開發,也在繼續朝着智能化、平臺化、低碼化的方向發展

智能化方面,當前可用的功能,是用 Modern.js 的初始化工具創建的項目,會開箱即用的在 VSCode 裏做好配置,啓用幾千條規則組成的 ESLint 全量規則集,加上按最佳實踐內置在 ESLint 裏的 Prettier,期望儘可能多自動修正問題,而不是僅僅提示問題。也追求儘可能多的讓 IDE 負責生成真正的源碼,讓開發者手寫的代碼變成跟 IDE 溝通的語言

在平臺化方面,Modern.js 的目標之一就是形成「工程標準」,讓各種前端 PaaS 平臺可以圍繞標準實現高級能力,比如圖上粉色部分列出的產品級 Web Server、差異化分發、SSR 兜底、ESR、微前端等,都需要結合代碼層面的工程標準。

除了部署運行環節方面的平臺,有了工程標準之後,研發環節也可以引入更多低碼提效。

目前我們內部使用的研發平臺,可以直接在圖形界面上簡單操作完成 項目的創建、開發、部署。圖上右側可以看到圖形界面上展示了當前項目的的狀態: 入口數量、項目配置等,藍色框內添加應用的入口,一鍵從 「單入口」 轉變爲 「多入口」 。右側在 Web IDE 中也能看到 src 目錄結構下的變化。

低碼化有兩個方向,一個是剛纔說的跟研發工具結合,另一個就是研發從某些工作中解脫出來的低碼搭建,昨天的低代碼專題中有介紹

總結

到目前爲止,我們從這些變革的角度,展示了很多 Modern.js 的 demo 和效果

二、Modern.js 的六大要素

接下來我們系統的看一下 Modern.js 是什麼,Modern.js 提供了什麼。

2.1 普及:現代 Web 開發範式

可以用這六大要素來說明 Modern.js 。

首先這個項目是希望能推動現代 Web 開發範式的普及,發展完整的現代 Web 工程體系,突破應用開發效率的瓶頸。

前面討論「現代 Web 開發」的時候,已經展示過這種範式的 9 大主要特徵。

其中 Serverless 範式、平臺化、低碼化這三個特徵,在當前版本的 Modern.js 裏還沒什麼體現,需要後續會跟一些平臺配合提供。

2.2 核心:現代 Web 應用(MWA)

繼續看第二個要素。Modern.js 三大工程類型中最核心的就是 「現代 Web 應用」,簡稱 MWA,或直接叫「應用」。

2.2.1 從 Universal JS 到 Universal App

前面提到過,「應用」工程方案支持所有需要部署和運行的項目,把這些項目收斂成用同一套框架、同一套約定、同一套模板、同一套架構、一套 API 來開發。

反過來,我們也可以從 Universal App、一體化、應用架構、Runtime API 這四個角度來了解 MWA。

Universal JS 指同一份 JS 代碼,既能在瀏覽器端運行,也能在服務器端運行。Universal App 是它的進一步發展,同一份 App 代碼可以在不同環節運行,也可以用不同的模式來運行。

首先是常見的 MPA 和 SPA 的需求,本質上是「服務器端路由」和「客戶端路由」的需求。

在 Modern.js 裏它們可以隨意組合。

我們之前的例子都是單入口應用,只需要把 App.tsx 組件、pages 目錄這樣的入口標識放到 src 的子目錄裏,就能將單入口應用變成多入口的 MPA。會基於入口名稱,自動生成服務器端路由,比如圖上的 admin-app 和 landing-page 兩個入口的 URL。

admin-app 和 landing-page  也分別都是 SPA,根據入口標識不同,一個使用基於組件的客戶端路由,一個使用基於文件系統的客戶端路由。

然後是 MWA 中的「動靜一體」

之前演示過,一個靜態的 CSR 項目如何直接開啓 SSR、SPR、SSG 功能。

Modern.js 也支持 CSR 和 SSR/SSG 混用,比如圖上右側紅色高亮部分會在服務端被渲染到 HTML 中,藍色區域的日期時間,在 CSR 階段動態展示在頁面上。也就是整體 SSR + 局部 CSR。

整體 CSR + 局部 SSR的能力後續會加入。

在 BFF 支持方面,Modern.js 中還提供了類型友好的方式,可以通過 Type Schema 實現運行時自動校驗接口的參數和返回值。比如右下角請求時,參數 text 類型爲 number 時,response 中會自動提示相應的錯誤。

Modern.js 還支持不同類型應用的開發和運行。

Modern.js 原生支持微前端,底層解決方案是 Web Infra 之前開源的 Garfish 微前端解決方案。一個 MWA 可以隨時變成微前端主應用,在配置中指定子應用列表的加載地址,Modern.js 就會自動在 Web Server 中預加載子應用數據,注入到運行時。在 Runtime API 的幫助下,可以像普通 React 組件一樣使用子應用。

MWA 也可以分別作爲獨立的 Web 和微前端子應用的運行和部署。

MWA 在啓用 Electron 支持之後,能作爲桌面應用來運行,項目裏會新增 electron 目錄用於寫主進程相關代碼。除了開箱即用的 Electron 構建等能力,也提供運行時 API 支持 Electron 的常見需求和最佳實踐,進一步提升開發效率。

2.2.2 前後端一體化

第二個看待 MWA 的角度是 「前後端一體化」

之前已經演示過 BFF 函數,api/ 目錄下每個文件就是 BFF 路由,當服務器端邏輯更重的時候,可以加入 Node.js 框架元素,目前支持了 4 種不同的框架,還可以自己開發 Modern.js 插件支持更多框架。

前面提到過:以客戶端爲中心,不代表不能掌控服務器端,不能直接寫服務器端業務邏輯。

比如之前演示過的火山引擎子應用,除了通過框架插件來實現,我們也可以在項目裏創建 server 目錄和鉤子文件,添加修改 HTML 渲染結果的邏輯。

\

在只有 src 目錄,或有 api 目錄的情況下,MWA 類似 JAMstack 項目。

如果增加了 server 等鉤子文件,MWA 就能像傳統 Node.js App 一樣直接寫服務器端業務邏輯,使用 Node.js 框架插件、中間件等。

如果刪掉 src 目錄,MWA 就是一個純 REST API 的項目。

我們把這三種模式之間隨意遷移的能力,稱作「三位一體」

2.2.3 應用架構

接下來,我們從「應用架構」的角度看看

傳統 Web 開發中的應用架構,等同於服務器端應用架構,前端部分的架構要麼缺失,要麼需要項目開發者自己摸索、搭建,缺乏 API 支持和一致的抽象,難以跨項目複用業務邏輯。

如上圖,MWA 提供的開箱即用的、客戶端爲中心的應用架構,可以通過標準化的 Runtime API,輕易實現 React 開發中缺少的 Model 層和 Controller 層。Model 作爲封裝 UI 無關業務邏輯的積木,跟 UI 組件一樣可以複用和組裝。

之前 Web Infra 舉辦的 React 核心開發者在線訪談裏,Redux 作者 Dan 提到,狀態管理最重要的是理解狀態的類型,根據需要處理的狀態是什麼種類,來選擇對應的方案,

常見的狀態管理方案,都有適合的狀態類型和場景,很多時候需要混合使用,而不是一把錘子錘所有釘子,要麼所有狀態都放到全局應用狀態裏,要麼所有狀態都在局部狀態裏。

\

很多開發者不用 Redux,是因爲 Redux 本身只能算底層 API,需要手動創建和維護 store,業務邏輯被 reducer,action 等分散在不同的地方,提高了維護成本。其實 Redux 社區一直有解決方案,比如 Ducks Modular 設計模式會把業務邏輯聚集在一起,Redux 官方支持的主流庫 RTK 也爲解決這樣的問題而生。

Modern.js 的 Model 基於 Redux 進一步提高抽象程度,保留了 Redux 在不可變數據、數據流等方面的收益,對整個 Redux 生態兼容,讓使用和不使用 Redux 的開發者都能受益。支持多種狀態類型,也支持不同的 Model 寫法

2.2.4 Runtime API 標準庫

最後看下 MWA 的 Runtime API 標準庫。

相當於「應用」層級的基礎 API,不止能在 MWA 裏使用,在 Modern.js 的模塊工程方案裏,同樣可以使用這些 API,開發可複用的業務組件,支持獨立調試和測試。

圖中最上面藍色方塊是業務開發中常用的 API,比如 useLoader,useModel 等 API。中間綠色部分就是前面提到的定製 web server、BFF 函數需要用到的 API,最底層的插件 API,是整個框架的基礎,框架裏所有的包都是用插件 API 來實現的,也可以用插件 API 來擴展框架、定製工程方案。粉色部分包括很多重要的工具 API,比如 useLocalModel。

\

當應用中的組件需要拆分成獨立的模塊複用時,實現中用到的 Runtime API  還能正常調試、測試嗎,答案是肯定的,這套 API 相當於「應用」領域的 API 標準庫,不止能在 MWA 裏使用,在 Modern.js 的「模塊」工程方案裏,同樣可以使用這些 API,開發可複用的業務組件,支持獨立調試和測試。上圖右側是模塊工程的目錄結構。左側 TableList 組件中使用了 useLoader API,調試時只需要提供對應的 story 文件,模塊工程方案支持我們在 Storybook 可視化測試以及單元測試中測試使用了 Runtime API 的組件。

2.3 內置:前端工程最佳實踐

對於第三個要素,簡單列舉幾個 Modern.js 內置的前端工程最佳實踐

2.3.1 Post-Webpack Era

傳統前端工程建設都是基於 Webpack 的配置封裝, Webpack 配置複雜和編譯緩慢的問題,大家應該都有比較深的感受, 但是從去年開始業界湧現很多新的工具,完全不涉及 Webpack,比如 Snowpack、 Vite、wmr 等,有人把它稱爲 JS 第三紀元。

從第三紀元開始 esbuild、swc 這種編譯打包工具使用非 JS 的系統編程語言開發,顯著提高編譯速度。編譯時間的縮減也意味着不打包,按需編譯的 ESM 場景可以實現,

Snowpack、Vite 這樣的工具,就是在 esbuild 的基礎上實現的、開發者體驗優先的、不打包的開發調試模式。在 Modern.js 中不打包的模式目前已經被用於公共庫的構建、業務項目的開發調試等真實場景。

Modern.js 中也內置類似 Snowpack、Vite 的不打包開發調試模式,圖中左側啓用該功能之後,運行效果就像圖中右側那樣,開發服務器在秒級啓動。

爲什麼可以做到速度這麼快?主要是因爲業務代碼只有在請求時使用 esbuild 按需編譯,第三方依賴自動從 Goofy PDN 加載已經預編譯好的產物。

2.3.2 CSS 最佳實踐

在 CSS 開發方面,Modern.js 默認推薦圖上「CSS 三劍客」搭配使用,有需要也可以開啓 LESS/SASS 等預處理器和 CSS Modules 支持。

2.3.3 默認零配置、樣板文件最小化

和以前把功能作爲樣板文件塞到項目裏相比,現代 Web 開發範式下的最佳實踐是默認零配置的,同時樣板文件儘可能簡潔最小化。之前我們也通過例子看到 Modern.js 項目手動創建非常簡單,只需要應用根組件和 package.json。

跟直接使用 Garfish 開發微前端主應用的項目做對比, 上圖可以看到直接使用 Garifish 的項目,需要手動運行 Garfish 框架、處理公用模塊、路由等邏輯。除了運行時,還需要編譯環節自定義一大堆配置。直接使用還是有一定的成本。

\

之前的例子已經說過,在 Modern.js 中使用微前端,只需要在 web 應用的基礎上啓用微前端功能,提供子應用列表即可,每個子應用加載後就是組件,路由可以自己靈活組織。

2.3.4 構建產物規範

Modern.js 的模塊工程方案,會並行編譯出多種符合社區主流規範的構建產物。

模塊的編譯也是不打包的,更容易引入速度更快的工具比如 esbuild、swc 等。

2.3.5 Monorepo 工程方案

應用和庫如果分散在不同項目中開發的話,通過 npm link 調試也比較麻煩,業界的主流方案通過 Monorepo 管理多個子項目, Modern.js 本身就是基於 pnpm monorepo 開發的,同時也將這部分最佳實踐收斂到 monorepo 工程方案,默認使用 pnpm 進行包管理, 左側是它的目錄結構, apps 對應的是 MWA 應用 、features/packages 對應是可複用的模塊。

右側內部模塊指的是不會發布到 npm、僅在當前倉庫下複用的庫。它本身不需要構建,同倉庫下的應用直接使用它的源碼即可。monorepo 我們也提供了 new 命令,可以選擇創建應用或者模塊。

2.3.6 更多

除了上面提到的一些最佳實踐,Modern.js 還提供了單元測試、集成測試、Visual Testing 等、ESlint 全量規則集等最佳實踐。這裏不一一展開介紹。可以查閱 Modern.js 文檔進一步瞭解。

2.4 包含:Web 開發全流程

Modern.js 不只是在上述運行時、構建、調試等方面提供了支持,它本身就覆蓋了 Web 開發的全流程。

在編碼環節,可以通過微生成器啓用某個功能或者添加入口,從 SPA 遷移到 MPA。和前面提到的通過研發平臺 「低碼提效」類似,還可以像圖上那樣在項目目錄下執行 new 命令選擇要啓用的功能。這個命令會自動重構我們的代碼。

通過微生成器按需自動啓用的方式,可以放心的將一些功能作爲插件提供,也可以控制 Modern.js 初始化項目的體積。

在微前端子應用開發時,通常情況下主應用已經部署上線了,這時候開發子應用就需要結合線上的主應用一起調試,解決方式之一是通過全局代理子應用 JS 到本地,比較麻煩。

在 Modern.js 中,只需要主應用像圖中右下角那樣啓用 DEBUG 模式,之後打開主應用線上鏈接,在 header 中設置需要開發的子應用信息,server 會自動替換注入到 html 中的子應用列表數據。這樣也就可以讓線上主應用加載本地子應用。

在運行環節,傳統的 Web 開發模式,通常沒有提供生產環境運行項目的方式,MWA 項目本身自帶產品級的 Server,自己就能產品級的運行自己,比如圖上的自動 Polyfill 服務。之前也提過,結合 serverless 平臺,可以自動做一些優化,也可以在本地運行模擬生產環境的效果。

2.5 提供:工程標準體系

Modern.js 不只是一個現代 Web 應用開發框架,而是提供了整套的現代 Web 工程體系。

前面已經介紹過,我們將前端開發中涉及的場景收斂到 3 種:應用、模塊和 monorepo。

不僅解決了業務模板數量爆炸的問題。融合後的工程類型,比如 MWA 不是多個場景簡單疊加,導致工程變的大而全,通過抽象可以做到很輕量,也能更容易交付一些之前不好實現的功能。

2.6 鼓勵:定製工程方案

Modern.js 鼓勵業務結合自身場景定製垂直的工程方案。

就像前面提到的火山引擎例子一樣,封裝插件、微生成器、定製出自己的業務工程方案。

關於 Modern.js 六大要素的更多解釋和例子,可以到 Modern.js 官網進一步查閱。

三、Modern.js 社區和現代 Web 研發體系

最後我們一起看下除了已經發布的開源項目,還有哪些對現代 Web 開發者有幫助的事情在發起和推進中。

Modern.js 開源項目現在是剛起步的狀態,昨天上線的官網,以及最新發的 1.0 版,都是公測狀態,還需要更多意見、測試和實踐,希望大家多參與社區建設。

Modern.js 的起點是字節內部的現代 Web 工程體系項目,現在大部分代碼已經完全轉到 Github 上開發,工作流還在建設中。

雙月計劃、每週計劃、缺陷管理等,也都會全面轉到 Github 上公開推進。

當前版本還沒有包含 Roadmap 上一些重要功能,計劃以每週發版的節奏,把這些功能補上。

昨天的分享介紹了 「現代 Web 研發體系」中的其他部分,這些部分也都算是 Modern.js 的重要功能,後續會陸續對外開放,歡迎大家關注。

最後,歡迎大家掃碼入羣交流,也可以在官網上通過快速上手和實戰教程瞭解更多 Modernjs 的細節使用部分。\

謝謝大家。

官網:https://modernjs.dev/

Github: https://github.com/modern-js-dev/modern.js

Reference

[1]《邁入現代 Web 開發(字節跳動的現代 Web 開發實踐)》:https://zhuanlan.zhihu.com/p/386607009

[2]稀土開發者大會: http://conf.juejin.cn/xdc2021

[3]Modern.js: https://modernjs.dev/

[6]昨天上午的主題演講: https://conf.juejin.cn/xdc2021

[7]Modern.js: https://modernjs.dev/

點擊直達官網,瞭解更多信息。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章