天天看點

CodeSandBox私有化部署實踐

作者:閃念基因

CodeSandBox私有化部署實踐

原創 魏明 楊明 微盟技術中心 2023-07-21 18:10 發表于上海

收錄于合集#前端13個

前言

快速發展的前端領域,作為公司的前端工程師,經常面臨着許多挑戰,其中之一就是如何有效地預覽和調試前端物料元件。預覽元件的重要性不言而喻,它們為前端工程師提供了對使用者界面和功能的實時展示,能夠更好地了解和評估設計和實作的效果。然而,傳統的預覽方式往往耗時繁瑣,需要在本地環境中進行搭建和調試,給開發流程帶來了一定的延遲和複雜性。

為了解決這一問題,我們選擇了基于浏覽器的線上開發環境 CodeSandBox方案。CodeSandBox 提供了無需本地環境搭建的全功能開發體驗,讓我們能夠在浏覽器中實時預覽、調試和共享前端代碼,極大地提升了元件開發效率、降低接入成本。

在接下來的文章中,我們将介紹CodeSandBox的相關原理以及我們在落地方案的全過程。

二、CodeSandBox原理

2.1 簡述

CodeSandBox 是一個線上的代碼編輯器,它在浏覽器端進行項目建構而無需依賴服務 器,這是它最大的特點。

通常情況下,要在浏覽器中運作一個前端項目,需要配置 Node 環境,通過 npm 安裝一系列依賴,然後使用類似 webpack 的工具進行編譯,最後再運作一個開發伺服器才能在浏覽器中預覽項目。那麼,CodeSandBox 是如何實作這一切的呢?

本質上 CodeSandBox 實作了一個浏覽器端的 webpack ,通過其實作的 Packager 安裝所需要的依賴,在 SandBox 中通過這個 webpack 将檔案編譯,再将編譯後的檔案通過 eval 在浏覽器中運作。

2.2 組成部分

CodeSandBox的實作涉及三個主要部分:Editor、Packager和SandBox。

Editor: 用于編輯代碼和管理檔案的子產品。當代碼被修改後,通過 postMessage 通知到 SandBox,以便進行相應的處理。

Packager:運作在服務端,它是負責處理項目依賴關系的子產品。通過傳入包名及其版本資訊,Packager 會生成一個包含該依賴項所有内容的 manifest 檔案。這個manifest 檔案會被緩存在雲服務上,以便後續使用。

SandBox:一個運作在獨立的 iframe 中的代碼執行環境。它負責編譯和運作代碼。SandBox 利用 postMessage 與外部進行通信,接收來自 Editor 的代碼修改通知,并相應地處理。它使用了内部的編譯器來将代碼進行編譯,并通過 eval 函數在浏覽器中執行編譯後的代碼。

CodeSandBox私有化部署實踐

2.3 建構過程

如上文的組成關系圖所示,在 SandBox 中接收到代碼之後,就開始進行整個建構的流程,這個流程就是 CodeSandBox 的核心功能。

整個建構流程圖如下。(這裡隻是列舉了簡易流程,實際場景還有很多複雜的邏輯,如中斷建構、緩存、熱更新等等)

CodeSandBox私有化部署實踐

根據建構流程,下面來看看幾個重要的子產品,及其實作的思路。

a) Packager(依賴管理器)

盡管 npm 是個"黑洞",但作為一個前端項目,還是離不開它,如何處理項目的依賴是線上運作的第一個難題。

在 Pakcager 的實作中,借鑒了 WebpackDllPlugin 的方法,為每個依賴用”包名@版本“作為唯一辨別,生成一個 Manifest 檔案并進行緩存(後續再有同樣的依賴直接從緩存擷取),這個檔案包含了一個包的所有檔案目錄索引、依賴、檔案内容等資訊。

這個檔案的生成也是通過yarn在服務端安裝依賴,隻不過這裡為了剔除 npm 子產品中多餘的檔案,服務端還周遊了所有依賴的入口檔案 (package.json#main),解析 AST 中的 require 語句,遞歸解析被 require 的子產品,形成一個依賴圖,隻保留必要的檔案,最終輸出 Manifest 檔案,大緻内容如下:

CodeSandBox私有化部署實踐

那麼在 SandBox 接收到收到一個完整的項目檔案,其中包括 package.json,分析其中的 dependencies,剔除一些并不需要的(如@types/*)依賴,接着将每個依賴根據包名@版本生成唯辨別,去請求 Packager,拿這個辨別去請求伺服器,拿到manifest 檔案。

擷取到所有依賴的 manifest 内容,依賴安裝的部分就完成了,可以開始對檔案進行編譯。

b) Transpilation

依賴安裝完成之後,将會從入口檔案開始,對檔案進行編譯。

上文提到過,CodeSandBox 實作了一個浏覽器端的 webpack,并且在 Editor 和SandBox 通信的時候,除了檔案内容之外,還有 Template 字段,這個就是指定需要編譯的模闆。這個模闆預設了一些 loader,用來編譯指定類型的檔案,類似webpack 的配置檔案。目前 SandBox 内部預設的一些模闆,如 create-react-app、vue-cli 等。

這裡可以看一下 create-react-app 預設的部配置設定置,可以看出和 webpack 的配置很像,設定一個檔案字尾名稱檢測,指定一個 loader 來編譯目前類型的檔案。

CodeSandBox私有化部署實踐

SandBox 根據預設配置,從入口檔案進行編譯,解析 AST,找出下級依賴,并遞歸編譯。最終會形成一個依賴圖。

CodeSandBox私有化部署實踐

c) Evaluation

有了編譯完成後的檔案和依賴圖,這裡就可以運作了,從入口檔案開始,用 eval 執行,如果執行過程中有 reqiure,則遞歸 eval 依賴的子產品。

至此,整個建構流程就說完了。SandBox 從接收到代碼,再安裝依賴、編譯檔案、執行檔案,最終在 iframe 中呈現出運作之後的結果。

這其中很多細節邏輯都比較複雜,比如編譯的 loader 實作、熱更新支援等等,有興趣可以拜讀一下源碼研究研究。

三、落地實踐

了解完 CodeSandBox 的大緻實作原理,我們開始動手實踐。雖然 CodeSandBox 官網上功能大部分都是開源的,但是由于一些業務場景和限制,我們最終采用的方案是複用 SandBox 部分功能,并對其代碼進行部分改造,以便于實作我們自定義的一些需求。

3.1 實作Editor

CodeSandBox 本身內建了 vscode 的編輯器,功能強大且複雜,然而對于我們的需求來說,有點備援。于是我們使用 vscode 的簡易版(monaco-editor)實作了一個編輯器。

在物料的角度,Editor 隻需要簡單檔案編輯、切換檔案等功能。并且我們在 Editor内封裝了一些模闆,這樣我們的物料并不需要關心 create-react-app 之類的模闆檔案,隻要寫一個 example 入口檔案就可以,并且内部已經處理過 ReactDOM.render之類的方法。會直接渲染入口檔案的内容。

CodeSandBox私有化部署實踐

在和 SandBox 通信方面,使用 SandBox 開源的 @CodeSandBox/sandpack-client,這個包是專門用來處理和 iframe 通信相關的功能。

3.2 SandBox 私有化部署

CodeSandBox 不支援私有源配置,于是我們需要修改部分源碼,并将其私有化部署到我們的服務上,才能正常使用。

a) 修改Packager請求接口

首先需要将 Packager 服務也私有化部署到我們的服務上,然後修改 SandBox 中請求的路徑位址為我們的服務,這樣就擷取到私有源包的 manifest 檔案。

檔案路徑為packages/sandpack-core/src/npm/preloaded/fetch-dependencies.ts

CodeSandBox私有化部署實踐

b) 支援unpkg

CodeSandBox 中,除了會向打包服務請求擷取依賴的内容之外,還有回退方案。就是在找不到依賴的時候,會直接請求某個依賴的 unpk 位址,這裡需要添加内部的unpkg 位址邏輯并指向公司的 unpkg 服務上。

添加邏輯,請求内部的位址并拿到檔案内容。

CodeSandBox私有化部署實踐

這裡看到源碼會根據優先級找不同的 cdn 檔案,我們加上優先級,優先使用内部的unpkg 服務,如果找不到會降級去找 npm 的 unpkg 的,最後會去 jsdeliver。

檔案路徑是packages/sandpack-core/src/npm/dynamic/fetch-protocols/index.ts

CodeSandBox私有化部署實踐

修改完配置之後并 build,将輸出的 www 檔案夾部署到我們的伺服器上,其前端相關的部署就完成了。

3.3 Packager 私有化部署

CodeSandBox 的服務端 Sandpack Packager 也是開源的,下面是它的倉庫位址:

https://github.com/CodeSandBox/dependency-packager#sandpack-packager           

其内部實作了一個服務端打包的接口,并接入緩存,CodeSandBox 接入的是亞馬遜的緩存存儲服務。

CodeSandBox 用戶端拿到 package.json 之後,将 dependencies 轉換為一個由依賴和版本号組成的Combination(辨別符, 例如 v1/combinations/[email protected]&[email protected]&[email protected]&[email protected]&[email protected]&[email protected]&[email protected]), 再拿這個 Combination 到服務端請求。伺服器會根據 Combination 作為緩存鍵來緩存打包結果,如果沒有命中緩存,則進行打包。伺服器會根據 Combination 作為緩存鍵來緩存打包結果,如果沒有命中緩存,則進行打包。

CodeSandBox私有化部署實踐

其業務中代碼建構邏輯在functions/packager/r/index及installDependiceies中,下面我們就來看看如何改造相關代碼進行本地部署。

a)調整不相容windows的指令(需要在windows執行服務的有此步驟)

調試代碼時需要注意這個項目的很多指令在 Window 下無法使用的,建議用 Mac 或 Linux 運作,如果确實要在 Windows 下面運作,那就需要改造源碼。我們是在Mac 與 Linux下運作的,如果在 Windows下測試的話,建議啟動後根據指令報錯逐個修改替換或增加相容指令。

b)移除sentry相關的代碼

可在項目中檢索 config.secret,然後順着調用關系删除相關代碼。

c)S3緩存邏輯移除

CodeSandBox 的線上打包服務使用了亞馬遜 S3 緩存,我們在實際操作中使用了我們的 CDN 當緩存來用。

CodeSandBox私有化部署實踐

在打包操作前,首先從 CDN 查詢相關資源是否存在,存在則直接 JSON,不存在則發起打包操作,并将結果上傳至 CDN。

d)遷移代碼至自有工程

将打包相關代碼遷移至自有工程中,這部分内容較簡單,就不做過多贅述了。

四、物料接入

在物料平台首頁,我們展示了一個簡單的示例,一個CSS動畫的物料,我們下面就以這個例子講講物料的接入。

4.1 實時預覽能力

需要接入物料平台并預覽,首先在項目根目錄添加 example 檔案夾,編寫入口檔案,這裡有一個小細節需要注意,我們這裡的 import Bike from '@oort2/bike',這裡 @oort2/bike 是目前包名,這是被預覽的包給外部使用時的導入方式,預覽也是使用了這種形式(我們必須且隻能這麼做),實際開發調試時使用了 vite/webpack alias 重定向到了 src/index 中。

另外,目前的 SandBox 運作配置中,沒有類似 babel-plugin-import 的插件,當物料有依賴樣式檔案的時候,需要在入口檔案引入才能正常加載。

CodeSandBox私有化部署實踐

4.2 多架構支援

在 example 檔案夾下添加 perview.config.json,指定項目的模闆和入口檔案,以下是字段含義。

main:index.(tsx|jsx|vue|any): 入口檔案,可以随意命名,要和 preview.config.json 中的 main 對應上。

template:模闆,目前可以支援 vue-cli、create-react-app、solid、augular、sevlet等常用的前端架構。

CodeSandBox私有化部署實踐

4.3 上下文處理

在項目中,尤其是一些業務元件,常常會依賴架構層等提供的配置資訊和依賴,并挂載在 Window 對象上。但是在物料預覽的沙箱 Window 上,并沒有挂載這些對象,這個時候運作起來的元件可能與預期不一緻。

于是我們提供了一個上下文檔案,約定為 context.ts。在這個檔案中,可以手動将一些所需要的對象挂載到 Window 上,以便物料得以正常運作。

再通過 oort2 腳手架上傳到物料平台,在物料平台中就可以檢視到相關資訊,這時候打開物料的預覽詳情,就可以像 CodeSandBox 一樣,展示在右側。

CodeSandBox私有化部署實踐

五、總結展望

通過私有化部署和定制化配置,我們成功将 CodeSandBox 內建到我們的内部環境中。這樣,我們可以更好地控制資料安全性、自定義擴充功能,并适應我們的特定需求。我們修改了 Packager 的請求接口和 unpkg 位址,實作了對私有源包的支援。我們還将打包服務端代碼遷移到了我們的工程中,以便更好地管理和定制。

此外,我們對物料工程進行了改造和接入,使其能夠與物料平台無縫對接,并在平台上進行預覽和展示。這為我們的團隊提供了更直覺、實時的物料展示和交流平台,促進了物料的複用和協作。

CodeSandBox私有化部署實踐

在微盟,物料平台為包括裝修、融合、微盟雲在内的多個業務提供了數千個元件,基于 CodeSandBox 的私有物料元件預覽方案為我們的元件開發帶來了許多好處,降低了元件的開發、接入成本,提升了開發效率,我們将進一步完善和優化這一方案。通過持續的優化和拓展,這一方案将進一步提升我們的開發效率和協作能力,為我們的前端團隊帶來更多的價值和創新。

六、參考資料

[1]CodeSandbox 如何工作?(https://bobi.ink/2019/06/20/codesandbox/)

[2]Sandpack Packager 解析(https://cloud.tencent.com/developer/article/1794207)

[3]代碼倉庫codesandbox-client(https://github.com/codesandbox/codesandbox-client)

[4]代碼倉庫dependency-packager(https://github.com/codesandbox/dependency-packager)

作者:魏明 楊明

來源:微信公衆号:微盟技術中心

出處:https://mp.weixin.qq.com/s/Rx8_RRB69X7dBTxNEABLzg

繼續閱讀