一、概念
Rollup 是一个 JavaScript 模块打包器,可应用于打包 application 或 library。但 Rollup 更专注于打包 JavaScript 类库(库、组件)。
特点:
- Tree-shaking:静态分析 ESModule 代码,并排除未使用到的代码;
- 相较 Webpack,它轻量、可快速构建 library;
- Vue、React、Angular、React-Redux 等这些框架/库都在用 Rollup 作为打包工具。
命令行参数:
-c, --config <filename> 使用配置文件(如果使用参数但是值没有
指定, 默认就是 rollup.config.js)
-d, --dir <dirname> 构建块的目录(如果不存在,就打印到标准输出)
-e, --external <ids> 逗号分隔列出排除的模块 ID
-f, --format <format> 输出类型 (amd, cjs, es, iife, umd, system)
-g, --globals <pairs> 逗号分隔列出 `moduleID:Global` 对
-h, --help 显示帮助信息
-i, --input <filename> 输入 (替代 <entry file>)
-m, --sourcemap 生成 sourcemap (`-m inline` 生成行内 map)
-n, --name <name> UMD 导出的名字
-o, --file <output> 单个的输出文件(如果不存在,就打印到标准输出)
-p, --plugin <plugin> 使用指定的插件(可能重复)
-v, --version 显示版本号
-w, --watch 监听 bundle 中的文件并在文件改变时重新构建
--amd.id <id> AMD 模块 ID(默认是匿名的)
--amd.define <name> 代替 `define` 使用的功能
--assetFileNames <pattern> 构建的资源命名模式
--banner <text> 插入 bundle 顶部(包装器之外)的代码
--chunkFileNames <pattern> 次要构建块的命名模式
--compact 压缩包装器代码
--context <variable> 指定顶层的 `this` 值
--entryFileNames <pattern> 入口构建块的命名模式
--environment <values> 设置传递到配置文件 (看示例)
--no-esModule 不增加 __esModule 属性
--exports <mode> 指定导出的模式 (auto, default, named, none)
--extend 通过 --name 定义,拓展全局变量
--no-externalLiveBindings 不生成实施绑定的代码
--footer <text> 插入到 bundle 末尾的代码(包装器外部)
--no-freeze 不冻结命名空间对象
--no-hoistTransitiveImports 不提升传递性的导入到入口构建块
--no-indent 结果中不进行缩进
--no-interop 不包含互操作块
--inlineDynamicImports 使用动态导入时创建单个 bundle
--intro <text> 在 bundle 顶部插入代码(包装器内部)
--minifyInternalExports 强制或者禁用内部导出的压缩
--namespaceToStringTag 为命名空间创建正确的 `.toString` 方法
--noConflict 为 UMD 全局变量生成 noConflict 方法
--outro <text> 在 bundle 的末尾插入代码(包装器内部)
--preferConst 使用 `const` 代替 `var` 进行导出
--no-preserveEntrySignatures 避免表面的构建块作为入口
--preserveModules 保留模块结构
--preserveSymlinks 解析文件时不要遵循符号链接
--shimMissingExports 给丢失的导出创建填充变量
--silent 不打印警告
--sourcemapExcludeSources source map 中不包含源码
--sourcemapFile <file> source map 中指定 bundle 的路径
--no-stdin 不从标准输入中读取 "-"
--no-strict 在生成的模块中不使用 `"use strict";`
--strictDeprecations 不推荐使用的特性抛出错误
--systemNullSetters 用 `null` 替换空的 SystemJS setter
--no-treeshake 禁用 tree-shaking 优化
--no-treeshake.annotations 忽略纯的调用注释
--no-treeshake.moduleSideEffects 假设模块没有副作用
--no-treeshake.propertyReadSideEffects 忽略属性访问的副作用
--no-treeshake.tryCatchDeoptimization 不关闭 try-catch-tree-shaking
--no-treeshake.unknownGlobalSideEffects 假设未知的全局变量不抛出
--waitForBundleInput 等待 bundle 的输入文件
--watch.buildDelay <number> 监听重新构建的延时
--no-watch.clearScreen 重新构建时不进行清屏
--watch.skipWrite 监听时不写入文件到磁盘
--watch.exclude <files> 监听时排除的文件
--watch.include <files> 限制监听指定的文件
下面我们从一个 Demo 入手,一步一步扩展和熟悉 Rollup 实战应用。
二、搭建环境
1、初始化工程并安装 rollup:
mkdir rollup-demo && cd rollup-demo && yarn init -y && yarn add rollup -D
2、新建入口文件:
// src/index.js
export const fn = () => {
console.log('hello rollup');
}
3、命令执行打包:
和 webpack 相似,在没有配置文件情况下,可以通过 cli 进行最低配置基础打包。
npx rollup -i src/index.js -o dist/bundle.js -f cjs
上面命令解释为:
-i指定要打包的文件,-i是--input的缩写。src/index.js是打包入口文件。-o指定输出的文件,是 --output.file 或 --file 的缩写。(如果没有这个参数,则直接输出到控制台)dist/bundle.js是输出文件。-f指定打包文件的格式,-f 是 --format 的缩写,esm 表示使用 ES6 模块规范,cjs 表示 CommonJS,格式种类有:amd, cjs, esm, iife, umd。
4、配置文件:
由于 cli 方式打包配置比较薄弱,下面我们在工程根目录下新建 rollup.config.js 作为 Rollup 配置文件:
export default {
input: "./src/index.js",
output: [
{
file: './dist/my-lib-umd.js',
format: 'umd',
name: 'myLib', // umd 格式必须指定 name
},
{
file: './dist/my-lib-es.js',
format: 'es'
},
{
file: './dist/my-lib-cjs.js',
format: 'cjs'
}
],
plugins: [], // 配置插件
}
5、配置脚本:
在 package.json 下配置打包执行脚本。
"scripts": {
"build": "rollup -c rollup.config.js"
},
现在,我们就可以通过 yarn build 或者 npm run build 进行打包。
至此,基础的环境已经搭建完成,下面我们通过 Plugin 插件来完善我们的工程。
三、常用插件配置
1、@rollup/plugin-node-resolve
用于处理 import 一个模块时的解析规则,模块可以是本工程内的文件,也可以是 node_modules 第三方依赖模块,有了它,import 一个目录时会默认指向目录下的 index 文件。
// 不配置 @rollup/plugin-node-resolve 插件引入方式
import module from './module/index';
// 配置了 @rollup/plugin-node-resolve 插件引入方式
import module from './module';
- 安装:
yarn add @rollup/plugin-node-resolve -D
- 配置:
import resolve from '@rollup/plugin-node-resolve';
const extensions = [ '.ts', '.tsx', '.js', '.jsx'];
plugins: [
resolve({
extensions, // 指定 import 模块后缀解析规则
}),
...
]
2、@rollup/plugin-commonjs
rollup 默认只支持 ES6+ 模块方式的 import / export,由于 node_modules 中大多数包都是 CommonJS 方式,导致无法直接引入使用,这就需要配置 CommonJS 插件。
- 安装:
yarn add @rollup/plugin-commonjs -D
- 配置:
import commonjs from '@rollup/plugin-commonjs';
plugins: [
...
commonjs(),
],
3、@rollup/plugin-babel
这个大家都很熟悉,babel,就是转换 ES6+ 语法、api 为浏览器都识别的基础语法。
- 安装:
yarn add @rollup/plugin-babel @babel/core @babel/cli @babel/preset-env -D
yarn add @babel/plugin-transform-runtime -D
yarn add @babel/runtime
- 配置:
import { babel } from '@rollup/plugin-babel';
const extensions = ['.js','.jsx','.ts','.tsx'];
export default {
input: 'src/index.js',
output: {
dir: 'dist',
format: 'umd',
name: 'myLib',
},
external: [
/@babel\/runtime/
],
plugins: [
...
babel({
extensions,
presets: [
'@babel/preset-env'
],
plugins: [
'@babel/transform-runtime'
],
babelrc: false, // 忽略工程内的 babel 配置文件,使用 rollup 这里的配置
babelHelpers: 'runtime', // 当工程作为程序应用时推荐使用 bundled(默认值),当构建库时推荐使用 runtime。
})
]
};
注意这三个插件顺序:@rollup/plugin-commonjs 要在 @rollup/plugin-babel 配置之前,在 @rollup/plugin-node-resolve 配置之后。
4、@rollup/plugin-alias
进行路径别名。类似于 webpack.resolve.alias,一般我们会配置 @ 代替 src 目录来简化 import Path。
- 安装:
yarn add @rollup/plugin-alias -D
- 配置:
import alias from '@rollup/plugin-alias';
plugins: [
...
alias({
entries: [
{ find: '@', replacement: path.resolve(__dirname, 'src') },
],
}),
]
- 使用:
import module1 from '@/module1'; // src/module1.js
另外,如果想让编辑器(VSCode)鼠标移动在路径上通过 command + 点击快速进入到模块文件,可以新建 jsconfig.json 进行配置:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
5、rollup-plugin-terser
用于压缩代码,减少代码体积(代码压缩至一行、删除代码内注释)。
- 安装:
yarn add rollup-plugin-terser -D
- 配置:
import { terser } from "rollup-plugin-terser";
plugins: [
...
terser(),
]
6、@rollup/plugin-replace
埋入环境变量。比如让打包资源支持对 process.env.NODE_ENV 变量的访问。
- 安装:
yarn add @rollup/plugin-replace -D
- 配置:
import replace from '@rollup/plugin-replace';
plugins: [
...
replace({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
}),
]
四、支持 TypeScript
Rollup 团队提供了一个无缝连接 TypeScript 插件:@rollup/plugin-typescript,但自己在使用过程中发现编译工作会与 @rollup/plugin-eslint 配置的 rules 产生冲突,如:no-var,最后决定采用 babel 来转换 TS。
- 安装:
yarn add typescript @babel/preset-typescript -D
- 配置:
babel({
presets: [
...
["@babel/preset-typescript", {
"isTSX": true,
"allExtensions": true,
}]
],
}),
- 注意:
将 ts 转换工作交给 babel 处理后,它不会输出
.d.ts类型声明文件,这一工作需要手动执行 ts 命令进行输出,特别注意在tsconfig.json中需要进行以下配置:
{
"compilerOptions": {
"noEmit": false, // 关闭不输出开关
"emitDeclarationOnly": true, // 仅输出类型声明文件
"declaration": true, /* Generates corresponding '.d.ts' file. */
"declarationDir": "types", // 类型声明文件输出目录
...
},
}
- 执行脚本:
"scripts": {
"build:types": "yarn run tsc",
}
五、接入 React
由于团队内选用的组件库技术栈为 React,下面我们通过配置让 Rollup 打包支持 React。
- 安装:
yarn add react react-dom
yarn add @babel/preset-react @types/react @types/react-dom -D
- 配置:
plugins: [
babel({
...
presets: [
...
'@babel/preset-react'
],
}),
]
- 使用:
import React from 'react';
import ReactDOM from 'react-dom';
const App = () => {
return (
<div>
Hello React!
</div>
)
}
ReactDOM.render(<App />, document.getElementById('app'));
注意,React JSX 语法必须在 .jsx / .tsx 文件内使用,否则 Rollup 在打包时会无法识别 JSX 语法。
六、外部扩展
外部扩展(external),和 webpack external 配置一致,防止将某些 import 的第三方以来,打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖。
比如:react、react-dom,如果不排除,默认会将它们全量代码进行打包。
一般构建一个 library,而非 application 时,这个配置非常有用。
- 配置:
output: {
dir: 'dist',
format: 'cjs',
name: 'myLib',
globals: {
'react': 'React', // key 为包名,value 为包暴露给 window 上的变量名
'react-dom': 'ReactDOM',
},
},
external: [
'react',
'react-dom',
],
七、ESLint
一个好的工程,离不开 ESLint 去约束开发者的编程习惯。下面我们配置 ESLint 接入到 Rollup 打包流程中,并且支持 React、TypeScript 相关的 ESLint 校验。
- 安装:
yarn add eslint @rollup/plugin-eslint -D
yarn add @typescript-eslint/eslint-plugin @typescript-eslint/parser -D
yarn add eslint-plugin-react -D
- Rollup 接入 ESLint:
import eslint from '@rollup/plugin-eslint';
plugins: [
eslint(), // 使用 .eslintrc.js 配置文件
]
- ESLint 配置文件:
// .eslintrc.js
module.exports = {
"env": { // 指定代码的运行环境
"browser": true,
"es6": true
},
// 使用 eslint-plugin-react 插件时需要指定 React 版本
"settings": {
"react": {
"version": "17" // or "detect"
}
},
"extends": [
"plugin:react/recommended"
],
"plugins": [
"react",
"@typescript-eslint"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": "latest", // 支持 ES 最新版本的语法校验
"sourceType": "module" // 设置 ECMAScript modules
},
// rules: https://eslint.bootcss.com/docs/rules/
"rules": {
// 禁止出现定义了,但未使用过的变量
"no-unused-vars": "warn",
// 阻止 var 的使用,推荐用 let 和 const
"no-var": "warn",
}
};
// .eslintignore
node_modules
dist
注意:关于 .eslintignore 在 VSCode 编辑器下不生效情况:如果 eslintignore 配置文件,不在 VSCode 打开的工程跟目录下,可能造成不生效。
- 执行脚本:
"scripts": {
"lint": "eslint src --ext .js,.jsx,.ts,.tsx",
"lint-fix": "eslint src --ext .js,.jsx,.ts,.tsx --fix"
}
八、本地服务
通常我们会有一个本地服务来运行和调试代码。需要注意的是:rollup serve 并不会像 webpack-dev-server 那样开发环境下将打包资源生成在内存中,而是需要先进行打包,然后 HTML 文件中引入打包后的资源。
- 安装:
yarn add rollup-plugin-serve rollup-plugin-livereload -D
- 配置:
import serve from 'rollup-plugin-serve';
import livereload from 'rollup-plugin-livereload';
const isEnvProduction = process.env.NODE_ENV === 'production';
plugins: [
!isEnvProduction && serve({
contentBase: '', // 服务器启动的文件夹,默认是项目根目录,需要在该文件下创建index.html
port: 3000
}),
!isEnvProduction && livereload('dist'), // watch dist目录,当目录中的文件发生变化时,刷新页面
],
注意:我们需要提供 index.html template 文件,并且需要手动引入打包后的 js 和 css 资源。
九、样式处理
Rollup 提供了 rollup-plugin-postcss 插件来解析 css 文件。
- 安装:
yarn add postcss rollup-plugin-postcss -D
- 配置:
import postcss from 'rollup-plugin-postcss';
plugins: [
...
postcss(),
]
- 提取 CSS: 默认 Rollup 会将样式通过 style 标签注入到 head 标签内,如果想提取 CSS,可配置:
plugins: [
postcss({
minimize: true, // 压缩 css
extract: true, // 默认提取的样式文件名为:index.css
// extract: path.resolve('dist/my.css'), // 自定义样式文件名
}),
]
- 支持 sass: 只需要安装 node-sass,即可支持 .scss/.sass 文件解析:
yarn add node-sass@^6.0.0 -D
-
支持浏览器厂商前缀:
- 安装:
yarn add autoprefixer -D- 配置:
import autoprefixer from 'autoprefixer'; plugins: [ ... postcss({ plugins: [ autoprefixer(), ] }), ]autoprefixer 除了上面的配置,还需要指定 browserslist 来确定我们程序运行的目标环境,避免去兼容程序不考虑的浏览器环境。我们在 package.json 下:
"browserslist": [ "> 1%", // 全球超过 1% 人使用的浏览器 "last 2 versions", // 所有浏览器兼容到最后两个版本,比如 IE 的最新版本为 11,向后兼容两个版本即为 10、11 "not ie <= 8" // 排除指定的浏览器版本 ] -
自动引入 css 文件: 在打包后,如果希望打包出的 js 资源自动去引入 css 文件,可以在 output 配置:
output: {
format: 'es',
dir: 'dist',
banner: 'import "index.css"', // 自动引入样式文件
},
十、图片资源
类似于 webpack url-loader,在 Rollup 中可以使用 @rollup/plugin-url 处理图片 base64 资源。
- 安装:
yarn add @rollup/plugin-url -D
- 配置:
import url from '@rollup/plugin-url';
plugin: [
url({
limit: 10240, // 大于10k,打包生成单独静态资源,否则处理成 base64
fileName: 'assets/[name].[hash][extname]',
}),
]
十一、环境变量
cross-env 是跨平台设置和使用环境变量的脚本。由于在大多数 Windows 命令行中在使用 NODE_ENV = production 设置环境变量时会报错,使用 cross-env 可以设置在不同的平台上有相同的 NODE_ENV 参数。
- 安装:
yarn add cross-env -D
- 执行脚本:
"scripts": {
"build": "cross-env NODE_ENV=production rollup -c rollup.config.js",
},
- 配置中读取环境变量:
const isEnvProduction = process.env.NODE_ENV === 'production';
plugins: [
isEnvProduction && terser(),
]
十二、代码分割,将每个模块文件作为独立文件输出
按照源码的目录结构进行输出,常用于构建组件库实现按需加载。使用示例如下:
import { babel } from "@rollup/plugin-babel";
const extensions = [".js", ".jsx", ".ts", ".tsx"];
export default {
input: "lib/index.js",
output: {
dir: "es/",
format: "es",
preserveModules: true, // 关键
},
plugins: [
babel({
extensions,
presets: ["@babel/preset-env"],
babelrc: false, // 忽略工程内的 babel 配置文件,使用 rollup 这里的配置
}),
],
};
preserveModules 的使用参考:rollup.nodejs.cn/configurati…
最后
可以看到,一个完整的工程配置还是很多的,比如还可以加入 Git Commit 相关规范校验、组件库快速生成文档等功能。
感谢阅读。
- Git Commit 规范配置可参考:Commit message 使用指南
- 组件库文档生成可参考:dumi 文档说明