目前主流的前端框架vue和react都采用rollup來打包,為了探索rollup的奧妙,接下來就讓我們一步步來探索,並基於rollup搭建一個庫打包腳手架,來發布自己的庫和組件。
前言
寫rollup的文章是因為筆者最近要規范前端開發的業務流程和架構,並提供內部公有組件庫和工具庫供團隊使用。在查閱大量資料並對比了webpack和rollup的優缺點之后,最終選擇rollup來作為打包工具,我們最終要實現通過npm的方式安裝我們的組件庫和工具庫:
// 安裝 npm install @xuxi/tools // 使用 import { sleep } from '@xuxi/tools'
下面我們一步步來復盤rollup的配置過程和最佳實踐。
rollup介紹
Rollup is a module bundler for JavaScript which compiles small pieces of code into something larger and more complex, such as a library or application. It uses the new standardized format for code modules included in the ES6 revision of JavaScript, instead of previous idiosyncratic solutions such as Commonjs and AMD.
意思大致是說Rollup 是一個 JavaScript 模塊打包器,可以將小塊代碼編譯成大塊復雜的代碼,例如 library 或應用程序。Rollup 對代碼模塊使用新的標准化格式,這些標准都包含在 JavaScript 的 ES6 版本中,而不是像Commonjs 和 AMD這種特殊解決方案。
rollup最大的亮點就是Tree-shaking,即可以靜態分析代碼中的 import,並排除任何未使用的代碼。這允許我們架構於現有工具和模塊之上,而不會增加額外的依賴或使項目的大小膨脹。如果用webpack做,雖然可以實現tree-shaking,但是需要自己配置並且打包出來的代碼非常臃腫,所以對於庫文件和UI組件,rollup更加適合。
搭建庫打包腳手架
1. rollup入門
首先我們安裝一下rollup:
npm i rollup -g
然后在本地創建一個項目:
mkdir -p my-project cd my-project
其次我們創建一個入口並寫入如下代碼:
// src/main.js import say from './say.js'; export { say } // src/say.js export default function(name){ console.log(name) };
基本代碼准備好了之后,我們寫rollup的配置文件(rollup.config.js在根目錄下):
// rollup.config.js export default { input: 'src/main.js', output: { file: 'bundle.js', format: 'cjs' } };
這樣,我們在終端執行:
// --config 或 -c 來使用配置文件 rollup -c
這樣在更目錄下就生成了一個bundle.js,就是我們想要的打包后的文件。我們也可以用package.json來設置打包配置信息,用npm run xxx來打包和測試代碼。
2.rollup插件使用
為了更靈活的打包庫文件,我們可以配置rollup插件,比較實用的插件有:
- rollup-plugin-node-resolve ---幫助 Rollup 查找外部模塊,然后導入
- rollup-plugin-commonjs ---將CommonJS模塊轉換為 ES2015 供 Rollup 處理
- rollup-plugin-babel --- 讓我們可以使用es6新特性來編寫代碼
- rollup-plugin-terser --- 壓縮js代碼,包括es6代碼壓縮
- rollup-plugin-eslint --- js代碼檢測
打包一個庫用以上插件完全夠用了,不過如果想實現對react等組件的代碼,可以有更多的插件可以使用,這里就不一一介紹了。
我們可以這樣使用,類似於webpack的plugin配置:
import resolve from 'rollup-plugin-node-resolve'; import commonjs from 'rollup-plugin-commonjs'; import babel from "rollup-plugin-babel"; import { terser } from 'rollup-plugin-terser'; import { eslint } from 'rollup-plugin-eslint'; export default [ { input: 'src/main.js', output: { name: 'timeout', file: '/lib/tool.js', format: 'umd' }, plugins: [ resolve(), // 這樣 Rollup 能找到 `ms` commonjs(), // 這樣 Rollup 能轉換 `ms` 為一個ES模塊 eslint(), babel(), terser() ] } ];
是不是很簡單呢?個人覺得比webpack的配置簡單很多。通過如上配置,雖然能實現基本的javascript文件打包,但是還不夠健壯,接下來我們一步步來細化配置。
3.利用babel來編譯es6代碼
首先我們先安裝babel相關模塊:
npm i core-js @babel/core @babel/preset-env @babel/plugin-transform-runtime
然后設置.babelrc文件
{
"presets": [ [ "@babel/preset-env", { "modules": false, "useBuiltIns": "usage", "corejs": "2.6.10", "targets": { "ie": 10 } } ] ], "plugins": [ // 解決多個地方使用相同代碼導致打包重復的問題 ["@babel/plugin-transform-runtime"] ], "ignore": [ "node_modules/**" ] }
@babel/preset-env可以根據配置的目標瀏覽器或者運行環境來自動將ES2015+的代碼轉換為es5。需要注意的是,我們設置"modules": false,否則 Babel 會在 Rollup 有機會做處理之前,將我們的模塊轉成 CommonJS,導致 Rollup 的一些處理失敗。
為了解決多個地方使用相同代碼導致打包重復的問題,我們需要在.babelrc的plugins里配置@babel/plugin-transform-runtime,同時我們需要修改rollup的配置文件:
babel({ exclude: 'node_modules/**', // 防止打包node_modules下的文件 runtimeHelpers: true, // 使plugin-transform-runtime生效 }),
如果你對babel不太熟,可以看我之前webpack的文章或者去官網學習。
4.區分測試環境和開發環境
我們可以在package.json中配置不同的執行腳本和環境變量來對開發和生產做不同的配置:
// package.json "scripts": { "build": "NODE_ENV=production rollup -c", "dev": "rollup -c -w", "test": "node test/test.js" },
我們可以手動導出NODE_ENV為production和development來區分生產和開發環境,然后在代碼中通過process.env.NODE_ENV來獲取參數。這里我們主要用來設置在開發環境下不壓縮代碼:
const isDev = process.env.NODE_ENV !== 'production'; // ... plugins: [ !isDev && terser() ]
使用eslint來做代碼檢測
我們可以使用上面的提到的rollup-plugin-eslint來配置:
eslint({ throwOnError: true, throwOnWarning: true, include: ['src/**'], exclude: ['node_modules/**'] })
然后建立.eslintrc.js來根據自己風格配置具體檢測:
module.exports = { "env": { "browser": true, "es6": true, "node": true }, "extends": "eslint:recommended", "globals": { "Atomics": "readonly", "SharedArrayBuffer": "readonly", "ENV": true }, "parserOptions": { "ecmaVersion": 2018, "sourceType": "module" }, "rules": { "linebreak-style": [ "error", "unix" ], "quotes": [ "error", "single" ] } };
詳細的eslint配置可以去官網學習。
廣州品牌設計公司https://www.houdianzi.com
5. external屬性
使用rollup打包,我們在自己的庫中需要使用第三方庫,例如lodash等,又不想在最終生成的打包文件中出現jquery。這個時候我們就需要使用external屬性。比如我們使用了lodash,
import _ from 'lodash' // rollup.config.js { input: 'src/main.js', external: ['lodash'], globals: { lodash: '_' }, output: [ { file: pkg.main, format: 'cjs' }, { file: pkg.module, format: 'es' } ] }
6.導出模式
我們可以將自己的代碼導出成commonjs模塊,es模塊,以及瀏覽器能識別的模塊,通過如下方式設置:
{
input: 'src/main.js', external: ['ms'], output: [ { file: pkg.main, format: 'cjs' }, { file: pkg.module, format: 'es' }, { file: pkg.module, format: 'umd' } ] }
發布到npm
如果你是之前沒有注冊npm賬號,你可以通過如下方式配置:
npm adduser
然后輸入用戶名,郵箱,密碼,最后使用npm publish發布。這里介紹包的配置文件,即package.json:
{
"name": "@alex_xu/time", "version": "1.0.0", "description": "common use js time lib", "main": "dist/tool.cjs.js", "module": "dist/time.esm.js", "browser": "dist/time.umd.js", "author": "alex_xu", "homepage": "https://github.com/MrXujiang/timeout_rollup", "keywords": [ "tools", "javascript", "library", "time" ], "dependencies": { // ... }, "devDependencies": { // ... }, "scripts": { "build": "NODE_ENV=production rollup -c", "dev": "rollup -c -w", "test": "node test/test.js", "pretest": "npm run build" }, "files": [ "dist" ] }
name是包的名字,可以直接寫包名,比如loadash,或者添加域,類似於@koa/router這種,@后面是你npm注冊的用戶名。key為包的關鍵字。
發布后,我們可以用類似於下面這種方式安裝:
npm install @alex_xu/time // 使用 import { sleep } from '@alex_xu/time' // 或 const { sleep } = requrie('@alex_xu/time')
如下是安裝截圖:

在npm上也可以搜索到自己的包:

是不是很有成就感呢?快讓大家一起使用你開發的包吧!