Rollup 是一个用于 JavaScript 的模块打包器 https://rollupjs.org/guide/en/#overview
为什么不是webpack
- webpack 借助各种loader、plugin 帮我们解决各种问题 ,但是生成的代码有很多自由的模块加载功能,繁琐的配置及打包后的文件体积,不适合开发js库,所以更适合项目开发。
- rollup 只是把源文件生成目标js代码,所以更适合做js库的开发。
快速开始
1.npm 初始化,生成package.json
npm init
1.安装 rollup ,创建rollup.config.js
yarn add rollup -D
3.添加.gitignore文件
/node_modules
/dist
/example/dist
4.添加 README.md
### rollup-test
#### 本地启动 `yarn dev`
#### 打包 `yarn build`
5.创建src目录,用于编写要打包的代码
常用配置
input
项目入口文件
export default {
input: 'src/index.jsx',
}
output
项目输出文件,支持数组
export default {
input: 'src/index.jsx',
output: {
file: 'dist/bundle.js',
format: 'umd',
},
}
export default {
input: 'src/index.jsx',
output: [
{
file: 'dist/bundle.es.js',
format: 'esm',
},
{
file: 'dist/bundle.umd.js',
format: 'umd',
name: 'ReactUI'
}
],
}
plugins
各种插件的配置位置
export default {
input: 'src/index.jsx',
output: {
file: 'example/dist/bundle.js',
format: 'umd',
},
plugins: [
resolve(),
commonjs(),
json(),
babel({
exclude: 'node_modules/**',
}),
]
}
external
配置第三方库不打包,而是使用外部依赖(需要单独引入)
export default {
external: ['react', 'react-dom'], // 配置第三方库不打包,而是使用外部依赖
input: 'src/index.jsx',
output: {
file: 'example/dist/bundle.js',
format: 'umd',
},
plugins: []
}
深入使用
使用babel
安装依赖
yarn add -D @rollup/plugin-babel @babel/core core-js@2 @babel/preset-env @babel/preset-react @babel/plugin-proposal-decorators
配置 rollup.config.js
import babel from '@rollup/plugin-babel';
export default {
external: ['react', 'react-dom'], // 配置第三方库不打包,而是使用外部依赖
input: 'src/index.jsx',
output: {
file: 'example/dist/bundle.js',
format: 'umd',
},
plugins: [
babel({
exclude: 'node_modules/**',
}),
]
}
配置 .babelrc
{
"presets": [
"@babel/preset-react", // react预设环境
[
"@babel/preset-env",
{
"modules": false, // 设置 "modules": false ,否则 Babel 会在 Rollup 有机会做处理之前,将我们的模块转成 CommonJS ,导致 Rollup 的一些处理失败。
"corejs": 2,
"useBuiltIns": "usage" // 只加载使用的polyfills
}
]
],
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }], // 类装饰器使用
"@babel/plugin-proposal-class-properties" // 支持 class 属性转化
]
}
node模块的引用
安装依赖
yarn add -D @rollup/plugin-node-resolve @rollup/plugin-commonjs
配置rollup.config.js
import babel from '@rollup/plugin-babel';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
export default {
external: ['react', 'react-dom'], // 配置第三方库不打包,而是使用外部依赖
input: 'src/index.jsx',
output: {
file: 'example/dist/bundle.js',
format: 'umd',
},
plugins: [
//---new----
resolve({ extensions }), // 在node_modules中找到并捆绑第三方依赖项
commonjs(), // 用来将 CommonJS 转换成 ES6 模块
babel({
exclude: 'node_modules/**',
}),
]
}
typescript支持
编译ts 有两种方案,babel编译、tsc编译,以下介绍两种方案的接入。两种方案都需要安装typescript
yarn add -D typescript
配置tsconfig.json
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"moduleResolution": "node",
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "react",
"lib": ["es2015", "es2016", "es2017", "dom"],
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
},
"declarationDir": "types",
"declaration": true,
},
"include": [
"src/**/*"
],
"exclude":[
"node_modules/**"
]
}
方案一:tsc编译
安装依赖
yarn add -D @rollup/plugin-typescript tslib
配置rollup.config.js
import typescript from '@rollup/plugin-typescript';
export default {
input: 'src/index.jsx',
output: {
file: 'dist/bundle.js',
format: 'umd',
},
plugins: [
typescript({
tsconfig: './tsconfig.json', // 指定ts配置文件
}),
]
}
配置执行脚本
"build": "rm -rf dist && rollup -c ./build/rollup.build.js",
方案二:babel编译
使用 @babel/preset-typescript 预设支持ts编译,配合 tsc 做类型检查、输出声明文件等
安装依赖
yarn add -D @babel/preset-typescript
配置.babelrc
{
"presets": [
"@babel/preset-react",
"@babel/typescript",
[
"@babel/preset-env",
{
"modules": false, // 设置 "modules": false ,否则 Babel 会在 Rollup 有机会做处理之前,将我们的模块转成 CommonJS ,导致 Rollup 的一些处理失败。
"corejs": 2,
"useBuiltIns": "usage" // 只加载使用的polyfills
}
]
],
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
"@babel/plugin-proposal-class-properties"
]
}
配置rollup.config.js
import babel from '@rollup/plugin-babel';
const extensions = [
'.js', '.jsx', '.ts', '.tsx',
];
export default {
input: 'src/index.jsx',
output: {
file: 'example/dist/bundle.js',
format: 'umd',
},
plugins: [
babel({
extensions,
exclude: 'node_modules/**',
}),
]
}
ts类型检查
配置tsc执行脚本
"type-check": "tsc --noEmit",
"type-check:watch": "tsc --noEmit --watch"
输出ts声明文件
新建 tsconfig.types.json 文件,只生成声明文件,不生成js文件
{ // 继承 tsconfig.json 中的通用配置 "extends": "./tsconfig.json", "compilerOptions": { "declaration": true, /* 生成相应的 '.d.ts' file. */ "declarationDir": "./dist/types", /* 类型声明文件输出目录 */ "emitDeclarationOnly": true, /* 只生成声明文件,不生成 js 文件*/ "rootDir": "./src", /* 指定输出文件目录(用于输出),用于控制输出目录结构 */ } }
配置执行脚本 ```json “build:babel”: “rm -rf dist && yarn build:types && rollup -c ./build/rollup.build.babel.js”,
“build:types”: “tsc -b ./tsconfig.types.json”,
<a name="prJw5"></a>
### 总结
<a name="XJsQg"></a>
#### 方案一(@rollup/plugin-typescript)
优点:
1. 通过rollup插件编译,配置简单
缺点:
1. 每次修改都要经过 编译、类型检查,可能比较慢
1. 对于新api,如 Promise Set Map 或者 Object.assign Array.includes 等,ts编译器是不会做处理的,需要单独引入所需的polyfill。
1. rollup 插件编译ts,如何和 babel/polyfill 完美结合 ?
<a name="imFhS"></a>
#### 方案二(@babel/preset-typescript)
优点:
1. babel7 开始支持ts,通过预设@babel/preset-typescript 来实现
1. babel7.4 移除了babel/polyfill ,参考:[https://babeljs.io/docs/en/babel-polyfill](https://babeljs.io/docs/en/babel-polyfill)
1. babel7.4 支持corejs 按需加载,配合 useBuiltIns: usage 使用
缺点:
1. 不支持ts类型校验,需要配合 tsc 做类型校验
1. babel 是不支持编译部分语法的(不过不影响)
<a name="DUQj7"></a>
#### 最好的选择
综上所述,最好的方案就是 ,babel做编译,tsc做校验,两者结合。
<a name="nWi1Q"></a>
## 压缩代码
<a name="tHO7c"></a>
### 安装依赖
```shell
yarn add -D rollup-plugin-terser
配置rollup.config.js
import babel from '@rollup/plugin-babel';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import { terser } from 'rollup-plugin-terser';
export default {
external: ['react', 'react-dom'], // 配置第三方库不打包,而是使用外部依赖
input: 'src/index.jsx',
output: {
file: 'example/dist/bundle.js',
format: 'umd',
},
plugins: [
resolve({ extensions }), // 在node_modules中找到并捆绑第三方依赖项
commonjs(), // 用来将 CommonJS 转换成 ES6 模块
babel({
exclude: 'node_modules/**',
}),
//---new----
terser(), // 压缩js代码
]
}
编译css
安装依赖
yarn add -D rollup-plugin-postcss postcss cssnano autoprefixer
配置rollup.config.js
import babel from '@rollup/plugin-babel';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import { terser } from 'rollup-plugin-terser';
import postcss from 'rollup-plugin-postcss';
import autoprefixer from 'autoprefixer';
import cssnano from 'cssnano';
export default {
external: ['react', 'react-dom'], // 配置第三方库不打包,而是使用外部依赖
input: 'src/index.jsx',
output: {
file: 'example/dist/bundle.js',
format: 'umd',
},
plugins: [
resolve({ extensions }), // 在node_modules中找到并捆绑第三方依赖项
commonjs(), // 用来将 CommonJS 转换成 ES6 模块
babel({
exclude: 'node_modules/**',
}),
terser(), // 压缩js代码
//---new----
postcss({
plugins: [
autoprefixer(), // 加前缀
cssnano(), // 压缩css
],
extract: 'css/index.css', // 单独抽离css文件
}),
]
}
支持预编译(less)
安装less ,即可引入 .less 文件。stylus/sass 的支持,分别安装 stylus/node-sass 既可。
yarn add -D less
环境变量
安装依赖
yarn add -D rollup-plugin-replace
配置rollup.config.js
import babel from '@rollup/plugin-babel';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import { terser } from 'rollup-plugin-terser';
import postcss from 'rollup-plugin-postcss';
import autoprefixer from 'autoprefixer';
import cssnano from 'cssnano';
import replace from 'rollup-plugin-replace';
export default {
external: ['react', 'react-dom'], // 配置第三方库不打包,而是使用外部依赖
input: 'src/index.jsx',
output: {
file: 'example/dist/bundle.js',
format: 'umd',
},
plugins: [
resolve({ extensions }), // 在node_modules中找到并捆绑第三方依赖项
commonjs(), // 用来将 CommonJS 转换成 ES6 模块
babel({
exclude: 'node_modules/**',
}),
terser(), // 压缩js代码
postcss({ // css处理
plugins: [
autoprefixer(), // 加前缀
cssnano(), // 压缩css
],
extract: 'css/index.css', // 单独抽离css文件
}),
//---new----
replace({ // 设置环境变量
'process.env.NODE_ENV': JSON.stringify('production')
})
]
}
本地服务器
安装依赖
yarn add -D rollup-plugin-serve
配置rollup.config.js
import path from 'path';
import babel from '@rollup/plugin-babel';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import { terser } from 'rollup-plugin-terser';
import postcss from 'rollup-plugin-postcss';
import autoprefixer from 'autoprefixer';
import cssnano from 'cssnano';
import replace from 'rollup-plugin-replace';
import serve from 'rollup-plugin-serve';
export default {
external: ['react', 'react-dom'], // 配置第三方库不打包,而是使用外部依赖
input: 'src/index.jsx',
output: {
file: 'example/dist/bundle.js',
format: 'umd',
},
plugins: [
resolve({ extensions }), // 在node_modules中找到并捆绑第三方依赖项
commonjs(), // 用来将 CommonJS 转换成 ES6 模块
babel({
exclude: 'node_modules/**',
}),
terser(), // 压缩js代码
postcss({ // css处理
plugins: [
autoprefixer(), // 加前缀
cssnano(), // 压缩css
],
extract: 'css/index.css', // 单独抽离css文件
}),
//---new----
serve({
// open: true, // 自动打开页面
port: 8000,
sourcemap: true,
contentBase: path.join(__dirname, '../example'),
}),
replace({ // 设置环境变量
'process.env.NODE_ENV': JSON.stringify('production')
})
]
}
开启热更新
安装依赖
yarn add -D rollup-plugin-livereload
配置rollup.config.js
import path from 'path';
import babel from '@rollup/plugin-babel';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import { terser } from 'rollup-plugin-terser';
import postcss from 'rollup-plugin-postcss';
import autoprefixer from 'autoprefixer';
import cssnano from 'cssnano';
import serve from 'rollup-plugin-serve';
import replace from 'rollup-plugin-replace';
import serve from 'rollup-plugin-serve';
import livereload from 'rollup-plugin-livereload';
export default {
external: ['react', 'react-dom'], // 配置第三方库不打包,而是使用外部依赖
input: 'src/index.jsx',
output: {
file: 'example/dist/bundle.js',
format: 'umd',
},
plugins: [
resolve({ extensions }), // 在node_modules中找到并捆绑第三方依赖项
commonjs(), // 用来将 CommonJS 转换成 ES6 模块
babel({
exclude: 'node_modules/**',
}),
terser(), // 压缩js代码
postcss({ // css处理
plugins: [
autoprefixer(), // 加前缀
cssnano(), // 压缩css
],
extract: 'css/index.css', // 单独抽离css文件
}),
//---new----
livereload(path.join(__dirname, '../example/dist')), // 热更新 监听本地文件
serve({
// open: true, // 自动打开页面
port: 8000,
sourcemap: true,
contentBase: path.join(__dirname, '../example'),
}),
replace({ // 设置环境变量
'process.env.NODE_ENV': JSON.stringify('production')
})
]
}
配置别名
安装依赖
yarn add -D @rollup/plugin-alias
配置rollup.config.js
import path from 'path';
import babel from '@rollup/plugin-babel';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import { terser } from 'rollup-plugin-terser';
import postcss from 'rollup-plugin-postcss';
import autoprefixer from 'autoprefixer';
import cssnano from 'cssnano';
import replace from 'rollup-plugin-replace';
import serve from 'rollup-plugin-serve';
import livereload from 'rollup-plugin-livereload';
import alias from '@rollup/plugin-alias';
export default {
external: ['react', 'react-dom'], // 配置第三方库不打包,而是使用外部依赖
input: 'src/index.jsx',
output: {
file: 'example/dist/bundle.js',
format: 'umd',
},
plugins: [
resolve({ extensions }), // 在node_modules中找到并捆绑第三方依赖项
commonjs(), // 用来将 CommonJS 转换成 ES6 模块
babel({
exclude: 'node_modules/**',
}),
terser(), // 压缩js代码
postcss({ // css处理
plugins: [
autoprefixer(), // 加前缀
cssnano(), // 压缩css
],
extract: 'css/index.css', // 单独抽离css文件
}),
//---new----
alias({
entries: [
{ find: '@', replacement: path.join(__dirname, '../src') },
]
}),
livereload(path.join(__dirname, '../example/dist')), // 热更新 监听本地文件
serve({
// open: true, // 自动打开页面
port: 8000,
sourcemap: true,
contentBase: path.join(__dirname, '../example'),
}),
replace({ // 设置环境变量
'process.env.NODE_ENV': JSON.stringify('production')
})
]
}
json
安装依赖
yarn add -D @rollup/plugin-json
配置rollup.config.js
import path from 'path';
import babel from '@rollup/plugin-babel';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import { terser } from 'rollup-plugin-terser';
import postcss from 'rollup-plugin-postcss';
import autoprefixer from 'autoprefixer';
import cssnano from 'cssnano';
import replace from 'rollup-plugin-replace';
import livereload from 'rollup-plugin-livereload';
import alias from '@rollup/plugin-alias';
import json from '@rollup/plugin-json';
export default {
external: ['react', 'react-dom'], // 配置第三方库不打包,而是使用外部依赖
input: 'src/index.jsx',
output: {
file: 'example/dist/bundle.js',
format: 'umd',
},
plugins: [
resolve({ extensions }), // 在node_modules中找到并捆绑第三方依赖项
commonjs(), // 用来将 CommonJS 转换成 ES6 模块
//---new----
json(), // 支持json 文件导入
babel({
exclude: 'node_modules/**',
}),
terser(), // 压缩js代码
postcss({ // css处理
plugins: [
autoprefixer(), // 加前缀
cssnano(), // 压缩css
],
extract: 'css/index.css', // 单独抽离css文件
}),
alias({
entries: [
{ find: '@', replacement: path.join(__dirname, '../src') },
]
}),
livereload(path.join(__dirname, '../example/dist')), // 热更新 监听本地文件
serve({
// open: true, // 自动打开页面
port: 8000,
sourcemap: true,
contentBase: path.join(__dirname, '../example'),
}),
replace({ // 设置环境变量
'process.env.NODE_ENV': JSON.stringify('production')
})
]
}
支持eslint
基本依赖
这里我使用的是 aribnb 编码规范
yarn add -D eslint eslint-config-airbnb eslint-plugin-import eslint-plugin-react eslint-plugin-react-hooks and eslint-plugin-jsx-a11y
解析ts
yarn add -D @typescript-eslint/eslint-plugin @typescript-eslint/parser
模版配置
module.exports = {
parser: '@typescript-eslint/parser', // Specifies the ESLint parser
parserOptions: {
ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features
sourceType: 'module' // Allows for the use of imports
},
extends: [
'airbnb', // Uses airbnb, it including the react rule(eslint-plugin-react/eslint-plugin-jsx-a11y)
// 'plugin:@typescript-eslint/recommended', // Optional enable, will more stricter
// 'plugin:import/typescript', // Use prettier/react to pretty react syntax
// 'plugin:promise/recommended',
'plugin:react/recommended',
// 'plugin:prettier/recommended',
// 'prettier/@typescript-eslint'
],
settings: {
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx']
},
'import/resolver': {
// use <root>/path/to/folder/tsconfig.json
typescript: {
directory: './tsconfig.json'
}
}
},
env: {
browser: true // enable all browser global variables
},
plugins: ['@typescript-eslint', 'react-hooks', 'promise'],
rules: {
// Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
// e.g. '@typescript-eslint/explicit-function-return-type': 'off',
'no-useless-constructor': 0,
'react/jsx-filename-extension': [1, { 'extensions': ['.ts', '.tsx'] }],
'react/jsx-one-expression-per-line': 0,
'react-hooks/rules-of-hooks': 'error',
/**
* @description rules of @typescript-eslint
*/
'@typescript-eslint/prefer-interface': 'off', // also want to use 'type'
'@typescript-eslint/explicit-function-return-type': 'off', // annoying to force return type
'@typescript-eslint/indent': 'off' // avoid conflict with airbn
/**
* @description rules of eslint-plugin-prettier
*/
// 'prettier/prettier': [
// error, {
// singleQuote: true,
// semi: false
// }
// ]
},
};
遇到的问题
由于官方 @rollup/plugin-typescript 和 @rollup/plugin-eslint 两个插件同时使用有冲突,似乎ESLint是在编译后的代码上执行的,所以避免同时使用。在官方渠道也没找到合理的方案,以下提供三种替代方案。
方案一
将ts插件替换为 rollup-plugin-typescript2
修改 rollup.config.js 配置
import eslint from '@rollup/plugin-eslint';
// import typescript from '@rollup/plugin-typescript';
import typescript from 'rollup-plugin-typescript2';
export default {
input: 'src/index.jsx',
output: {
file: 'dist/bundle.js',
format: 'umd',
},
plugins: [
eslint(),
typescript({
tsconfig: './tsconfig.json', // 指定ts配置文件
}),
]
}
方案二
移除 @rollup/plugin-eslint,替换为以下插件
具体可参考:
- https://stackoverflow.com/questions/65181259/rollup-typescript-eslint-wrong-warning
https://github.com/TrySound/rollup-plugin-eslint/issues/47
方案三
不再使用 @rollup/plugin-typescript 或 rollup-plugin-typescript2 编译ts,而是使用babel 编译ts
参考上方 typescript 支持/方案二参考链接
- rollup plugins