Rollup用来干啥
Rollup是当前流行的库打包器,在Webpack、ESM(ES6模块)之后出现,Rollup支持开发者使用ESM模块语法开发。
文件类型
几乎只支持JS,其他类型的文件如CSS等均需要使用插件处理(后续会介绍 )
特点
Rollup将代码转码成目标js(支持umd/commonjs/es)
Rollup推崇ESM模块标准开发(借助浏览器对ESM的支持)
Rollup打包产物对比Webpack较为精简,如下
webpack:1. 有大量的诸如 __webpack_require__之类的代码,这些都是 Webpack 自身 Polyfill 的在运行时的模块加载, 就是为了让产物代码在所有浏览器都能运行(wepack出现的时候还没有ESM ,当时的模块标准还很混乱,Webpack抹平了差异。) 2. 用 IIFE 实现模块之间的隔离,并且用__webpack_require__ __webpack_exports__ 等 Polyfill 实现在浏览器环境里模拟 CJS 模块加载 3. 用 Webpack 打包后的代码实际上更像是跑在 Webpack 给我们实现的“虚拟 Runtime”上
Rollup:
Rollup 诞生在 ESM 模块标准出来之后,所以 Rollup 完全遵从 ESM 标准,不需要像 Webpack 那样做很多 Runtime Polyfill,
完全把代码交给浏览器运行。对于一些项目里依赖的老旧的 CJS 的包,也可以通过插件来对这些依赖处理。
快速0 -> 1
新建工程
创建空文件夹,如 mkdir proton-utils
安装rollup
cd proton-utils & code .
npm init -y
npm install rollup –save-dev
touch .gitignore & vim .gitignore
/node_modules
生成tsconfig.json配置文件
tsc –init
默认的配置其实已经够用,后续可以根据需要删减配置。
{
"compilerOptions": {
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
/* Modules */
"module": "commonjs", /* Specify what module code is generated. */
/* Interop Constraints */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}
创建rollup.config.js
touch rollup.config.js
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import typescript from 'rollup-plugin-typescript';
import pkg from './package.json';
export default {
input: 'src/index.ts', // 打包入口
output: { // 打包出口
file: pkg.browser, // 最终打包出来的文件路径和文件名,这里是在package.json的browser: 'dist/index.js'字段中配置的
format: 'umd', // umd是兼容amd/cjs/iife的通用打包格式,适合浏览器
name:'proton-utils' // 包的全局变量名称
},
plugins: [ // 打包插件
resolve(), // 查找和打包node_modules中的第三方模块
commonjs(), // 将 CommonJS 转换成 ES2015 模块供 Rollup 处理
typescript() // 解析TypeScript
]
};
vim package.json
"browser": "dist/index.ts",
npm i -D rollup typescript tslib rollup-plugin-node-resolve rollup-plugin-commonjs rollup-plugin-typescript
- tslib在rollup.config.js没有引入,为啥安装? -> rollup-plugin-typescript插件依赖了该库。
编写需打包库源码
0 -> 1 小demo
跑通源码编写 -> 打包构建 -> 引入使用
源码编写
mkdir src & touch src/index.ts
vim src/index.ts
console.log('baishu test rollup:')
:wq
打包构建
vim package.json
"scripts": {
"build":"rollup -c"
},
:wq
npm run build
手动发布: npm publish
引入使用
mkdir web_app & cd web_app & yarn create @umijs/umi-app
构建发布脚本
在安装使用之前,需将打包构建好的文件发布至类似npm仓库( verdaccio搭建私有npm仓库后续文章介绍),供项目安装。
由于上一章节只是一个demo,则手动publish至npm仓库(源码也被推上去了,显然不稳当,如下图)
解决问题:
1. 只发布打包后文件
2. 发布版本号自动+1
3. 脚本发布
拷贝文件
vim package.json
"scripts": {
...,
"copy": "cp package.json dist"
},
修改文件
mkdir scripts & cd scripts & touch scripts/publish.js
npm i -D shelljs commander
vim package.json
"scripts": {
...,
"copy": "cp package.json dist",
"build": "rollup -c && npm run copy"
},
vim scripts/publish.js
const path = require('path');
const shelljs = require('shelljs');
const program = require('commander');
const targetFile = path.resolve(__dirname, '../dist/package.json');
const packagejson = require(targetFile);
const currentVersion = packagejson.version;
const versionArr = currentVersion.split('.');
const [mainVersion, subVersion, phaseVersion] = versionArr;
// 默认版本号
const defaultVersion = `${mainVersion}.${subVersion}.${+phaseVersion+1}`;
let newVersion = defaultVersion;
// 从命令行参数中取版本号
program
.option('-v, --versions <type>', 'Add release version number', defaultVersion);
program.parse(process.argv);
if (program.versions) {
newVersion = program.versions;
}
function publish() {
shelljs.sed('-i', '"name": "proton-utils"', '"name": "baishu-proton-utils"', targetFile); // 修改包名
shelljs.sed('-i', `"version": "${currentVersion}"`, `"version": "${newVersion}"`, targetFile); // 修改版本号
shelljs.cd('dist');
shelljs.exec('npm publish'); // 发布
}
publish();
发布
vim package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"copy": "cp package.json dist",
"build": "rollup -c && npm run copy",
"publish": "node scripts/publish.js"
},
npm run publish
总结
至此,我们已经完成从0 -> 1的过程
学会了:
1.如何配置Rollup和TypeScript
2.脚本自动化发布工具库到npm仓库
引入包内容(仅源码 归功于publish.js)
接下来章节中,将会仔细介绍Rollup相关内容😄
常用配置
input
入口文件地址
output
output:{
file:'bundle.js', // 输出文件
format: 'cjs, // 五种输出格式:amd / es6 / iife / umd / cjs
name:'A', //当format为iife和umd时必须提供,将作为全局变量挂在window(浏览器环境)下:window.A=...
sourcemap:true //生成bundle.map.js文件,方便调试
}
plugins
各种插件使用的配置
external
external:[‘lodash’] //告诉rollup不要将此lodash打包,而作为外部依赖
global
global:{
'jquery':'$' //告诉rollup 全局变量$即是jquery
}
import nodeResolve from 'rollup-plugin-node-resolve' // 帮助寻找node_modules里的包
import babel from 'rollup-plugin-babel' // rollup 的 babel 插件,ES6转ES5
import replace from 'rollup-plugin-replace' // 替换待打包文件里的一些变量,如process在浏览器端是不存在的,需要被替换
import commonjs from 'rollup-plugin-commonjs' // 将非ES6语法的包转为ES6可用
import uglify from 'rollup-plugin-uglify' // 压缩包
const env = process.env.NODE_ENV
const config = {
input: 'src/index.js',
external: ['react', 'redux'], // 告诉rollup,不打包react,redux;将其视为外部依赖
output: {
format: 'umd', // 输出 UMD格式,各种模块规范通用
name: 'ReactRedux', // 打包后的全局变量,如浏览器端 window.ReactRedux
globals: {
react: 'React', // 这跟external 是配套使用的,指明global.React即是外部依赖react
redux: 'Redux'
}
},
plugins: [
nodeResolve(),
babel({
exclude: '**/node_modules/**'
}),
replace({
'process.env.NODE_ENV': JSON.stringify(env)
}),
commonjs()
]
}
if (env === 'production') {
config.plugins.push(
uglify({
compress: {
pure_getters: true,
unsafe: true,
unsafe_comps: true,
warnings: false
}
})
)
}
export default config
深入使用
使用Babel
为了正确解析我们的模块并使其与旧版浏览器兼容,使用babel编译输出。以便可以使用未被浏览器和 Node.js 支持的将来版本的 JavaScript特性。
npm install rollup-plugin-babel –save-dev
vim rollup.config.js
import babel from 'rollup-plugin-babel' import resolve from 'rollup-plugin-node-resolve'; import commonjs from 'rollup-plugin-commonjs'; import typescript from 'rollup-plugin-typescript'; import pkg from './package.json'; const extensions = ['.js', '.ts', '.tsx', '.json'] export default { input: 'src/index.ts', // 打包入口 output: { // 打包出口 file: pkg.browser, // 最终打包出来的文件路径和文件名,这里是在package.json的browser: 'dist/index.js'字段中配置的 format: 'umd', // umd是兼容amd/cjs/iife的通用打包格式,适合浏览器 name:'proton-utils' // 包的全局变量名称 }, plugins: [ // 打包插件 resolve(), // 查找和打包node_modules中的第三方模块 commonjs(), // 将 CommonJS 转换成 ES2015 模块供 Rollup 处理 typescript(), // 解析TypeScript babel({ include: 'src/**/*', exclude: '**/node_modules/**', extensions, }), ] };
cd src & touch .babelrc & vim .babelrc
{ "presets": [ [ "@babel/env", { "modules":false } ] ] } 1.首先,设置 "modules": false ,否则 Babel 会在 Rollup 有机会做处理之前,将我们的模块转成 CommonJS ,导致 Rollup 的一些处理失败。 2.将 .babelrc 文件放在 src 中,而不是根目录下。 这允许我们对于不同的任务有不同的 .babelrc 配置,比如像测试,如果我们以后需要的话 - 通常为单独的任务单独配置会更好。
npm install @babel/core @babel/preset-env –save-dev
npm run build
打包后出来的文件内容经过babel转换后有es6语法变成了es5语法:
node模块的引用
使用rollup打包的项目有时会引用三方包(node_modules文件夹中的软件包)
rollup.js编译源码中的模块引用默认只支持 ES6+的模块方式import/export。然而大量的npm模块是基于CommonJS模块方式,这就导致了大量 npm 模块不能直接编译使用。所以辅助rollup.js编译支持 npm模块和CommonJS模块方式的插件就应运而生。
rollup-plugin-node-resolve :插件允许加载第三方模块
rollup-plugin-commonjs :将 CommonJS 转换成 ES2015 模块供 Rollup 处理
npm i -D rollup typescript tslib rollup-plugin-node-resolve rollup-plugin-commonjs
使用三方库lodash
npm install lodash –save-dev
vim src/index.ts
import _ from 'lodash' const flatArray = (arrs:any) => { return _.flattenDeep(arrs); } const isBaishu = (name:string) => { return name === 'baishu' } export { isBaishu, flatArray }
npm run build
打包后的文件多了很多内容,即ladash的代码,被打包整合进来了。
vim rollup.config.js
export default {
input:'',
output:{},
plugins:[],
external:['lodash']
};
使用typescript
npm i rollup-plugin-typescript –save-dev
配置rollup.config.js
plugins: [ // 打包插件 ... typescript(), // 解析TypeScript ... ],
配置tsconfig.json
压缩代码
npm i rollup-plugin-terser –save-dev
配置rollup.config.js
import babel from 'rollup-plugin-babel' import resolve from 'rollup-plugin-node-resolve'; import commonjs from 'rollup-plugin-commonjs'; import typescript from 'rollup-plugin-typescript'; import pkg from './package.json'; import { terser } from "rollup-plugin-terser"; const extensions = ['.js', '.ts', '.tsx', '.json'] export default { input: 'src/index.ts', // 打包入口 output: { // 打包出口 file: pkg.browser, // 最终打包出来的文件路径和文件名,这里是在package.json的browser: 'dist/index.js'字段中配置的 format: 'umd', // umd是兼容amd/cjs/iife的通用打包格式,适合浏览器 name:'proton-utils' // 包的全局变量名称 }, plugins: [ // 打包插件 resolve(), // 查找和打包node_modules中的第三方模块 commonjs(), // 将 CommonJS 转换成 ES2015 模块供 Rollup 处理 typescript(), // 解析TypeScript babel({ include: 'src/**/*', exclude: '**/node_modules/**', extensions, }), terser() ], external:['lodash'] };
npm run build
编译css
一般是具体业务项目需css编译,使用webpack
js类库若必须使用css的话,rollup也是有插件编译css的
npm install rollup-plugin-postcss –save-dev
配置rollup.config.js
import babel from 'rollup-plugin-babel' import resolve from 'rollup-plugin-node-resolve'; import commonjs from 'rollup-plugin-commonjs'; import typescript from 'rollup-plugin-typescript'; import pkg from './package.json'; import { terser } from "rollup-plugin-terser"; import postcss from "rollup-plugin-postcss"; const extensions = ['.js', '.ts', '.tsx', '.json'] export default { input: 'src/index.ts', // 打包入口 output: { // 打包出口 file: pkg.browser, // 最终打包出来的文件路径和文件名,这里是在package.json的browser: 'dist/index.js'字段中配置的 format: 'umd', // umd是兼容amd/cjs/iife的通用打包格式,适合浏览器 name:'proton-utils' // 包的全局变量名称 }, plugins: [ // 打包插件 resolve(), // 查找和打包node_modules中的第三方模块 commonjs(), // 将 CommonJS 转换成 ES2015 模块供 Rollup 处理 typescript(), // 解析TypeScript babel({ include: 'src/**/*', exclude: '**/node_modules/**', extensions, }), terser(), postcss() ], external:['lodash'] };
区分开发环境和生产环境
在开发环境我们需要sourcemap开启,配置热更新和本地服务,在生产环境我们需要sourcemap关闭,不需要热更新和本地服务,需要代码压缩等,所以需要区分。
rollup.config.js拆分成两个rollup.config.dev.js和rollup.config.build.js
修改 package.json 中的打包命名
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "rm -rf ./dist && rollup --config rollup.config.build.js && npm run copy", "dev": "rm -rf ./dist && rollup --config rollup.config.dev.js -w", "copy": "cp package.json README.md dist", "publish": "node scripts/publish.js", "webpack": "webpack" },
开启本地服务
npm install rollup-plugin0dev –save-dev
配置rollup.config.dev.js
import babel from 'rollup-plugin-babel' import resolve from 'rollup-plugin-node-resolve'; import commonjs from 'rollup-plugin-commonjs'; import typescript from 'rollup-plugin-typescript'; import pkg from './package.json'; // import { terser } from "rollup-plugin-terser"; // import postcss from "rollup-plugin-postcss"; import dev from 'rollup-plugin-dev'; const extensions = ['.js', '.ts', '.tsx', '.json'] export default { input: 'src/index.ts', // 打包入口 output: { // 打包出口 file: './dist/index.js', // 最终打包出来的文件路径和文件名,这里是在package.json的browser: 'dist/index.js'字段中配置的 format: 'umd', // umd是兼容amd/cjs/iife的通用打包格式,适合浏览器 name:'proton-utils' // 包的全局变量名称 }, plugins: [ // 打包插件 resolve(), // 查找和打包node_modules中的第三方模块 commonjs(), // 将 CommonJS 转换成 ES2015 模块供 Rollup 处理 typescript(), // 解析TypeScript babel({ include: 'src/**/*', exclude: '**/node_modules/**', extensions, }), // terser(), // postcss(), dev({ port:8010, dirs:["dist",'./'], }) ], external:['lodash'], };
根目录创建页面
touch index.html & vim index.htmlRollup Example baishu testnpm run dev 生成 dist/index.js,同时可访问 8010 端口:
开启热更新
npm install rollup-plugin-livereload –save-dev
配置rollup.config.dev.js
import babel from 'rollup-plugin-babel' import resolve from 'rollup-plugin-node-resolve'; import commonjs from 'rollup-plugin-commonjs'; import typescript from 'rollup-plugin-typescript'; import pkg from './package.json'; // import { terser } from "rollup-plugin-terser"; // import postcss from "rollup-plugin-postcss"; import dev from 'rollup-plugin-dev'; import livereload from "rollup-plugin-livereload"; const extensions = ['.js', '.ts', '.tsx', '.json'] export default { input: 'src/index.ts', // 打包入口 output: { // 打包出口 file: './dist/index.js', // 最终打包出来的文件路径和文件名,这里是在package.json的browser: 'dist/index.js'字段中配置的 format: 'umd', // umd是兼容amd/cjs/iife的通用打包格式,适合浏览器 name:'proton-utils' // 包的全局变量名称 }, plugins: [ // 打包插件 resolve(), // 查找和打包node_modules中的第三方模块 commonjs(), // 将 CommonJS 转换成 ES2015 模块供 Rollup 处理 typescript(), // 解析TypeScript babel({ include: 'src/**/*', exclude: '**/node_modules/**', extensions, }), // terser(), // postcss(), dev({ port:8010, dirs:["dist",'./'], }), livereload(), ], external:['lodash'], };
多个输入输出文件
- 配置rollup.config.dev.js
import babel from ‘rollup-plugin-babel’
import resolve from ‘rollup-plugin-node-resolve’;
import commonjs from ‘rollup-plugin-commonjs’;
import typescript from ‘rollup-plugin-typescript’;
import pkg from ‘./package.json’;
import { terser } from “rollup-plugin-terser”;
// import postcss from “rollup-plugin-postcss”;
const extensions = [‘.js’, ‘.ts’, ‘.tsx’, ‘.json’]
export default [
// UMD for browser-friendly build
{
input: ‘src/index.ts’, // 打包入口
output: { // 打包出口
file: pkg.browser, // 最终打包出来的文件路径和文件名,这里是在package.json的browser: ‘dist/index.js’字段中配置的
format: ‘umd’, // umd是兼容amd/cjs/iife的通用打包格式,适合浏览器
name:’proton-utils’ // 包的全局变量名称
},
plugins: [ // 打包插件
resolve(), // 查找和打包node_modules中的第三方模块
commonjs(), // 将 CommonJS 转换成 ES2015 模块供 Rollup 处理
typescript(), // 解析TypeScript
babel({
include: ‘src//*’,
exclude: ‘/node_modules/**’,
extensions,
}),
terser()
// postcss()
],
external:[‘lodash’]
},
// CommonJS for Node and ES module for bundlers build
{
input: ‘src/index.ts’,
plugins: [
typescript()
],
output: [
{ file: pkg.main, format: ‘cjs’, exports: ‘auto’ },
{ file: pkg.module, format: ‘es’, exports: ‘auto’ }
],
external:[‘lodash’]
}
];
2. npm run build
打包后dist目录:
这种模式并不常见,更常见的模式是前面的一个入口文件,多个输出文件,其中输出文件的不同在于使用了不同的模块定义,比如同时输出 ES6 模块和 CommonJS 模块。
更多插件
- 本文链接:http://example.com/2020/06/02/webpack/rollup/
- 版权声明:本博客所有文章除特别声明外,均默认采用 许可协议。