本文做者:唐江洪[1]css
背景
這幾個月在公司內作一個跨前端項目之間共享組件/區塊的工程,主要思路就是在 Bit[2] 的基礎上進行開發。Bit 主要目的是實現不一樣項目 共享 與 同步 組件/區塊,大體思路以下:html
在 A 項目中經過執行 Bit 提供的命令行工具將須要共享的組件/區塊的源碼推送到遠端倉庫,而後在 B 項目中就能夠一樣經過 Bit 提供的命令行工具拉取存儲在 Bit 遠程倉庫的組件/區塊。聽起來比較像 Git,主要的區別是 Bit 除了推送源碼以外,還會包括組件的依賴圖譜分析、組件的版本管理等功能。下面這張圖就描述了 Bit 的實現思路。更多細節能夠參考 Bit 官方文檔 Bit-Docs[3]前端
![](http://static.javashuo.com/static/loading.gif)
雖然 Bit 開源了命令行工具,但並無開源共享組件/區塊的展現站點,相似 Bit 官方提供的網站 bit.dev[4]。也就是說使用者沒法經過瀏覽組件/區塊的構建後的視圖的方式,來查找保存在 Bit 遠程倉庫的組件/區塊代碼。Bit 網站效果以下圖:vue
![](http://static.javashuo.com/static/loading.gif)
接下來就須要本身實現一個相似的網站,進而就會發現其中最難的部分就是實現一個在線 IDE,用於展現組件/區塊代碼,並支持代碼實時構建以及獲取構建後的頁面截圖等功能。效果以下圖:node
![](http://static.javashuo.com/static/loading.gif)
使用目前提供的在線 IDE 的問題
看到這裏你可能會有個疑問,爲何不能直接使用現有免費的在線 IDE?例如 CodeSandbox[5]、CodePen[6]、Stackblitz[7] 等。主要有以下緣由:react
-
對於稍具必定規模的公司,都會有本身的私有 npm 源,而在線 IDE 沒法獲取到這些 npm 包;webpack
-
前端項目構建中一些特定的配置,而現有的在線 IDE 沒法支持;nginx
例如 CodeSandbox 只能設置構建模板的類型,例如 create-react-app,並無提供外部修改具體的構建配置的 API。例如項目中用到了 less 文件,選擇 create-react-app 模板是沒法構建的該類型文件的。git
-
特殊的功能沒法實現,例如點擊頁面的按鈕,能夠實現對在線 IDE 右側構建出來的頁面進行截圖,並將圖片數據傳輸出來;github
-
使用在線 IDE 提供的服務,通常意味着你的組件/區塊是暴露在公網上的,然而可能有些代碼涉密,是不能上傳到公網上的。
-
部分構建工具依賴 node_modules 等文件,沒法在沒有 node_modules 的瀏覽器中正常工做。例如 babel 插件等。這個在後面的定製 CodeSandbox 功能部分會舉個例子細說。
因此咱們須要搭建一個屬於本身的在線 IDE ,以解決上面提的幾個問題。那麼接下來有兩種方式:一種是徹底從零開發一個在線 IDE,另外一種是找到一個開源的項目,並在此基礎上進行定製。
最開始筆者選擇了本身開發,可是開發一段時間後,發現花費了大量精力實現出來 IDE 和已有的產品相比,不管是從功能豐富度仍是易用性上,都徹底落敗。再加上筆者主要想實現的是一個跨前端項目區塊複用的平臺,在線 IDE 只是其中一個非必要的組成部分(注:其實也能夠將共享的組件/區塊的源代碼直接在頁面上展現,經過組件/區塊名稱來區分,雖然這種方式確實很 low)。因此最終仍是選擇在已經開源的在線 IDE 基礎上二次開發。
CodeSandbox 基本原理
筆者主要研究的是 Codesandbox[8] 以及 Stackblitz[9] 。這兩個都是商業化的項目,其中 Stackblitz 的核心部分並無開源出來,而 CodeSandbox 絕大部分的功能都已經開源出來了,因此最終選擇了 CodeSandbox。
爲了方便後續講解如何定製和部署 CodeSandbox,這裏大概說一下它的基本原理(下面主要引用了CodeSandbox 如何工做? 上篇[10] 的部份內容):
CodeSandbox 最大的特色是採用在瀏覽器端作項目構建,也就是說打包和運行不依賴服務器。因爲瀏覽器端並無 Node 環境,因此 CodeSandbox 本身實現了一個能夠跑在瀏覽器端的簡化版 webpack。
CodeSandbox 組成部分
以下圖所示,CodeSandbox 主要包含了三個部分:
![](http://static.javashuo.com/static/loading.gif)
-
Editor 編輯器:主要用於編輯代碼,代碼變更後會通知 Sandbox 進行轉譯
-
Sandbox 代碼運行沙盒:在一個單獨的 iframe 中運行,負責代碼的編譯 Transpiler 和運行 Evalation
-
Packager npm 在線打包器:給 Sandbox 提供 npm 包中的文件內容
CodeSandbox 構建項目過程
構建過程主要包括了三個步驟:
-
Packager--npm 包打包階段:下載 npm 包並遞歸查找全部引用到的文件,而後提供給下個階段進行編譯
-
Transpilation--編譯階段:編譯全部代碼, 構建模塊依賴圖
-
Evaluation--執行階段:使用 eval 運行編譯後的代碼,實現項目預覽
Packager--npm 包打包階段
Packager 階段的代碼實現是在 CodeSandbox 託管在 GitHub 上的倉庫 dependency-packager[11] 裏,這是一個基於 express[12] 框架提供的服務,而且部署採用了 Serverless(基於 AWS Lambda) 方式,讓 Packager 服務更具伸縮性,能夠靈活地應付高併發的場景。(注:在私有化部署中若是沒有 Serverless 環境,能夠將源碼中有關 AWS Lambda 部分所有註釋掉便可 )
以 react 包爲例,講解下 Packager 服務的原理,首先 express 框架接收到請求中的包名以及包版本,例如 react@16.8.0。而後經過 yarn 下載 react 以及 react 的依賴包到磁盤上,經過讀取 npm 包的 package.json 文件中的 browser、module、main、unpkg 等字段找到 npm 包入口文件,而後解析 AST 中全部的 require 語句,將被 require 的文件內容添加到 manifest 文件中,而且遞歸執行剛纔的步驟,最終造成依賴圖。這樣就實現將 npm 包文件內容轉移到 manifest.json 上的目的,同時也實現了剔除 npm 模塊中多餘的文件的目的。最後返回給 Sandbox 進行編譯。下面是一個 manifest 文件的示例:
{
// 模塊內容
"contents": {
"/node_modules/react/index.js": {
"content": "'use strict';↵↵if ....", // 代碼內容
"requires": [ // 依賴的其餘模塊
"./cjs/react.development.js",
],
},
//...
},
// 模塊具體安裝版本號
"dependencies": [{
name: "@babel/runtime",
version: "7.3.1"
}, /*…*/ ],
// 模塊別名, 好比將react做爲preact-compat的別名
"dependencyAliases": {},
// 依賴的依賴, 即間接依賴信息. 這些信息能夠從yarn.lock獲取
"dependencyDependencies": {
"object-assign": {
"entries": ["object-assign"], // 模塊入口
"parents": ["react", "prop-types", "scheduler", "react-dom"], // 父模塊
}
//...
}
}
值得一提的是爲了提高 npm 在線打包的速度,CodeSandbox 做者使用了 AWS 提供的 S3 雲存儲服務。當某個版本的 npm 包已經打包過一次的話,會將打包的結果 manifest.json
文件存儲到 S3 上。在下一次請求一樣版本的包時,就能夠直接返回儲存的 manifest.json
文件,而不須要重複上面的流程了。在私有化部署中能夠將 S3 替換成你本身的文件存儲服務。
Transpilation--編譯階段
當 Sandbox 從 Editor 接收到前端項目的源代碼、npm 依賴以及構建模板 Preset。Sandbox 會初始化配置,而後從 Packager 服務下載 npm 依賴包對應的 manifest 文件,接着從前端項目的入口文件開始對項目進行編譯,並解析 AST 遞歸編譯被 require 的文件,造成依賴圖(注:和 webpack 原理基本一致)。
注意 CodeSandbox 支持外部預約義項目的構建模板 Preset。Preset 規定了針對某一類型的文件,採用哪些 Transpiler(至關於 Webpack 的 Loader)對文件進行編譯。目前可供選擇的 Preset 選項有:vue-cli
、 create-react-app
、create-react-app-typescript
、 parcel
、angular-cli
、preact-cli
。可是不支持修改某個 Preset 中的具體配置,這些都是內置在 CodeSandbox 源碼中的。Preset 具體配置示例以下:
import babelTranspiler from "../../transpilers/babel";
...
const preset = new Preset(
"create-react-app",
["web.js", "js", "json", "web.jsx", "jsx", "ts", "tsx"], {
hasDotEnv: true,
setup: manager => {
const babelOptions = {...};
preset.registerTranspiler(
module =>
/\.(t|j)sx?$/.test(module.path) && !module.path.endsWith(".d.ts"),
[{
transpiler: babelTranspiler,
options: babelOptions
}],
true
);
...
}
}
);
Evaluation--執行階段
Evaluation 執行階段是從項目入口文件對應的編譯後的模塊開始,遞歸調用 eval 執行全部被引用到的模塊。
因爲本文主要是闡述如何搭建本身的在線 IDE,因此 CodeSandbox 更多的實現細節能夠參考以下文章:
-
CodeSandbox 如何工做? 上篇[13]
-
CodeSandbox是如何讓npm上的模塊直接在瀏覽器端運行的[14]
私有化部署 CodeSandbox
瞭解完 CodeSandbox 基本原理後,接下來就到了本文的核心內容:如何私有化部署 CodeSandbox。
在線打包服務 Packager
首先是 npm 在線打包服務 dependency-packager[15]。筆者是經過鏡像部署到本身的服務器上的。
接着是將 npm 源改爲公司的私有 npm 源,能夠經過兩種方式,一種是在鏡像中經過 npm config 命令全局修改,例如以下 Dockerfile:
FROM node:12-alpine
COPY . /home/app
# 設置私有 npm 源
RUN cd /home/app && npm config set registry http://npm.xxx.com && npm install -f
WORKDIR /home/app
CMD ["npm", "run", "dev"]
第二種方式是在源碼中經過 yarn 下載 npm 包的命令後面添加參數 --registry=http://npm.xxx.com
,相關代碼在 functions/packager/dependencies/install-dependencies.ts[16] 文件中。
另外該服務依賴了 AWS 的 Lambda 提供的 Serverless,並採用 AWS 提供的 S3 存儲服務緩存 npm 包的打包結果。若是讀者沒有這些服務的話,能夠將源碼中這部份內容註釋掉或者換成對應的其餘雲計算廠商的服務便可。dependency-packager[17] 本質上就是一個基於 express 框架的 node 服務,能夠簡單地直接跑在服務器中。
編輯器 Editor
在 CodeSandbox-client 工程中的 standalone-packages/react-sandpack[18] 項目,就是 CodeSandbox 提供的基於 react[19] 實現的的編輯器項目。區別於主項目實現的編輯器,這個編輯器主要是爲了給使用者進行定製,因此實現的比較簡陋,使用者能夠根據本身的需求在這個編輯器的基礎上加入本身須要的功能。固然若是沒有自定義編輯器的需求,能夠直接使用 react-sandpack 項目對應的 npm 包 react-smooshpack[20],使用方式以下:
import React from 'react';
import { render } from 'react-dom';
import {
FileExplorer,
CodeMirror,
BrowserPreview,
SandpackProvider,
} from 'react-smooshpack';
import 'react-smooshpack/dist/styles.css';
const files = {
'/index.js': {
code: "document.body.innerHTML = `<div>${require('uuid')}</div>` ",
},
};
const dependencies = {
uuid: 'latest',
};
const App = () => (
<SandpackProvider
files={files}
dependencies={dependencies}
entry="/index.js"
bundlerURL= `http://sandpack-${version}.codesandbox.io` >
<div style={{ display: 'flex', width: '100%', height: '100%' }}>
<FileExplorer style={{ width: 300 }} />
<CodeMirror style={{ flex: 1 }} />
<BrowserPreview style={{ flex: 1 }} />
</div>
</SandpackProvider>
);
render(<App />, document.getElementById('root'));
其中子組件 FileExplorer、CodeMirror、BrowserPreview 分別是左側的文件目錄樹、中間的代碼編輯區和右側的項目構建後的頁面預覽區。
經過查看這個獨立庫的源碼,能夠知道除了這三個子組件以外,SandpackProvider 還會再插入一個 iframe 標籤,主要用於顯示項目構建後的頁面,而右側預覽區組件 BrowserPreview 中的 Preview 組件會將這個 ifame 插入到本身的節點,這樣就實現了將項目構建的頁面實時顯示出來的目的。
而 iframe 加載的 bundlerUrl 默認是官方提供的地址 http://sandpack-${version}.codesandbox.io
,其中這個域名對應的服務其實就是 CodeSandbox 的核心--在瀏覽器端構建前端項目的服務,大體原理剛剛已經闡述過了。下一小節會闡述如何將官方提供的構建服務替換成本身的。
至於代碼編輯區的代碼/依賴如何同步到 iframe 中加載的構建服務,其實它依賴了另外一個獨立庫 sandpack(和 react-sandpack 同級目錄),其中有一個 Manager 類就是在代碼編輯區和右側預覽區的構建服務之間搭建橋樑,主要是用了 codesandbox-api 包提供的 dispatch 方法進行編輯器和構建服務之間的通訊。
代碼運行沙盒 SandBox
怕你們誤解先提早說明下,上一小節提到的構建服務並非後端服務,這個服務其實就是 CodeSandbox 構建出來的前端頁面。基本原理部分已經闡述了 CodeSandbox 實際上在瀏覽器裏實現了一個 webpack,項目的構建所有是在瀏覽器中完成的。
而 CodeSandbox 前端構建的核心部分的目錄在 CodeSandbox-client 工程中 packages/app[21] 項目,其中的原理已經在上面闡述過了,這裏只須要將該項目構建出來的 www 文件夾部署到服務器便可。因爲該核心庫又依賴了其餘庫,因此也須要先構建下依賴庫。下面筆者寫了一個 build.sh 文件,放置在整個項目的一級目錄便可。
# # 運行和構建須要 Node 10 環境
# nvm use 10
# 安裝依賴
yarn
# 構建依賴庫
yarn run build:deps
# 進入到核心庫 packages/app 進行構建
cd packages/app
yarn run build:sandpack-sandbox
# 因爲一些緣由,一些須要的靜態文件須要從總體項目的構建目錄中獲取
# 所以須要在執行該 shell 腳本以前,將整個項目構建一次,即執行 npm run build 便可(這個構建的時間會比較久)
cp -rf ../../www/static/* ./www/static
當執行完上面的 shell 腳本以後,就能夠將 packages/app 目錄下構建的產物 www 部署到服務器上,筆者採用的是容器部署,下面是 dockerfile 文件內容。
FROM node:10.14.2 as build
WORKDIR /
ADD . .
RUN /bin/sh build.sh
FROM nginx:1.16.1-alpine
COPY --from=build /packages/app/www /usr/share/nginx/html/
注意這裏採用了分階段構建鏡像,即先構建 CodeSandbox 項目,再構建鏡像。但在實踐中發現 CodeSandbox 項目放在服務器上構建不是很順利,因此最終仍是選擇在本地構建該項目,而後將構建產物一併上傳到遠程 git 倉庫,這樣在打包機上只須要構建鏡像並運行便可。
整個部署的靈感來自 GitLab 的官方倉庫的一個 issue: GitLab hosted Codesandbox[22]
定製 CodeSandbox 功能
上個小節讀者可能會有個疑問,爲何直接使用 CodeSandbox 提供的默認構建服務?其實就是爲了對 CodeSandbox 的構建流程進行定製,接下來舉四個例子來講明下。
替換組件樣式自動引入的 babel 插件功能
針對公司自建的組件庫,通常都會開發相似 babel-plugin-import 這樣的插件,以便在代碼中使用組件時無需額外再引入組件的樣式文件,babel-plugin-import 插件會在 js 編譯階段自動插入引入樣式的代碼。但這種插件可能會須要遍歷組件的 package.json 中的依賴中是否有其餘組件,若是有也要把其餘組件的樣式文件的引入寫到編譯後的 js 中,並遞歸執行剛纔的過程。這裏就須要讀入 node_modules 中的相關文件。可是諸如 CodeSandbox[23]、Stackblitz[24] 等都是在瀏覽器中進行構建,並無 node_modules。
針對這個問題,筆者最終放棄了利用 babel 插件在 js 編譯階段進行插入引入樣式文件代碼的方式,而是在代碼運行階段從 npm 在線打包服務中獲取組件的樣式文件,而後將樣式文件內容經過 style 標籤動態插入到 head 標籤上面。下面是具體改動:
在線 npm 打包服務側
在線 npm 打包服務通常只會返回 js 文件,因此須要在該服務基礎上增長一個功能:當判斷請求的 npm 包爲內建組件,則還要額外返回樣式文件。下面是 dependence-packager[25] 項目中添加的核心代碼:
爲了提供獲取私有組件樣式文件的方法,能夠在 functions/packager/utils[26] 目錄下新建一個文件 fetch-builtin-component-style.ts
,核心代碼以下:
// 根據組件 npm 包名以及經過 yarn 下載到磁盤上的 npm 包路徑,讀入對應的樣式文件內容,並寫入到 manifest.json 的 contents 對象上
const insertStyle = (contents: any, packageName: string, packagePath: string) => {
const stylePath = `node_modules/${packageName}/dist/index.css`;
const styleFilePath = join(
packagePath,
`node_modules/${packageName}/dist/index.css` ,
);
if (fs.existsSync(styleFilePath)) {
contents[stylePath] = {
contents: fs.readFileSync(styleFilePath, "utf-8"),
isModule: false,
};
}
};
// 獲取內建組件的樣式文件,並寫入到返回給 Sandbox 的 manifest.json 文件中
const fetchBuiltinComponentStyle = (
contents: any,
packageName: string,
packagePath: string,
dependencyDependencies: any,
) => {
// 當 npm 包或者其依賴以及依賴的依賴中有內建組件,則將該內建組件對應的樣式文件寫入到 manifest.json 文件中
if (isBuiltinComponent(packageName)) {
insertStyle(contents, packageName, packagePath);
Object.keys(dependencyDependencies.dependencyDependencies).forEach(
(pkgName) => {
if (isBuiltinComponent(pkgName)) {
insertStyle(contents, pkgName, packagePath);
}
},
);
}
};
並在 functions/packager/index.ts[27] 文件中調用該方法。代碼以下:
+ // 針對私有組件,將組件樣式文件也寫到返回給瀏覽器的 manifest.json 文件中
+ fetchBuiltinComponentStyle(
+ contents,
+ dependency.name,
+ packagePath,
+ dependencyDependencies,
+ );
// 做爲結果返回
const response = {
contents,
dependency,
...dependencyDependencies,
};
瀏覽器 CodeSandbox 側
瀏覽器 CodeSandbox 側須要提供處理私有組件樣式的方法,主要是在 Evaluation 執行階段將樣式文件內容經過 style 標籤動態插入到 head 標籤上面,能夠在 packages/app/src/sandbox/eval/utils[28] 目錄下新建一個文件 insert-builtin-component-style.ts
,下面是核心代碼:
// 基於樣式文件內容建立 style 標籤,並插入到 head 標籤上
const insertStyleNode = (content: string) => {
const styleNode = document.createElement('style');
styleNode.type = 'text/css';
styleNode.innerHTML = content;
document.head.appendChild(styleNode);
}
const insertBuiltinComponentStyle = (manifest: any) => {
const {contents, dependencies, dependencyDependencies} = manifest;
// 從依賴以及依賴的依賴中根據 npm 包名篩選出內建組件
const builtinComponents = Object.keys(dependencyDependencies).filter(pkgName => isBuiltinComponent(pkgName));
dependencies.map((d: any) => {
if (isBuiltinComponent(d.name)) {
builtinComponents.push(d.name);
}
});
// 根據基於內建組件 npm 名稱拼裝成的 key 查找到具體的文件內容,並調用 insertStyleNode 方法插入到 head 標籤上
builtinComponents.forEach(name => {
const { content } = contents[ `/node_modules/${name}/dist/index.css` ];
if (content) {
insertStyleNode(content);
}
});
}
並在 Evaluation 執行階段調用該方法,相關文件在 packages/sandpack-core/src/manager.ts[29] ,具體修改以下:
...
setManifest(manifest?: Manifest) {
this.manifest = manifest || {
contents: {},
dependencies: [],
dependencyDependencies: {},
dependencyAliases: {},
};
+ insertBuiltinComponentStyle(this.manifest);
...
}
...
添加預覽區域截圖功能
在區塊複用平臺項目中,在點擊保存按鈕時,不只要保存編輯好的代碼,還須要對構建好的右側預覽區域進行截圖並保存。以下圖所示:
![](http://static.javashuo.com/static/loading.gif)
右側預覽區域所展現的內容是 SandpackProvider 組件插入的 iframe,因此只須要找到這個 iframe,而後經過 postMessage 與 iframe 內頁面進行通訊。當 iframe 內部頁面接收到截圖指令後,對當前 dom 進行截圖並傳出便可,這裏筆者用的是 html2canvas 進行截圖的。下面是 CodeSandbox 側的代碼改造,文件在 packages/app/src/sandbox/index.js[30] 中,主要是在文件結尾處添加以下代碼:
const fetchScreenShot = async () => {
const app = document.querySelector('#root');
const c = await html2canvas(app);
const imgData = c.toDataURL('image/png');
window.parent.postMessage({
type: 'SCREENSHOT_DATA',
payload: {
imgData
}
}, '*');
};
const receiveMessageFromIndex = (event) => {
const {
type
} = event.data;
switch (type) {
case 'FETCH_SCREENSHOT':
fetchScreenShot();
break;
default:
break;
}
};
window.addEventListener('message', receiveMessageFromIndex, false);
在 CodeSandbox 使用側,則須要在須要截圖的時候,向 iframe 發送截圖指令。同時也須要監聽 iframe 發來的消息,從中篩選出返回截圖數據的指令,並獲取到截圖數據。因爲實現比較簡單,這裏就不展現具體代碼了。
create-react-app 模板中添加對 less 文件編譯的支持
主要是對 create-react-app 這個 preset 的配置作一些修改,文件地址 packages/app/src/sandbox/eval/presets/create-react-app/v1.ts[31]。修改代碼以下:
...
+ import lessTranspiler from '../../transpilers/less';
+ import styleProcessor from '../../transpilers/postcss';
export default function initialize() {
...
+ preset.registerTranspiler(module => /\.less$/.test(module.path), [
+ { transpiler: lessTranspiler },
+ { transpiler: styleProcessor },
+ {
+ transpiler: stylesTranspiler,
+ options: { hmrEnabled: true },
+ },
+ ]);
...
}
修改 CodeSandbox 請求的 npm 打包服務地址
能夠將打包 npm 的服務換成上面私有化部署的服務,以解決沒法獲取私有 npm 包等問題。相關文件在 packages/sandpack-core/src/npm/preloaded/fetch-dependencies.ts[32] 。修改代碼以下:
const PROD_URLS = {
...
// 替換成本身的在線 npm 打包服務便可
- bucket: 'https://prod-packager-packages.codesandbox.io',
+ bucket: 'http://xxx.xxx.com'
};
...
function dependencyToPackagePath(name: string, version: string) {
- return `v${VERSION}/packages/${name}/${version}.json` ;
+ return `${name}@${version}` ;
}
這四個例子就講完了,讀者能夠根據本身的需求進行更多的定製。當你明白了整個 CodeSandbox 的運行機制後,就會發現定製並無那麼難。
結束語
到此爲止,私有化部署一個屬於本身而且能夠任意定製的在線 IDE 的目標就已經達成了。固然在線 IDE 的項目構建不只僅侷限在瀏覽器中,還能夠將整個構建過程放在服務端,藉助於雲+容器化的能力,使得在線 IDE 有着跟本地 IDE 幾乎徹底同樣的功能。其實這二者應用的場景很少,徹底基於瀏覽器構建更適用於單一頁面項目的實時預覽,而基於服務端構建是徹底能夠適用於真實的項目開發的,而且不只僅侷限於前端項目。筆者也在嘗試探索基於服務端構建 IDE 的可能性,期待後面可以有些產出分享給你們。
接下來若是讀者感興趣的話,能夠繼續閱讀基於 Bit 和 CodeSandbox 實現的區塊平臺項目--跨項目區塊複用方案實踐[33]
參考資料
-
CodeSandbox 如何工做? 上篇 [34] -
GitLab hosted Codesandbox [35]
參考資料
唐江洪: https://github.com/mcuking
[2]Bit: https://github.com/teambit/bit
[3]Bit-Docs: https://docs.bit.dev/docs/how-bit-works
[4]bit.dev: https://bit.dev
[5]CodeSandbox: https://codesandbox.io/
[6]CodePen: https://codepen.io/
[7]Stackblitz: https://stackblitz.com/
[8]Codesandbox: https://github.com/codesandbox
[9]Stackblitz: https://github.com/stackblitz
[10]CodeSandbox 如何工做? 上篇: https://bobi.ink/2019/06/20/codesandbox/
[11]dependency-packager: https://github.com/codesandbox/dependency-packager
[12]express: https://expressjs.com/
[13]CodeSandbox 如何工做? 上篇: https://bobi.ink/2019/06/20/codesandbox/
[14]CodeSandbox是如何讓npm上的模塊直接在瀏覽器端運行的: https://www.yuque.com/wangxiangzhong/aob8up/uf99c5
[15]dependency-packager: https://github.com/codesandbox/dependency-packager
[16]functions/packager/dependencies/install-dependencies.ts: https://github.com/codesandbox/dependency-packager/blob/master/functions/packager/dependencies/install-dependencies.ts
[17]dependency-packager: https://github.com/codesandbox/dependency-packager
[18]standalone-packages/react-sandpack: https://github.com/codesandbox/codesandbox-client/tree/master/standalone-packages/react-sandpack
[19]react: https://reactjs.org/
[20]react-smooshpack: https://www.npmjs.com/package/react-smooshpack
[21]packages/app: https://github.com/codesandbox/codesandbox-client/tree/master/packages/app
[22]GitLab hosted Codesandbox: https://gitlab.com/gitlab-org/gitlab/-/issues/27144
[23]CodeSandbox: https://codesandbox.io/
[24]Stackblitz: https://stackblitz.com/
[25]dependence-packager: https://github.com/codesandbox/dependency-packager
[26]functions/packager/utils: https://github.com/codesandbox/dependency-packager/tree/master/functions/packager/utils
[27]functions/packager/index.ts: https://github.com/codesandbox/dependency-packager/blob/master/functions/packager/index.ts
[28]packages/app/src/sandbox/eval/utils: https://github.com/codesandbox/codesandbox-client/tree/master/packages/app/src/sandbox/eval/utils
[29]packages/sandpack-core/src/manager.ts: https://github.com/codesandbox/codesandbox-client/blob/master/packages/sandpack-core/src/manager.ts
[30]packages/app/src/sandbox/index.js: https://github.com/codesandbox/codesandbox-client/blob/master/packages/app/src/sandbox/index.js
[31]packages/app/src/sandbox/eval/presets/create-react-app/v1.ts: https://github.com/codesandbox/codesandbox-client/blob/master/packages/app/src/sandbox/eval/presets/create-react-app/v1.ts
[32]packages/sandpack-core/src/npm/preloaded/fetch-dependencies.ts: https://github.com/codesandbox/codesandbox-client/blob/master/packages/sandpack-core/src/npm/preloaded/fetch-dependencies.ts
[33]跨項目區塊複用方案實踐: https://github.com/mcuking/blog/issues/88
[34]CodeSandbox 如何工做? 上篇: https://bobi.ink/2019/06/20/codesandbox/
[35]GitLab hosted Codesandbox: https://gitlab.com/gitlab-org/gitlab/-/issues/27144
[36]網易雲音樂大前端團隊: https://github.com/x-orpheus
本文分享自微信公衆號 - 前端之露(gh_ef72c6726e70)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。