Rollup
Rollup 是一个用于 JavaScript 的模块打包工具,它将小的代码片段编译成更大、更复杂的代码,例如库或应用程序。它使用 JavaScript 的 ES6 版本中包含的新标准化代码模块格式,而不是以前的 CommonJS 和 AMD 等特殊解决方案。ES 模块允许你自由无缝地组合你最喜欢的库中最有用的个别函数。 这在未来将在所有场景原生支持,但 Rollup 让你今天就可以开始这样做。 https://cn.rollupjs.org/introduction/
一个真实的组件库的 rollup 打包配置
该项目支持:
打包输出文件保留原始模块结构 自动将 dependencies 依赖声明为 externals 支持处理外部 npm 依赖 支持基于 CommonJS 模块引入 支持 typescript,并导出声明文件 支持 scss,并添加前缀 支持自动清除调试代码 支持按需加载
- 安装
npm i rollup -g
yarn add typescript postcss @rollup/plugin-commonjs @rollup/plugin-node-resolve @rollup/plugin-strip @rollup/plugin-typescript rollup-plugin-postcss rollup-plugin-node-externals autoprefixer -D
- 配置 项目根目录下新增 rollup.config.js:
import path from 'node:path';
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import strip from '@rollup/plugin-strip';
import typescript from '@rollup/plugin-typescript';
import autoprefixer from 'autoprefixer';
import externals from 'rollup-plugin-node-externals';
import postcss from 'rollup-plugin-postcss';
import pkg from './package.json';
export default [
{
input: './src/index.ts', // 入口文件
output: [
{
// 出口文件
dir: path.dirname(pkg.module),
format: 'es', // es模块导出,支持按需加载
name: pkg.name,
exports: 'named', // 指定导出模式(自动、默认、命名、无)
preserveModules: true, // 保留模块结构
preserveModulesRoot: 'src', // 将保留的模块放在根级别的此路径下
},
],
plugins: [
// 自动将dependencies依赖声明为 externals
externals({
devDeps: false,
}),
// 处理外部依赖
resolve(),
// 支持基于 CommonJS 模块引入
commonjs(),
// 支持 typescript,并导出声明文件
typescript({
outDir: 'es',
declaration: true,
declarationDir: 'es',
}),
// 支持 scss,并添加前缀
postcss({
plugins: [autoprefixer()],
}),
// 清除调试代码
strip(),
],
},
];
更新 packages.json:
{
"module": "es/index.js",
"types": "es/index.d.ts",
"files": [
"es"
],
"scripts": {
"build": "rimraf es && rollup -c"
}
}
新增 tsconfig.json:
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": [
"src"
],
"exclude": [
"src/**/stories.*",
"src/**/.spec.*",
"src/**/.mdx"
]
}
rollup.config.js
packages/perfect-design/rollup.config.js
import ts from 'rollup-plugin-typescript2';
import less from 'rollup-plugin-less';
import clear from 'rollup-plugin-clear';
import cjs from '@rollup/plugin-commonjs';
import babel from 'rollup-plugin-babel';
import resolve from '@rollup/plugin-node-resolve';
import { terser } from 'rollup-plugin-terser';
import { uglify } from 'rollup-plugin-uglify';
import copy from 'rollup-plugin-copy';
function getBaseRollupPlugins({ typescript = {} } = {}) {
return [
resolve(),
cjs({
ignoreGlobal: true,
include: /\/node_modules\//,
external: ['react', 'react-dom', 'styled-components']
}),
ts(typescript),
terser(),
uglify()
];
}
export default {
input: ['./src/index.ts'],
output: [
{
file: 'dist/cjs/index.js',
format: 'cjs',
name: 'cjs.js'
},
{
file: 'dist/umd/index.js',
format: 'umd',
name: 'umd.js'
},
{
file: 'dist/es/index.js',
format: 'es',
name: 'index.js'
}
],
plugins: [
less({ output: './dist/style/index.css' }),
clear({
targets: ['dist']
}),
...getBaseRollupPlugins(),
babel({
exclude: 'node_modules/**',
runtimeHelpers: true
}),
copy({
targets: [
{
src: '../../scripts/globalStyle/compiled-colors.less',
dest: 'dist/style'
}
]
})
],
external: ['react', 'react-dom']
};
"rollup": "^3.17.2",
"rollup-plugin-babel": "^4.4.0",
"rollup-plugin-clear": "^2.0.7",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-copy": "^3.4.0",
"rollup-plugin-delete": "^2.0.0",
"rollup-plugin-eslint": "^7.0.0",
"rollup-plugin-less": "^1.1.3",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.34.1",
"rollup-plugin-uglify": "^6.0.4",
rollup配置及使用
https://segmentfault.com/a/1190000041180789#item-3-6
项目配置
babel
引入依赖 首先运行以下命令安装babel相关:
yarn add @babel/core @babel/cli @babel/preset-env -D
配置babel.config.js
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: '> 0.25%, not dead'
}
]
]
};
搭配@babel/plugin-transform-runtime和core-js:
yarn add core-js @babel/runtime
yarn add @babel/plugin-transform-runtime -D
修改babel.config.js如下:
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: '> 0.25%, not dead',
useBuiltIns: 'usage',
corejs: '3.6.5'
}
]
],
plugins: ['@babel/plugin-transform-runtime']
};
增加 npm scripts
"scripts:" {
"babel": "babel ./src/index.js -o ./dist/index.js"
}
Typescript
面向未来,所以这里引入typescript,统一用 ts 进行开发
yarn add typescript@4.3.5 -D
yarn add @babel/preset-typescript -D
修改babel配置如下:
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: '> 0.25%, not dead',
useBuiltIns: 'usage',
corejs: '3.6.5'
},
'@babel/preset-typescript'
]
],
plugins: ['@babel/plugin-transform-runtime']
};
rollup
项目是纯粹的Javascript项目,没有vue、react相关的业务性代码,所以使用 rollup 进行打包。
引入依赖
yarn add rollup @rollup/plugin-babel @rollup/plugin-commonjs @rollup/plugin-node-resolve rollup-plugin-terser rollup-plugin-typescript2 tslib -D
配置rollup.config.js
import babel from '@rollup/plugin-babel'; // 引入babel
import commonjs from '@rollup/plugin-commonjs'; // 引入cjs插件,支持 commonjs 语法
import { nodeResolve } from '@rollup/plugin-node-resolve'; // 引入resolve,告诉 Rollup 如何查找外部模块
import typescript from 'rollup-plugin-typescript2'; // ts
import { terser } from 'rollup-plugin-terser'; // 压缩打包文件
const extensions = ['.js', '.ts'];
const pkg = require('./package.json'); // 从package.json引入
const version = pkg.version; // 项目版本
const license = pkg.license; // 协议
const author = pkg.author; // 作者
// 打包文件的头部声明
const banner
= '/*!\n'
+ ` * ${pkg.name} v${version}\n`
+ ` * (c) 2020-${new Date().getFullYear()} ${author}\n`
+ ` * Released under the ${license} License.\n`
+ ' */';
module.exports = {
input: 'src/index.ts',
output: [
// 文件输出配置
{
file: 'dist/index.umd.js', // 打包后生产的文件位置,及文件名
format: 'umd',
name: 'utools', // 包的全局变量名称
banner
},
{
file: 'dist/index.esm.js', // 打包后生产的文件位置,及文件名
format: 'esm',
name: 'utools', // 包的全局变量名称
banner
}
],
plugins: [
nodeResolve({
extensions,
modulesOnly: true
}),
commonjs(),
typescript(),
babel({
babelHelpers: 'runtime',
include: 'src/**',
exclude: 'node_modules/**',
extensions
}),
terser()
]
};
增加 npm scripts
"scripts": {
"build": "rollup -c", // -c参数即 config 使用配置文件
"dev": "rollup -c -w" // -w参数监听源文件是否有改动,如果有改动,重新打包
},
package.json 如下
{
"name": "module name",
"version": "module version",
"description": "module desc",
"main": "dist/index.umd.js",
"types": "types/index.d.ts",
"module": "dist/index.esm.js",
"scripts": {
"test": "jest",
"babel": "babel ./src/index.js -o ./dist/index.js",
"build": "rimraf dist/* && rollup -c",
"release": "release-it",
"release:beta": "release-it major --preRelease=beta",
"fix:src": "npx eslint src --fix --ext .ts",
"fix:test": "npx eslint test --fix --ext .js",
"lint": "npm run fix:src && npm run fix:test"
},
"repository": {
"type": "git"
},
"author": "module author",
"license": "MIT",
"devDependencies": {},
"dependencies": {}
}
(!) Module level directives cause errors when bundled, 'use client' was ignored.
https://github.com/rollup/rollup/issues/4699
https://github.com/Ephem/rollup-plugin-preserve-directives/tree/main
热更新插件
划重点!!!在开发中怎么能少了热更新呢,众所周知,rollup 是不支持热更新的,但这么好用的功能,与时俱进的 rollup 怎么能没有呢?插件 rollup-plugin-hotreload 实现了这个功能!
npm install --save-dev rollup-plugin-hotreload
在 rollup.config.js 中配置如下:
import { rph, rphMultibundles } from "rollup-plugin-hotreload";
import path from "path";
import "your_server_path"; // import your server path;
export default rphMultibundles({
//...
plugins: [
rph({
templateHtmlPath: "src/index.html", // template html path relative to rootDir
isStopRPH: false, // stop hotreload or not
rootDir: __dirname, // rootDir
rootBuildDir: "build", // build root path relative to rootDir
buildPaths: [
// first one is relative path to rootDir...
["js/index.min.js", "src/index.js"]
// as many as you want ...
]
})
]
});
区分开发环境和生产环境
在开发环境我们需要sourcemap开启,配置热更新和本地服务,在生产环境我们需要sourcemap关闭,不需要热更新和本地服务,需要代码压缩等,所以需要区分。
拆分 roullup.config.dev.js和rollup.config.prod.js 将rollup.config.js拆分成两个rollup.config.dev.js和rollup.config.build.js
修改 package.json 中的打包命名即可。 配置 package.json 最后在package.json中配置命名(这里做了修改,区分了开发环境和生产环境)
配置rollup.config.dev.js
本地服务器 + source-map + 热更新
两个本地服务器插件 rollup-plugin-serve or rollup-plugin-dev
这两个插件相比:
rollup-plugin-dev 使用koa服务器实现,存在依赖 具有可能有用的附加功能
安装插件
先来安装一个插件开启本地服务器
安装rollup-plugin-serve or rollup-plugin-dev
npm install rollup-plugin-serve --save-dev
or
npm install rollup-plugin0dev --save-dev
开启热更新
安装rollup-plugin-livereload插件
npm install rollup-plugin-livereload --save-dev
在开发环境的rollup.config.dev.js中配置插件中配置rollup-plugin-livereload() 即可。
配置rollup.config.dev.js 在rollup.config.dev.js的打包输出 output 中配置 sourcemap: true,这样调试代码会更方便。
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import json from 'rollup-plugin-json';
import livereload from 'rollup-plugin-livereload';
import serve from 'rollup-plugin-serve';
import pkg from '../package.json'; // 引用文件路径要记得修改!!
export default [
{
input: pkg.main,
output: [
{
name: 'W',
file: pkg.browser,
format: 'umd',
sourcemap: true,
}
],
plugins: [
resolve(), // so Rollup can find `ms`
commonjs(), // so Rollup can convert `ms` to an ES module
json(), // so Rollup can read file `.json`
livereload(),
serve({
open: true,
port: 8082,
contentBase: ''
})
]
}
];
引用库
在根目录下建一个index.html文件来引用js类库,npm run dev即可自动打开浏览器页面。
配置rollup.config.prod.js
生产环境下不需要以上配置,主要需要代码压缩,安装插件:
import { terser } from "rollup-plugin-terser";
export default [
{
input: pkg.main,
output: [
{
name: 'W',
file: pkg.browser,
format: 'umd'
}
],
plugins: [
resolve(), // so Rollup can find `ms`
commonjs(), // so Rollup can convert `ms` to an ES module
json(), // so Rollup can read file `.json`
terser() // so minify
]
}
];
rollup-plugin-visualizer
rollup一般用于npm包的构建,相比webpack更轻量。rollup-plugin-visualizer功能和webpack-bundle-analyzer类似,但ui设计上提供了更多的template可以调用。
# NPM
npm install --save-dev rollup-plugin-visualizer
# Yarn
yarn add -D rollup-plugin-visualizer
const { visualizer } = require('rollup-plugin-visualizer');
export default {
input: './src/main.js',
output: {
file: 'bundle.js',
format: 'es'
},
plugins: [
// 放在所有插件最后
visualizer({
filename: 'report.html',
open: true,
gzipSize: true,
brotliSize: true,
})
]
};