rollup 最佳實踐!可調試、編譯的小型開源項目思路

📖閱讀本文,你將:

  1. 手把手帶你搭建一個極簡 npm 包( rollup + esbuild + ts );

  2. 從工程結構全方位思考 npm 包的開發原則。

  3. 學會 rollupesbuild 等新興構建工具的使用;

一、前置閱讀(推薦)

本文章不再贅述 rollup 和 官方 rollup 插件的使用,因此有需要的話建議先閱讀前置文章:

  1. 說不清 rollup 能輸出哪 6 種格式😥差點被鄙視

  2. 一文入門 rollup🪀!13 組 demo 帶你輕鬆駕馭

如果你對 rollup 和其官方插件有了一定了解,可跳過本節推薦的相關閱讀;

二、目標

先制定一個小目標:

從零構建一個通過 rollup 構建的 npm 包;它可發佈、可調試、支持 TypeScript、能夠通過 cdn 引入頁面、也可以通過 npm 在項目裏安裝引入。

這個目標有哪些關鍵詞?讓我們拆解一下:

  1. 是一個 可發佈npm 包;

  2. 通過 rollup 構建;

  3. 支持 調試

  4. 支持 TypeScript ;

  5. 能通過 cdn 方式引入

  6. 也支持通過 npm 安裝;

看似簡單,其實內中蘊含着許多前端相關的知識點,讓我們一一梳理;

三、典型 npm 包的結構

我們發佈一個 npm 包時,我們在發佈什麼?

  1. 一個用來聲明 npm 屬性的 package.json 文件;

  2. 若干可執行、可引入的文件;

僅此而已。

只有 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"
  ]
}

以上內容分別表示:

以上 5 個屬性,足以支撐一個 npm 包的發佈,但後續我們可能還會用到更多,此時,請先記住它們;

那麼其他文件呢?

“協議” 和 “文檔” 可以手動創建,但是 “構建物” 通常難逃 “構建” 這一過程。

rollup 是個不錯的選擇;

四、 rollup 的最簡用法

4.1 安裝 rollup

執行腳本:

npm i -D rollup
# or
yarn add -D rollup

4.2 創建 rollup.config.js

rollup.config.jsrollup 默認的配置文件

在這個配置文件裏,最簡單的配置莫過於:

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 構建了。

但這還遠遠不夠。

五、梳理:構建過程應該輸出什麼?

構建是爲了有輸出,輸出是爲了給客戶使用。

因此,當我們構建時,應該率先考慮客戶會在什麼場景下使用我們的庫。

(哪怕沒有人真的用,但我們得假裝自己的庫會非常受歡迎,不是嗎?🤣)

最常見的三種情況:

而本文的主要目標不涉及 nodejs 環境,因此暫時不考慮 cjs 輸出,所以我們應該輸出至少兩種格式:

  1. esm

  2. 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 呢?

答案是:速度更快

所以,我做出瞭如下選擇:

七、編譯成 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:esmbuild:esm 腳本的後置鉤子,當 build:esm 執行完成後會自動執行 postbuild:esm

執行腳本:

npm run build:esm

可以編譯出如下效果的代碼:

基本滿足我們對 esm 格式編譯產出的要求。

八、編譯 umd 格式

esm 格式的編譯要求不同,umd 格式更多的被用在 cdn 加載。

因此:

創建 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 格式的編譯。

完成 esmumd 的編譯後,需要考慮一下:

如何擁有一個良好的開發調試環境呢?

九、umd 構建物的調試

rollup 的命令行支持參數 -w (和 --watch 等價),其含義是監聽源文件的變動,當文件發生變化時立刻更新文件;

這聽起來和 webpackhrm 類似。

因此,umd 環境下構建物的調試思路便出來了:

這就是最簡單的前端調試邏輯。

此時,我們需要用到三個工具:

  1. cross-env 工具庫
    通過 cross-env,我們可以輕易地跨平臺地設置環境變量,比如:
cross-env NODE_ENV=production

可以把 process.env.NODE_ENV 設置成 production,這對我們的區分 “調試” 與 “構建” 具有非常大的幫助,

  1. rollup-plugin-serve 插件
    這是一個 rollup 插件;
    通過這個插件,你可以:
  1. 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,通過 exportsmainmodule 等屬性命中它該命中的入口文件。

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:esmnpm run serve:vue3,就能成功運行一個 vue3 項目,併成功進行調試。

10.4 並聯兩個腳本

因爲 serve:vue3watch: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