前言
1.在云在前端公众号中发送rollup领取源码地址
2.本章内容涉及到package.json,如果对其相关字段不熟悉,可以先参考我的上一篇文章# 中高级前端必须掌握的package.json最新最全指南
起步
1. rollup与webpack区别
Rollup 更适合构建库和组件,追求更高的代码优化和性能;
Webpack 更适合构建复杂的应用,提供了更多的功能和灵活性。
2. 安装 Rollup
- 初始化 package.json
yarn init -y
- 安装 rollup
yarn add rollup -D
- 新建src/foo.js
export default 'hello world!';
- 新建src/index.js
import foo from './foo.js';
export default function () {
console.log(foo);
}
- 修改 package.json
"main": "dist/index.cjs.js",
"module": "dist/index.es.js",
"browser": "dist/index.umd.js",
"scripts": {
"build": "rollup -c",
"serve": "rollup -c -w"
},
-c:代表读取配置去打包,默认读取根目录下的rollup.config.mjs
-w:代表了watch监听,调试的时候可以用
- 新建 rollup.config.mjs
import { defineConfig } from "rollup";
import pkg from "./package.json" assert { type: "json" }; //断言导出json模块
export default defineConfig([
{
input: "src/index.js", //入口文件
output: [
{
file: pkg.main, //出口文件
format: "cjs", //打包成CommonJS模块
},
{
file: pkg.module, //出口文件
format: "es", //打包成es module模块
},
{
name: "myUtils", //打包成UMD模式,需提供name
file: pkg.browser, //出口文件
format: "umd", //打包成UMD模块
},
],
},
]);
-
运行 yarn run build 进行打包,项目根目录会生成一个dist文件夹。
-
根目录新建index.html引入打包后的文件,进行测试
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<!-- 测试es module模块 -->
<script type="module">
import main from "./dist/index.es.js";
main();
</script>
<!-- 测试umd模块 -->
<script src="./dist/index.umd.js"></script>
<script>
window.myUtils();
</script>
</body>
</html>
提示:使用esm模块时,直接访问,浏览器会报跨域错误。
解决方式:安装vscode扩展 Live Server,然后使用open with Live Server打开。可以看到浏览器打印出两个hello world!,至此使用rollup已打包成功。
插件
rollup有丰富的插件,可以让我们做更多的处理,这里列举常用的插件使用方式。
1. @rollup/plugin-json(处理JSON文件)
- 安装
yarn add @rollup/plugin-json -D
- 修改src/index.js 文件
import { version } from "../package.json";
export default function () {
console.log("version " + version);
}
- 在 rollup.config.mjs 文件中加入 JSON plugin
import json from '@rollup/plugin-json';
export default defineConfig([
{
//...
plugins: [
json(),
],
},
]);
- 使用 yarn run build 运行 Rollup。dist/index.cjs.js文件内容应该如下所示
'use strict';
var version = "1.0.0";
function index () {
console.log('version ' + version);
}
module.exports = index;
结果中JSON文件已成功被处理,并且只导入了我们实际需要的数据version,其他内容例如name、devDependencies都被忽略了。这就是 tree shaking 的作用
2. @rollup/plugin-terser(压缩文件)
- 安装
yarn add @rollup/plugin-terser -D
- 修改rollup.config.mjs
import terser from "@rollup/plugin-terser";
export default defineConfig([
{
//...
plugins: [terser()],
},
]);
- 使用 yarn run build 进行打包,生成的打包文件将全部被压缩
3. @rollup/plugin-node-resolve(处理外部依赖)
- 安装
yarn add @rollup/plugin-node-resolve -D
- 修改rollup.config.mjs
import resolve from "@rollup/plugin-node-resolve";
export default defineConfig([
{
//...
plugins: [resolve()],
},
]);
-
运行 yarn add lodash-es,安装lodash-es包进行测试
-
修改src/index.js
import { add } from "lodash-es"; //引入第三方包
export default function () {
console.log("sum " + add(2 + 4));
}
- 使用 yarn run build 打包。这里如果不使用@rollup/plugin-node-resolve,会报错:Uncaught TypeError: Cannot read properties of undefined (reading 'add')
4. @rollup/plugin-commonjs(将第三方包CommonJS转ES)
- 安装
yarn add @rollup/plugin-commonjs -D
- 修改rollup.config.mjs
import commonjs from "@rollup/plugin-commonjs";
export default defineConfig([
{
//...
plugins: [commonjs()],
},
]);
-
运行 yarn add ms,安装ms包进行测试
-
修改src/index.js
import ms from "ms"; //引入CommonJS类型包
export default function () {
console.log(ms("2 days"));
}
- 使用 yarn run build 进行打包
5. @rollup/plugin-alias(路径别名)
- 安装
yarn add @rollup/plugin-alias -D
- 修改rollup.config.mjs
import alias from "@rollup/plugin-alias";
import path from "path";
import { fileURLToPath } from "url";
import { dirname } from "path";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
export default defineConfig([
{
//...
plugins: [
//之前的插件保留,在plugins数组末尾添加alias
alias({
entries: [{ find: "@", replacement: path.resolve(__dirname, "src") }],
}),
],
},
]);
- 新建src/utils/index.js
export function Add(a, b) {
return a + b;
}
- 修改src/index.js
import { Add } from "@/utils";
export default () => {
console.log(Add(1, 2));
};
- 使用 yarn run build 进行打包
代码分割
1. 自动拆分代码块
rollup对于使用import()方式引入的文件,会自动将代码拆分成块,并以chunk-[hash].js的格式命名文件,其中[hash]是基于内容的哈希字符串。
由于umd模块不支持代码分割,并且打包后从一个文件变为一个文件夹,因此需要做一些调整
- 修改package.json
"main": "dist/cjs",
"module": "dist/es",
- 修改rollup.config.mjs
export default defineConfig([
{
//...
output: [
{
dir: pkg.main, //出口文件夹
format: "cjs", //打包成CommonJS模块
},
{
dir: pkg.module, //出口文件夹
format: "es", //打包成es module模块
},
],
},
]);
- 修改src/index.js
export default function () {
import("./foo.js").then(({ default: foo }) => console.log(foo));
}
-
运行yarn run build
-
修改index.html,进行测试
<!-- 测试es module模块 -->
<script type="module">
import main from "./dist/es/index.js";
main();
</script>
2. 显式拆分代码块
通过配置 output.manualChunks 显式地将模块拆分成单独的块。常用于拆分第三方包。
- 修改rollup.config.mjs
export default defineConfig([
{
//...
output: [
{
dir: pkg.main, //出口文件
format: "cjs", //打包成CommonJS模块
manualChunks: {
lodash: ["lodash-es"],
},
},
{
dir: pkg.module, //出口文件
format: "es", //打包成es module模块
manualChunks: {
lodash: ["lodash-es"],
},
},
],
},
]);
- 修改src/index.js
import { add } from "lodash-es";
export default function () {
console.log("sum " + add(2 + 4));
}
- 使用 yarn run build 运行 Rollup,可以看到lodash已被拆分出来,形成一个单独的文件
使用 babel
将es6代码转es5,以兼容旧版浏览器、特定的移动设备等
- 安装
@rollup/plugin-babel:在 Rollup 打包过程中使用 Babel 进行代码转换
@babel/core:babel核心库
@babel/preset-env:将ES6转换为向后兼容的JavaScript
@babel/plugin-transform-runtime:处理async,await、import()等语法关键字的帮助函数
yarn add @rollup/plugin-babel -D
yarn add @babel/core -D
yarn add @babel/preset-env -D
yarn add @babel/plugin-transform-runtime -D
- 修改rollup.config.mjs
import { babel } from "@rollup/plugin-babel";
export default defineConfig([
{
//...
plugins: [
babel({
babelHelpers: "runtime",
presets: ["@babel/preset-env"],
plugins: [["@babel/plugin-transform-runtime", { useESModules: true }]],
}),
],
},
]);
注意:如果使用了@rollup/plugin-commonjs,@rollup/plugin-commonjs一定要在@rollup/plugin-babel之前调用
import { babel } from '@rollup/plugin-babel';
import commonjs from '@rollup/plugin-commonjs';
const config = {
...
plugins: [
commonjs(),
babel(...)
],
};
- 修改src/index.js
import foo from "./foo";
export default () => {
console.log(foo);
};
- 使用 yarn run build 运行 Rollup,在dist/es/index.js文件已将es6转es5
处理 Sass
1. 打包支持sass文件
rollup-plugin-postcss 默认集成了对 scss、less、stylus 的支持
- 安装
yarn add sass -D
yarn add postcss rollup-plugin-postcss -D
- 修改rollup.config.mjs
import postcss from "rollup-plugin-postcss";
export default defineConfig([
{
//...
plugins: [
postcss(),
],
},
]);
- 新建src/foo.scss
$color: red;
body {
background-color: $color;
display: flex;
}
- 修改src/index.js
import foo from "./foo.js";
import "./foo.scss";
export default function () {
console.log(foo);
}
- 使用 yarn run build 运行 Rollup,在dist/es/index.js中可以看到样式被打包了进去
2. css加前缀
- 安装
yarn add autoprefixer -D
- 更新 packages.json
"browserslist": [
"defaults",
"not ie < 8",
"last 2 versions",
"> 1%",
"iOS 7",
"last 3 iOS versions"
]
- 修改rollup.config.mjs
import postcss from "rollup-plugin-postcss";
import autoprefixer from "autoprefixer";
export default defineConfig([
{
//...
plugins: [
postcss({
plugins: [autoprefixer()],
}),
],
},
]);
3. css压缩
- 安装
yarn add cssnano -D
- 修改rollup.config.mjs
import postcss from "rollup-plugin-postcss";
import autoprefixer from "autoprefixer";
import cssnano from "cssnano";
export default defineConfig([
{
//...
plugins: [
postcss({
plugins: [autoprefixer(), cssnano()],
}),
],
},
]);
4. 抽离单独的css文件
修改rollup.config.mjs
export default defineConfig([
{
//...
plugins: [
postcss({
plugins: [autoprefixer(), cssnano()],
extract: "css/index.css",
}),
],
},
]);
使用 yarn run build 运行 Rollup,在dist/es中可以看到样式已被拆分出去,单独在css文件夹下
处理 Typescript
1. typescript 插件
- 安装
yarn add rollup-plugin-typescript2 typescript tslib -D
- 新建src/bar.ts
const str = "hello ts!";
export default str;
- 修改src/index.js
import bar from "./bar.ts";
import "./foo.scss";
export default function () {
console.log(bar);
}
- 修改rollup.config.mjs
import typescript from "rollup-plugin-typescript2";
export default defineConfig([
{
//...
plugins: [typescript()]
},
]);
注意点:
- 如果使用了@rollup/plugin-node-resolve,@rollup/plugin-node-resolve要在rollup-plugin-typescript2之前调用
const config = {
...
plugins: [
resolve(),
typescript(...)
],
};
- 如果使用了@rollup/plugin-babel,需配置babel扩展
import { DEFAULT_EXTENSIONS } from '@babel/core';
export default defineConfig([
{
//...
plugins: [
babel({
babelHelpers: 'runtime',
presets: ["@babel/preset-env"],
plugins: [["@babel/plugin-transform-runtime", { useESModules: true }]],
extensions: [...DEFAULT_EXTENSIONS, ".ts", ".tsx"], //增加配置
}),
],
},
]);
2. 导出类型声明文件
新建tsconfig.json
{
"compilerOptions": {
"module": "ESNext",
"target": "ESNext",
"declaration": true, //生成声明文件
"outDir": "dist",
"rootDir": "src"
},
"exclude": ["node_modules", "dist"]
}
tsconfig.json配置会与typescript()配置合并,并覆盖其默认配置
使用 yarn run build 运行 Rollup,在dist/cjs与dist/es下会分别生成一份ts声明文件
优化
1. 打包前清空原打包目录
- 安装 rimraf 和 rollup-plugin-delete
yarn add rimraf -D //删除打包目录
yarn add rollup-plugin-delete -D //设置要删除的文件或目录
- 修改rollup.config.mjs
import { rimrafSync } from "rimraf";
import del from 'rollup-plugin-delete';
rimrafSync("dist");// 删除打包目录
export default defineConfig([
{
//...
plugins: [
del({ targets: "dist/*" }),
],
},
]);
2. 打包产物清除调试代码
- 安装
yarn add @rollup/plugin-strip -D
- 修改rollup.config.mjs
import strip from "@rollup/plugin-strip";
export default defineConfig([
{
//...
plugins: [
strip()
],
},
]);
- 修改src/index.js
import bar from "./bar.ts";
import "./foo.scss";
export default function () {
console.log(bar);
document.querySelector("body").innerHTML = bar;
}
使用 yarn run build 运行 Rollup,可以看到console.log部分的代码,在打包的时候已被删除
调试
1. 建立链接
例如当前项目名为rollup,想作为一个第三方包
在项目中运行npm link
,将rollup项目创建成本地依赖包
在项目例如名为my-app中,运行 npm link rollup
建立软链接,link后面的rollup为rollup项目中的package.json的name值
建立软链接之后,可以在项目my-app 的node_models中找到rollup,之后修改rollup包里的内容会实时更新,不用再运行npm link
在项目my-app中使用
import main from 'rollup';
main();
2. 解除链接
解除项目my-app 的rollup依赖,在项目 my-app 中运行
npm unlink rollup
发布
1. 发布到npm
- 如果以前改过npm的镜像地址,比如使用了淘宝的镜像,就先改回来
npm config set registry https://registry.npmjs.org/
-
如果没有npm账户,先去官网注册www.npmjs.com/ ,注册完去邮箱点击链…
-
在项目终端登录
npm login
,输入用户信息后会提示二次验证,最后把邮件的验证码输入即可 -
修改package.json
"files": [
"dist/es/*",
"dist/cjs/*"
],
配置files,设置需要推送到npm的文件
- 进行发布
npm publish
(如果包名有@符号,例如@username/component,默认为私有包,需要加上 --access public参数)
备注:这里包名为rollup,肯定会与npm上现有的包名重复,发布时会报错。需要修改package.json的name属性,重新设置属于自己的包名
- 可在npm个人主页查看上传的包
上传成功后,使用npm进行下载,可在node_modules中看到我们只上传了打包后的代码
2. 发布npm包时可能会遇到的一些坑
- 邮箱未验证
npm ERR! publish Failed PUT 403
npm ERR! code E403
npm ERR! you must verify your email before publishing a new package: https://www.npmjs.com/email-edit : your-package
去邮箱验证
- 没有权限发布
npm ERR! publish Failed PUT 403
npm ERR! code E403
npm ERR! You do not have permission to publish "your-package". Are you logged in as the correct user? : your-package
包和别人的包重名了。修改包名
- 需要登录
npm ERR! code ENEEDAUTH
npm ERR! need auth auth required for publishing
npm ERR! need auth You need to authorize this machine using `npm adduser`
使用 npm login 登录
- 只有管理员才有权限发布
npm ERR! publish Failed PUT 403
npm ERR! code E403
npm ERR! [no_perms] Private mode enable, only admin can publish this module [no_perms] Private mode enable, only admin can publish this module: your-package
源设置成第三方源,比如设置了淘宝镜像。只要把源设为默认的即可
npm config set registry http://registry.npmjs.org
- 包名过于类似
npm ERR! publish Failed PUT 403
npm ERR! code E403
npm ERR! Package name too similar to existing packages; try renaming your package to '@hopgoldy/auto-git' and publishing with 'npm publish --access=public' instead : your-package
- 无法发布私有包
npm ERR! publish Failed PUT 402
npm ERR! code E402
npm ERR! You must sign up for private packages :
这个当你的包名为@your-name/your-package时才会出现,原因是当包名以@your-name开头时,npm publish会默认发布为私有包,但是 npm 的私有包需要付费,所以需要添加如下参数进行发布:
npm publish --access public
或者在package.json中添加
"publishConfig": {
"access": "public"
},
- 包的命名空间
lerna ERR! E404 Scope not found
点击链接创建包的命名空间:www.npmjs.com/org/create