前端組件/庫(kù)打包利器rollup使用與配置實(shí)戰(zhàn)
目前主流的前端框架vue和react都采用rollup來(lái)打包,為了探索rollup的奧妙,接下來(lái)就讓我們一步步來(lái)探索,并基于rollup搭建一個(gè)庫(kù)打包腳手架,來(lái)發(fā)布自己的庫(kù)和組件。

前言
寫(xiě)rollup的文章是因?yàn)楣P者最近要規(guī)范前端開(kāi)發(fā)的業(yè)務(wù)流程和架構(gòu),并提供內(nèi)部公有組件庫(kù)和工具庫(kù)供團(tuán)隊(duì)使用。在查閱大量資料并對(duì)比了webpack和rollup的優(yōu)缺點(diǎn)之后,最終選擇rollup來(lái)作為打包工具,我們最終要實(shí)現(xiàn)通過(guò)npm的方式安裝我們的組件庫(kù)和工具庫(kù):
// 安裝
npm install @xuxi/tools
// 使用
import { sleep } from '@xuxi/tools'
下面我們一步步來(lái)復(fù)盤rollup的配置過(guò)程和最佳實(shí)踐。
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.
意思大致是說(shuō)Rollup 是一個(gè) JavaScript 模塊打包器,可以將小塊代碼編譯成大塊復(fù)雜的代碼,例如 library 或應(yīng)用程序。Rollup 對(duì)代碼模塊使用新的標(biāo)準(zhǔn)化格式,這些標(biāo)準(zhǔn)都包含在 JavaScript 的 ES6 版本中,而不是像CommonJS 和 AMD這種特殊解決方案。
rollup最大的亮點(diǎn)就是Tree-shaking,即可以靜態(tài)分析代碼中的 import,并排除任何未使用的代碼。這允許我們架構(gòu)于現(xiàn)有工具和模塊之上,而不會(huì)增加額外的依賴或使項(xiàng)目的大小膨脹。如果用webpack做,雖然可以實(shí)現(xiàn)tree-shaking,但是需要自己配置并且打包出來(lái)的代碼非常臃腫,所以對(duì)于庫(kù)文件和UI組件,rollup更加適合。
搭建庫(kù)打包腳手架
1. rollup入門
首先我們安裝一下rollup:
npm i rollup -g
然后在本地創(chuàng)建一個(gè)項(xiàng)目:
mkdir -p my-project
cd my-project
其次我們創(chuàng)建一個(gè)入口并寫(xiě)入如下代碼:
// src/main.js
import say from './say.js';
export { say }
// src/say.js
export default function(name){
console.log(name)
};
基本代碼準(zhǔn)備好了之后,我們寫(xiě)rollup的配置文件(rollup.config.js在根目錄下):
// rollup.config.js
export default {
input: 'src/main.js',
output: {
file: 'bundle.js',
format: 'cjs'
}
};
這樣,我們?cè)诮K端執(zhí)行:
// --config 或 -c 來(lái)使用配置文件
rollup -c
這樣在更目錄下就生成了一個(gè)bundle.js,就是我們想要的打包后的文件。我們也可以用package.json來(lái)設(shè)置打包配置信息,用npm run xxx來(lái)打包和測(cè)試代碼。
2.rollup插件使用
為了更靈活的打包庫(kù)文件,我們可以配置rollup插件,比較實(shí)用的插件有:
rollup-plugin-node-resolve —幫助 Rollup 查找外部模塊,然后導(dǎo)入
rollup-plugin-commonjs —將CommonJS模塊轉(zhuǎn)換為 ES2015 供 Rollup 處理
rollup-plugin-babel — 讓我們可以使用es6新特性來(lái)編寫(xiě)代碼
rollup-plugin-terser — 壓縮js代碼,包括es6代碼壓縮
rollup-plugin-eslint — js代碼檢測(cè)
打包一個(gè)庫(kù)用以上插件完全夠用了,不過(guò)如果想實(shí)現(xiàn)對(duì)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 能轉(zhuǎn)換 `ms` 為一個(gè)ES模塊
eslint(),
babel(),
terser()
]
}
];
是不是很簡(jiǎn)單呢?個(gè)人覺(jué)得比webpack的配置簡(jiǎn)單很多。通過(guò)如上配置,雖然能實(shí)現(xiàn)基本的javascript文件打包,但是還不夠健壯,接下來(lái)我們一步步來(lái)細(xì)化配置。
3.利用babel來(lái)編譯es6代碼
首先我們先安裝babel相關(guān)模塊:
npm i core-js @babel/core @babel/preset-env @babel/plugin-transform-runtime
然后設(shè)置.babelrc文件
{
"presets": [
[
"@babel/preset-env",
{
"modules": false,
"useBuiltIns": "usage",
"corejs": "2.6.10",
"targets": {
"ie": 10
}
}
]
],
"plugins": [
// 解決多個(gè)地方使用相同代碼導(dǎo)致打包重復(fù)的問(wèn)題
["@babel/plugin-transform-runtime"]
],
"ignore": [
"node_modules/**"
]
}
@babel/preset-env可以根據(jù)配置的目標(biāo)瀏覽器或者運(yùn)行環(huán)境來(lái)自動(dòng)將ES2015+的代碼轉(zhuǎn)換為es5。需要注意的是,我們?cè)O(shè)置"modules": false,否則 Babel 會(huì)在 Rollup 有機(jī)會(huì)做處理之前,將我們的模塊轉(zhuǎn)成 CommonJS,導(dǎo)致 Rollup 的一些處理失敗。
為了解決多個(gè)地方使用相同代碼導(dǎo)致打包重復(fù)的問(wèn)題,我們需要在.babelrc的plugins里配置@babel/plugin-transform-runtime,同時(shí)我們需要修改rollup的配置文件:
babel({
exclude: 'node_modules/**', // 防止打包node_modules下的文件
runtimeHelpers: true, // 使plugin-transform-runtime生效
}),
如果你對(duì)babel不太熟,可以看我之前webpack的文章或者去官網(wǎng)學(xué)習(xí)。
4.區(qū)分測(cè)試環(huán)境和開(kāi)發(fā)環(huán)境
我們可以在package.json中配置不同的執(zhí)行腳本和環(huán)境變量來(lái)對(duì)開(kāi)發(fā)和生產(chǎn)做不同的配置:
// package.json
"scripts": {
"build": "NODE_ENV=production rollup -c",
"dev": "rollup -c -w",
"test": "node test/test.js"
},
我們可以手動(dòng)導(dǎo)出NODE_ENV為production和development來(lái)區(qū)分生產(chǎn)和開(kāi)發(fā)環(huán)境,然后在代碼中通過(guò)process.env.NODE_ENV來(lái)獲取參數(shù)。這里我們主要用來(lái)設(shè)置在開(kāi)發(fā)環(huán)境下不壓縮代碼:
const isDev = process.env.NODE_ENV !== 'production';
// ...
plugins: [
!isDev && terser()
]
使用eslint來(lái)做代碼檢測(cè)
我們可以使用上面的提到的rollup-plugin-eslint來(lái)配置:
eslint({
throwOnError: true,
throwOnWarning: true,
include: ['src/**'],
exclude: ['node_modules/**']
})
然后建立.eslintrc.js來(lái)根據(jù)自己風(fēng)格配置具體檢測(cè):
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"
]
}
};
詳細(xì)的eslint配置可以去官網(wǎng)學(xué)習(xí)。
5. external屬性
使用rollup打包,我們?cè)谧约旱膸?kù)中需要使用第三方庫(kù),例如lodash等,又不想在最終生成的打包文件中出現(xiàn)jquery。這個(gè)時(shí)候我們就需要使用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.導(dǎo)出模式
我們可以將自己的代碼導(dǎo)出成commonjs模塊,es模塊,以及瀏覽器能識(shí)別的模塊,通過(guò)如下方式設(shè)置:
{
input: 'src/main.js',
external: ['ms'],
output: [
{ file: pkg.main, format: 'cjs' },
{ file: pkg.module, format: 'es' },
{ file: pkg.module, format: 'umd' }
]
}
發(fā)布到npm
如果你是之前沒(méi)有注冊(cè)npm賬號(hào),你可以通過(guò)如下方式配置:
npm adduser
然后輸入用戶名,郵箱,密碼,最后使用npm publish發(fā)布。這里介紹包的配置文件,即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是包的名字,可以直接寫(xiě)包名,比如loadash,或者添加域,類似于@koa/router這種,@后面是你npm注冊(cè)的用戶名。key為包的關(guān)鍵字。
發(fā)布后,我們可以用類似于下面這種方式安裝:
npm install @alex_xu/time
// 使用
import { sleep } from '@alex_xu/time'
// 或
const { sleep } = requrie('@alex_xu/time')
如下是安裝截圖:

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

是不是很有成就感呢?快讓大家一起使用你開(kāi)發(fā)的包吧!
最后
完整配置文件我已經(jīng)發(fā)布到github,如果想了解更多webpack,gulp,css3,javascript,nodeJS,canvas等前端知識(shí)和實(shí)戰(zhàn),歡迎在公眾號(hào)《趣談前端》加入我們一起學(xué)習(xí)討論,共同探索前端的邊界。
更多推薦
基于nodeJS從0到1實(shí)現(xiàn)一個(gè)CMS全棧項(xiàng)目的服務(wù)端啟動(dòng)細(xì)節(jié)
用 webpack 4.0 擼單頁(yè)/多頁(yè)腳手架 (jquery, react, vue, typescript)
笛卡爾乘積的javascript版實(shí)現(xiàn)和應(yīng)用 Canvas入門實(shí)戰(zhàn)之用javascript面向?qū)ο髮?shí)現(xiàn)一個(gè)圖形驗(yàn)證碼
歡迎關(guān)注下方公眾號(hào),獲取更多前端知識(shí)精粹和學(xué)習(xí)社群:
