這么多人用Codesandbox,他服務器扛得住么?
大家好,我卡頌。
codesandbox是前端工程師經常使用的「代碼在線運行環境」,頁面如下:
他的應用場景很廣,比如:
- 有代碼邏輯要分享,分享個codesandbox鏈接。
- 有新想法需要驗證,又不想本地起個項目,用codesandbox。
- 技術文檔演示Demo,用codesandbox。
作為一個在線運行代碼的編輯器,這么多人天天免費用,他服務器扛得住么?
畢竟,同樣作為在線代碼運行環境(主要是跑算法題)的leetcode[1],如果同時刷題的人多了,提交后都還得排隊:
codesandbox是如何實現的?他會面臨leetcode一樣的服務器壓力么?
codesandbox的分類
這個問題的本質其實是問 —— 用戶在codesandbox中寫的代碼,究竟是在前端還是后端編譯成靜態資源的?畢竟,如果是在后端完成,會增加服務器壓力。
比如,對于下面這段React代碼:
// main.jsx
import { createRoot } from "react-dom/client";
import { Cpn } from "./Cpn";
function App() {
return (
<Cpn />
);
}
createRoot(document.getElementById("root")).render(<App />);
要想在瀏覽器中運行,涉及幾個前置工作:
- 需要編譯JSX語法,比如將<App/>編譯為_jsx(App, {})。
- 需要解析并提前下載所有依賴,比如這里的react-dom、react包。
- 需要解析模塊依賴關系,比如main.jsx導入了Cpn.jsx中的Cpn組件。對于不支持ESM的瀏覽器,需要將代碼打包。對于支持ESM的瀏覽器,需要處理引入路徑。
- 如果涉及到其他資源,比如圖片、文字、HTML文件,需要有相應的處理。
上述工作,codesandbox是在瀏覽器還是服務器完成的呢?
在這個例子中,這些工作都能在瀏覽器完成,比如:
- 對于所有第三方依賴,可以在瀏覽器中直接請求CDN。
- 涉及編譯的工作(比如編譯JSX、模塊依賴分析),本質其實是字符串的解析,可以用瀏覽器版本的babel實現。
上面的例子是一個純前端的React項目。但有些依賴服務端環境的項目沒法采用上述方式運行,比如:
- 使用了Docker的項目。
- 類似Next.js這樣的全棧項目。
這種情況就需要一個真實的服務端環境。
兩者的區別可以用下圖概括:
- 純前端項目:編譯與執行都能在瀏覽器完成。
- 全棧項目:項目編譯在服務端進行,瀏覽器負責項目執行。
他們分別對應codesandbox的兩種運行環境:
- Browser Sandbox:基于瀏覽器的本地運行環境。
- Cloud Sandbox:基于MicroVM的云端運行環境。
當我們通過模板創建codesandbox項目時,可以通過「右上角是否有Cloud標記」區分兩者:
可以發現:
- 純前端項目(比如React項目、純JS項目)使用Browser Sandbox。
- 需要服務端運行環境(比如Docker項目、全棧框架項目)使用Cloud Sandbox。
對于Cloud Sandbox,他底層使用亞馬遜開發的Firecracker快速啟動輕量級的MicroVM,這也是AWS Lambda底層使用的庫。
所以,基于Cloud Sandbox啟動的項目確實會占用服務端資源。具體來說,每個項目會分配:
- CPU:2個虛擬 CPU(vCPUs)
- 內存:2GB
- 存儲:6GB
這塊是codesandbox公司的核心業務。畢竟,免費試用滿意后,可能就會上付費的Pro版(更多資源分配),或者團隊定制版。商業模式與Vercel類似 —— 提供免費基礎服務(自擔部分資源費用),通過增值的云服務收費。
而前端開發日常使用codesandbox創建的項目,大多數并不是基于Cloud Sandbox,而是基于Browser Sandbox啟動的。這些項目并不會給codesandbox帶來太多服務端壓力。
兩種sandbox的區別
有個很直觀的方式區分兩種Sandbox —— 當我們新建一個codesandbox項目,在預覽區域可以看到項目臨時url:
新開頁面,訪問這個url,如果請求的資源包括:
- 項目運行所需的靜態資源
webpack熱更新相關代碼
那代表這是個Cloud Sandbox項目。Cloud Sandbox在云端啟動后端服務與當前頁面通信,就類似我們本地開發時起的后端服務一樣。
如果請求的資源包括:
- 項目運行所需的靜態資源。
- sandbox初始化相關代碼。
那代表這是個Browser Sandbox項目。
「sandbox初始化相關代碼」是一個簡化版的webpack,他會在瀏覽器執行,下載依賴、編譯代碼,打包并執行代碼。
我們平時使用codesandbox時看到的如下初始化畫面就代表Browser Sandbox在瀏覽器執行相關操作。
比如,下圖是在通過CDN安裝依賴(@babel/core):
當依賴安裝完成后,下面是編譯代碼:
Browser Sandbox實現原理
Browser Sandbox相關代碼都是開源的,讓我們按照抽象程度從上往下介紹他。
首先是封裝最完整的庫 —— @codesandbox/sandpack-react。這個React庫提供了很多開箱即用的codesandbox模塊。
比如:
- SandPackCodeEditor:codesandbox左側的代碼編輯區域,底層采用的是codemirror[2]這個代碼編輯器。
- SandpackConsole:codesandbox中的控制臺。
- SandackPreview:codesandbox右側的預覽區域,會渲染一個iframe,iframe的地址對應了Browser Sandbox的執行環境。
各個組件通過postMessage與SandackPreview渲染的iframe交互。
我們會發現,codesandbox的核心實際上包含三部分內容:
- 各種編輯器相關模塊的實現(比如代碼編輯部分、控制臺、預覽)。
- Browser Sandpack運行環境,是一個獨立的網頁,在預覽模塊(SandackPreview)中通過iframe渲染。
- 1與2之間通信的協議(即頁面與iframe之間的通信協議)。
@codesandbox/sandpack-react實現了1,他依賴的@codesandbox/sandpack-client[3]實現了3。
2相關的源代碼在codesandbox-client/packages/app[4]中。將這個包的代碼部署上線后,就能獲得一個Browser Sandpack運行環境。
上面已經簡單介紹了Browser Sandpack的工作原理,再將他(2)與1、3結合起來的工作原理如下:
比如,用戶選擇React作為項目模版:
編輯項目代碼后,項目代碼與preset(類似webpack中的preset選項項,不同模版對應不同preset)會通過通信協議傳遞給Browser Sandpack頁面。
Browser Sandpack頁面通過內置的mini webpack與其他工具(比如babel),編譯并執行代碼。
代碼編譯、執行的信息也會通過通信協議傳遞回各個需要的模塊。比如,控制臺模塊可以根據type為console的信息打印消息。
總結
codesandbox有兩種代碼運行環境:
- Browser Sandpack:針對「編譯與執行都能在瀏覽器完成」的純前端項目。
- Cloud Sandpack:針對需要服務端運行環境的項目。
這兩種環境會體現為一個獨立網站,這個網站會作為iframe嵌入在codesandbox編輯器的預覽模塊中。
預覽模塊通過定義好的通信協議與其他模塊(比如代碼編輯模塊、控制臺模塊)通信。
對于Cloud Sandpack,會占用一定服務端資源。對于Browser Sandpack,則不會占用什么服務端資源,因為他大部分邏輯都是在前端執行的。
參考資料
[1]leetcode:https://leetcode.cn/problems/two-sum/。
[2]codemirror:https://codemirror.net/。
[3]@codesandbox/sandpack-client:https://www.npmjs.com/package/@codesandbox/sandpack-client。
[4]codesandbox-client/packages/app:https://github.com/codesandbox/codesandbox-client/tree/master/packages/app。