rollup 最佳實踐!可調試、編譯的小型開源項目思路
📖閱讀本文,你將:
-
手把手帶你搭建一個極簡
npm
包(rollup
+esbuild
+ts
); -
從工程結構全方位思考
npm
包的開發原則。 -
學會
rollup
、esbuild
等新興構建工具的使用;
一、前置閱讀(推薦)
本文章不再贅述
rollup
和 官方rollup
插件的使用,因此有需要的話建議先閱讀前置文章:
-
說不清 rollup 能輸出哪 6 種格式😥差點被鄙視
-
一文入門 rollup🪀!13 組 demo 帶你輕鬆駕馭
如果你對 rollup
和其官方插件有了一定了解,可跳過本節推薦的相關閱讀;
二、目標
先制定一個小目標:
從零構建一個通過
rollup
構建的npm
包;它可發佈、可調試、支持TypeScript
、能夠通過cdn
引入頁面、也可以通過npm
在項目裏安裝引入。
這個目標有哪些關鍵詞?讓我們拆解一下:
-
是一個 可發佈 的
npm
包; -
通過
rollup
構建; -
支持 調試 ;
-
支持 TypeScript ;
-
能通過
cdn
方式引入 -
也支持通過
npm
安裝;
看似簡單,其實內中蘊含着許多前端相關的知識點,讓我們一一梳理;
三、典型 npm
包的結構
我們發佈一個 npm
包時,我們在發佈什麼?
-
一個用來聲明
npm
屬性的package.json
文件; -
若干可執行、可引入的文件;
僅此而已。
只有 package.json
是特殊的,除此之外的一切都可以被歸爲 “若干文件” 之列;
找一個最典型、最簡單的 npm
包來印證我們的想法,你可以隨便找個項目,執行以下命令:
npm i the-answer
# 傳說,此庫以 “功能豐富”、“毫無破綻” 聞名於世
然後找到 node_modules/the-answer
文件夾,看看其項目目錄:
是以,要理解 npm
包,必須先從 package.json
入手,而一個典型的 package.json
至少應該具備以下屬性:
{
"name": "the-answer",
"version": "1.0.0",
"description": "description",
"main": "dist/the-answer.js",
"files": [
"dist"
]
}
以上內容分別表示:
-
name
: 包名 -
version
: 版本 -
description
: 包描述 -
main
:入口 -
files
:包發佈時,需要將哪些文件打包上傳
以上 5 個屬性,足以支撐一個 npm
包的發佈,但後續我們可能還會用到更多,此時,請先記住它們;
那麼其他文件呢?
“協議” 和 “文檔” 可以手動創建,但是 “構建物” 通常難逃 “構建” 這一過程。
rollup
是個不錯的選擇;
四、 rollup
的最簡用法
4.1 安裝 rollup
執行腳本:
npm i -D rollup
# or
yarn add -D rollup
4.2 創建 rollup.config.js
rollup.config.js
是rollup
默認的配置文件
在這個配置文件裏,最簡單的配置莫過於:
-
配置入口文件
-
配置輸出路徑 & 格式
export default {
input: path.resolve(__dirname, './src/index.ts'),
output: [
{
dir: path.resolve(__dirname, 'dist/esm'),
format: 'esm', // 通過esm格式輸出
}
]
}
4.3 編寫構建腳本
在 package.json
文件中添加如下代碼:
{
...,
"scripts": {
"build": "rollup -c"
}
}
編寫完上面腳本後,只需要執行:npm run build
,就能成功完成一個最簡單的 rollup
構建了。
但這還遠遠不夠。
五、梳理:構建過程應該輸出什麼?
構建是爲了有輸出,輸出是爲了給客戶使用。
因此,當我們構建時,應該率先考慮客戶會在什麼場景下使用我們的庫。
(哪怕沒有人真的用,但我們得假裝自己的庫會非常受歡迎,不是嗎?🤣)
最常見的三種情況:
-
在構建工具中引入(
esm
) -
在頁面中直接引入(
umd
) -
在
nodejs
環境中使用 (cjs
)
而本文的主要目標不涉及 nodejs
環境,因此暫時不考慮 cjs
輸出,所以我們應該輸出至少兩種格式:
-
esm
-
umd
關於以上格式的區別,以及怎麼輸出,前置閱讀推薦《說不清 rollup 能輸出哪 6 種格式😥差點被鄙視》裏有詳細介紹;
通過參考目前開源社區的實現方式,我們還可以發現,通常一個開源庫的 esm
產出物,並不是和 rollup
默認行爲那樣 “把所有文件打包成一個 index.esm.js
”,而是:
只編譯,不打包;(這樣就能完美支持按需引入了)
是的,文件結構依然和源碼結構保持一致,只是將 TypeScript
編譯爲 javascript
;
記住這個目標,它很重要;
六、編譯 TS
哪家強?
雖然有提案讓
ts
成爲下一代的瀏覽器語言,但這只是還未到來的未來。
因此,如果我們的庫是 TypeScript
編寫的,那麼我們在發佈之前,一定需要將其編譯爲 javascript
類型。
那麼,應該怎麼編譯呢?
社區裏目前的主流方案有三類:
a. TypeScript
https://www.npmjs.com/package/typescript
b. Esbuild
https://esbuild.github.io/
c. Babel
https://babeljs.io/
通過這篇文章:https://juejin.cn/post/7083298517753528334 的對比分析,可以得出結論:
【c】
比 【a】
支持的語言更豐富、編譯速度更快;但是 .d.ts
還是推薦通過 【a】
中的 tsc
編譯生成;
那麼 【b】
選項 Esbuild
呢?
答案是:速度更快 !
所以,我做出瞭如下選擇:
-
使用
esbuild
進行ts => js
的編譯 -
使用
TypeScript
進行.d.ts
文件的編譯;
七、編譯成 esm
格式
整個構建過程梳理清楚後,我們可以在項目里加入如下依賴:
npm i typescript esbuild rollup-plugin-esbuild --save-dev
除了以上這些之外,還有一些常規 rollup
插件不在此贅述;
創建文件:rollup.esm.config.js
export default {
input: path.resolve(__dirname, './src/index.ts'),
output: [
{
dir: path.resolve(__dirname, 'dist/esm'),
format: 'esm',
}
],
// 關鍵屬性,只有將其設置爲 `true` 才能保證只編譯、不打包
preserveModules: true,
plugins: [
esbuild({
target: 'es2018'
}),
nodeResolve(),
json(),
],
// 在 `esm` 構件中,`external` 非常重要,項目的 `dependencies` 裏的內容理應都在此列
external: ['gsap']
};
編寫如下腳本:
{
"scripts": {
"build:esm": "rollup -c rollup.esm.config.js",
"postbuild:esm": "tsc --emitDeclarationOnly --declaration --project ts.config.json --outDir dist/esm",
}
}
解釋一下,postbuild:esm
是 build:esm
腳本的後置鉤子,當 build:esm
執行完成後會自動執行 postbuild:esm
;
執行腳本:
npm run build:esm
可以編譯出如下效果的代碼:
基本滿足我們對 esm
格式編譯產出的要求。
八、編譯 umd
格式
和 esm
格式的編譯要求不同,umd
格式更多的被用在 cdn
加載。
因此:
-
構建物不僅需要編譯,還需要打包
-
無需
.d.ts
文件
創建 rollup.umd.config.js
:
export default {
input: path.resolve(__dirname, './src/index.ts'),
output: [
{
file: path.resolve(__dirname, 'dist/umd/index.js'),
format: 'umd',
// 這個name屬性非常重要,是通過 cdn 引入後,掛載到 window上的屬性名
name: 'rain'
}
],
plugins: [
esbuild({
// 爲了應對 umd 直接加載到瀏覽器裏,構建目標需要設定得兼容性更強
target: 'es2015'
}),
// 需要babel plugin
babel({
presets: ['@babel/preset-env'],
exclude: 'node_modules/**',
babelHelpers: 'bundled'
}),
nodeResolve(),
json(),
commonjs()
],
};
編寫腳本:
{
"scripts": {
"build:umd": "rollup -c rollup.umd.config.js",
}
}
執行腳本 npm run build:umd
即可完成 umd
格式的編譯。
完成 esm
和 umd
的編譯後,需要考慮一下:
如何擁有一個良好的開發調試環境呢?
九、umd
構建物的調試
rollup
的命令行支持參數 -w
(和 --watch
等價),其含義是監聽源文件的變動,當文件發生變化時立刻更新文件;
這聽起來和 webpack
的 hrm
類似。
因此,umd
環境下構建物的調試思路便出來了:
-
創建文件:
examples/base/index.html
-
在
index.html
裏引入dist/umd/index.js
-
起一個
http
靜態服務以便訪問index.html
頁面
這就是最簡單的前端調試邏輯。
此時,我們需要用到三個工具:
cross-env
工具庫
通過cross-env
,我們可以輕易地跨平臺地設置環境變量,比如:
cross-env NODE_ENV=production
可以把 process.env.NODE_ENV
設置成 production
,這對我們的區分 “調試” 與 “構建” 具有非常大的幫助,
rollup-plugin-serve
插件
這是一個rollup
插件;
通過這個插件,你可以:
-
在固定端口啓動
http
靜態服務 -
指定該端口的
root
文件夾 -
指定瀏覽器默認打開頁面,並可以指定直接訪問某個路由
rollup-plugin-livereload
插件
這也是一個rollup
插件;
通過這個插件,可以監聽某個文件夾,當裏面的文件發生變動後,刷新頁面。
使用以上三個工具,可以寫出如下代碼:
const isProduction = process.env.NODE_ENV === 'production'
const pluginsWithEnv = isProduction ? [] : [serve({
open: true,
openPage: '/base/',
port: 10001,
contentBase: ['dist', 'examples']
}), livereload('dist/umd')]
// 然後通過 ...pluginsWithEnv 把配置結構到 rollup的plugins 裏面去
並添加腳本:
{
"scripts": {
"dev:umd": "cross-env NODE_ENV=development rollup -w -c rollup.umd.config.js"
}
}
完成以上步驟:執行 npm run dev:umd
就能正常打開頁面,以及完成調試了。
十、esm
構建物的調試
相比於 umd
那簡單的調試結構,esm
通常情況下需要依賴構建工具,事情就變得複雜起來了。
我的選擇是 在項目裏內置一個基於 webpack/vite
的項目 ;
10.1 用 vue-cli
新增項目
先安裝 vue-cli
,見 vue-cli 官網
然後執行以下命令:
cd examples
vue create vue3
10.2 import
構建物
在 vue3
項目中通過相對路徑引用 esm
構建物,引入方式如下:
import xx from '../../../'
注意,此處不需要指定到某個 js
文件,是因爲要通過 esm
的導入方式,通過命中項目的 package.json
,通過 exports
、main
、module
等屬性命中它該命中的入口文件。
10.3 編寫腳本
編寫如下腳本:
{
"scripts": {
"serve:vue3": "cd ./examples/vue3 && yarn && yarn serve",
"watch:esm": "cross-env NODE_ENV=development rollup -w -c rollup.esm.config.js",
}
}
如果分別運行 npn run watch:esm
和 npm run serve:vue3
,就能成功運行一個 vue3
項目,併成功進行調試。
10.4 並聯兩個腳本
因爲 serve:vue3
和 watch:esm
這兩個腳本都是會導致命令行的命令,因此如果你這樣寫一個腳本:
npn run watch:esm && npm run serve:vue3
那麼 後者永遠不會執行。
因此,兩者必須同時執行。
再推薦一個小工具:npm-run-all
https://www.npmjs.com/package/npm-run-all
有了以上這個工具,你可以輕易的串行或者並行各種腳本。
此處只需要再新增一個腳本:
{
"scripts": {
"dev:esm": "run-p watch:esm serve:vue3",
}
}
這樣就可以異步同時執行兩個腳本了。
十一、開發 & 發佈
完成了以上所有步驟,你就可以開始安心開發腳本了,無論是 “構建” 抑或是 “調試”,目前都已經非常清晰了。
對於 rollup.js
在一個小型前端開源項目中應該如何使用,也漸漸摸清了脈絡。
等開發完項目,則需要考慮發佈的過程。
執行以下過程
npm version patch
項目編號會自動從 0.0.1
升級爲 0.0.2
。
再執行:
npm login
npm publish
就能成功發佈你的開源庫了。
十二、文章源碼(供參考)
參見:https://github.com/zhangshichun/rainy-window
十三、結束語
我是春哥
。
大齡前端打工仔,依然在努力學習。
我的目標是給大家分享最實用、最有用的知識點,希望大家都可以早早下班,並可以飛速完成工作,淡定摸魚🐟。
你可以在公衆號裏找到我:前端要摸魚
。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/-fn8ynYuvRcnDLB5VqRBFg