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