文章目录
- @rollup/plugin-node-resolve
- @rollup/plugin-commonjs
- @rollup/plugin-babel
- @babel/preset-react
- rollup-plugin-postcss
- rollup-plugin-vue
- rollup-plugin-terser
- @rollup/plugin-alias
- @rollup/plugin-strip
- rollup-plugin-copy
- rollup-plugin-clear
- @rollup/plugin-image
- @rollup/plugin-url
- @rollup/plugin-html
- rollup-plugin-serve
- rollup-plugin-livereload
通过阅读 rollup打包工具核心配置详解 我们知道了rollup的基础用法,在实际应用中,会有很多更复杂的需求,比如,如何支持es6语法,如何打包.vue、图片、压缩js等等。在rollup中,我们借助插件来完成。
在webpack中,使用loader对源文件进行预处理,plugin完成构建打包的特定功能需求。rollup的plugin兼具webpack中loader和plugin的功能。
@rollup/plugin-node-resolve
@rollup/plugin-node-resolve 插件可以告诉 Rollup 如何查找外部模块(node_modules 中的模块)。
比如我们使用 the-answer
,代码如下
npm install the-answer
// src/index.js
import answer from 'the-answer';
export default function () {
console.log('the answer is ' + answer);
}
// the-answer.js 源码
var index = 42;
export default index;
// rollup.config.js
export default {
input: 'src/index.js',
output: {
file: 'dist/index.js',
format: 'es',
}
};
执行 yarn run rollup -c
,会发现waring
打包结果如下
可以看到并没有编译打包 the-answer
模块,而是将引用的部分照搬过来。
这是因为 rollup 没有成功解析外部依赖,因此我们 @rollup/plugin-node-resolve
来解决这一类问题
// rollup.config.js
import resolve from '@rollup/plugin-node-resolve'
export default {
input: 'src/index.js',
output: {
file: 'dist/index.js',
format: 'es',
},
plugins: [
resolve(),
]
};
执行 yarn run rollup -c
,再没有警告输出 - 打包文件 bundle 包含了引用的模块。
@rollup/plugin-commonjs
一些库会导出成你可以正常导入的 ES6 模块。 但是目前, npm 中的大多数包都是以 CommonJS
模块的形式出现的。 在它们更改之前,我们需要将 CommonJS 模块转换为 ES2015 供 Rollup 处理。
@rollup/plugin-commonjs 插件就是用来将 CommonJS 转换成 ES2015 模块的。
请注意,@rollup/plugin-commonjs
应该用在其他插件转换你的模块之前 - 这是为了防止其他插件的改变破坏 CommonJS 的检测。
修改上面 the-answer
模块源码
// the-answer.js 源码
var index = 42;
module.exports index;
执行 yarn run rollup -c
,会发现报错,如下:
the-answer
的导出模式 module.exports
是 commonjs
模式,是不被标准模块所支持的。
因此我们需要将 CommonJS 模块转换为 ES2015 供 Rollup 处理。
// rollup.config.js
import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
export default {
input: 'src/main.js',
output: {
file: 'dist/index.js',
format: 'es',
},
plugins: [
resolve(),
commonjs(),
]
};
执行 yarn run rollup -c
,会发现打包成功,打包文件 dist/index.js 包含了引用的模块。
@rollup/plugin-babel
通过 Babel
将 es6/es7
代码编译转换为 es5
npm i -D @rollup/plugin-babel @babel/core @babel/preset-env
- @rollup/plugin-babel:在rollup里应用 babel 解析ES6的桥梁
@babel/core
:babel核心模块@babel/preset-env
:babel预设,内置一组 babel 插件的集合
在同一个 Rollup 配置中同时使用 @rollup/plugin-babel
和 @rollup/plugin-commonjs
时,需要注意的是,@rollup/plugin-commonjs
必须将其放在 @rollup/plugin-babel
之前,如下:
mport {
babel } from '@rollup/plugin-babel';
import commonjs from '@rollup/plugin-commonjs';
const config = {
...
plugins: [
commonjs(),
babel({
babelHelpers: 'runtime' })
],
};
Options
exclude
-
String | RegExp | Array[...String|RegExp]
-
它指定插件应该忽略的构建文件。当依赖 Babel 配置文件时,只能使用此选项排除其他文件,不能覆盖为 Babel 本身配置的内容。
include
-
String | RegExp | Array[...String|RegExp]
-
指定插件应该操作的构建中的文件。当依赖 Babel 配置文件时,不能包含已经排除的文件。
extensions
-
类型:
Array[...String]
-
默认值:
['.js', '.jsx', '.es6', '.es', '.mjs']
-
Babel 应该转换的文件扩展名数组。如果你想用这个插件转译 TypeScript 文件,必须在这个选项中包含
.ts
和。.tsx
babelHelpers
- 类型:
'bundled' | 'runtime' | 'inline' | 'external'
- 默认值:
'bundled'
建议显式配置此选项(即使使用其默认值),以便对如何将这些 babel 助手插入代码做出明智的决定。
-
'runtime'
在使用 Rollup 构建library
时应该使用此配置。它必须结合使用@babel/plugin-transform-runtime
使用,并且还应该将@babel/runtime
为包的依赖项。@babel/runtime
在捆绑cjs
&es
格式时,不要忘记告诉 Rollup 将从模块中导入的帮助程序视为外部依赖项。这可以通过正则表达式 (external: [/@babel\/runtime/]
) 或函数 (external: id => id.includes('@babel/runtime')
) 来完成。-
npm i @babel/runtime
-
npm i @babel/plugin-transform-runtime -D
-
-
'bundled'
主要应用在打包应用程序代码。如果你希望生成的代码包含helpers(每个最多一份),你应该使用它。
// rollup.config.js
import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import babel from '@rollup/plugin-babel'
export default {
input: 'src/index.js',
output: {
file: 'dist/index.js',
format: 'es'
},
external: [/@babel\/runtime/],
plugins: [
resolve(),
commonjs(),
babel({
babelHelpers: 'runtime',
exclude: 'node_modules/**' // 只编译我们的源代码
}),
]
}
// babel.config.js
module.exports = {
presets: [
[
'@babel/preset-env',
{
modules: false, // 将此设置为false将保留 ES 模块,否则 Babel 会在 Rollup 有机会做处理之前,将我们的模块转成 CommonJS ,导致 Rollup 的一些处理失败。
useBuiltIns: 'usage',
corejs: '3.25.0'
}
]
],
plugins: ['@babel/plugin-transform-runtime']
}
@babel/preset-react
@babel/preset-react 为所有 React 插件提供 Babel 预设
// src/Image.jsx
import React from 'react'
const Image = () => <h1>Image组件</h1>
export default Image
// rollup.config.js
import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import babel from '@rollup/plugin-babel'
export default {
input: 'src/Image.jsx',
output: {
file: 'dist/index.js',
format: 'es'
},
external: [/@babel\/runtime/, 'react'],
plugins: [
resolve(),
commonjs(),
babel({
babelHelpers: 'runtime',
exclude: 'node_modules/**' // 只编译我们的源代码
})
]
}
// babel.config.js
module.exports = {
presets: [
[
'@babel/preset-env',
{
modules: false,
useBuiltIns: 'usage',
corejs: '3.25.0'
}
]
],
plugins: ['@babel/plugin-transform-runtime']
}
当没有 @babel/preset-react
预设参与编译的babel打包 react 结果如下
如上提示,添加 @babel/preset-react
到你的Babel配置的 presets
部分,以启用转换。
npm install -D @babel/preset-react
此预设始终包含以下插件:
- @babel/plugin-syntax-jsx
- @babel/plugin-transform-react-jsx
- @babel/plugin-transform-react-display-name
没有选项的配置:
{
"presets": ["@babel/preset-react"]
}
有选项的配置:
{
"presets": [
[
"@babel/preset-react",
{
"pragma": "dom", // 默认 React.createElement(仅在经典运行时)
"pragmaFrag": "DomFrag", // 默认 React.Fragment(仅在经典运行时)
"throwIfNamespace": false, // 默认 true
"runtime": "classic" // 默认 classic
// "importSource": "custom-jsx-library" // 默认 react(仅在经典运行时)
}
]
]
}
加入 @babel/preset-react
module.exports = {
presets: [
[
'@babel/preset-env',
{
modules: false,
useBuiltIns: 'usage',
corejs: '3.25.0'
}
],
'@babel/preset-react'
],
plugins: ['@babel/plugin-transform-runtime']
}
运行 npx rollup -c
重新编译,可以看到编译时成功的
rollup-plugin-postcss
使用 rollup-plugin-postcss 处理css,它支持css文件的加载、css加前缀、css压缩、对scss/less的支持等等。
它很强大,在集成本插件之后,你将可以导入各种 postcss 插件
,完成关于 css
的各种复杂操作
npm i rollup-plugin-postcss postcss -D
这里演示less
// src/testCss.js
import './style.less'
// src/style.less
.bg {
color: red;
.title {
color: black;
}
}
// babel.config.js
import postcss from 'rollup-plugin-postcss'
export default {
input: 'src/testCss.js',
output: {
file: 'dist/index.js',
format: 'es'
},
plugins: [
postcss({
extract: true // 分离出css文件,默认 false,此处为了方便演示打包结果
})
]
}
执行 npx rollup -c
,,打包结果如下
.bg {
color: red;
}
.bg .title {
color: black;
}
Extract CSS
postcss({
extract: true, // 单独提取出css文件
// 也可以自定义文件名
extract: 'my-custom-file-name.css'
})
CSS modules
postcss({
modules: true,
// 也可以自定义 postcss-modules 选项,https://www.npmjs.com/package/postcss-modules
modules: {
}
})
使用 Sass/Stylus/Less
安装对应的依赖:
- 对于
Sass
安装node-sass
:yarn add node-sass --dev
- 对于
Stylus
安装stylus
:yarn add stylus --dev
- 对于
Less
安装less
:yarn add less --dev
安装对应依赖后就可以在你的 library 中引用 .styl
.scss
.sass
.less
文件了
imports
仅适用于 Sass/Scss。
与 webpack 的sass-loader的工作方式类似,您可以在路径前添加~
以告诉此插件解析node_modules
:
@import "~bootstrap/dist/css/bootstrap";
autoprefixer
可以使用 autoprefixer
插件为CSS3添加前缀
npm i autoprefixer -D
// babel.config.js
import postcss from 'rollup-plugin-postcss'
import autoprefixer from 'autoprefixer'
export default {
plugins:[
postcss({
plugins: [
autoprefixer()
]
})
]
}
Options
plugins
-
Type:
Array
-
PostCSS Plugins.
inject
-
Type:
boolean
object
function(cssVariableName, fileId): string
-
Default:
true
-
默认将 CSS 插入到
<head>
标签中,当extract: true
时为 false -
还可以使用
style-inject
、或者一个返回字符串的函数
extract
-
Type:
boolean
string
-
Default:
false
-
提取css,也可以自定义文件名及路径
minimize
- Type:
boolean
object
- Default:
false
- 压缩 CSS,可设置
boolean
或使用 cssnano
use
-
Type:
name[]
[name, options][]
{ sass: options, stylus: options, less: options }
-
Default:
['sass', 'stylus', 'less']
-
使用loader
// babel.config.js
import NpmImport from 'less-plugin-npm-import';
import postcss from 'rollup-plugin-postcss'
export default {
plugins:[
postcss({
use: {
less: {
plugins: [new NpmImport({
prefix: '~' })], // 使用带~前缀引入less文件
javascriptEnabled: true
// ...otherOptions
},
sass: {
// ...otherOptions
}
}
})
]
}
更多配置及文档请查看官方文档 rollup-plugin-postcss
rollup-plugin-vue
rollup-plugin-vue 用于处理 .vue文件
vue2和vue3项目所用的rollup-plugin-vue版本不一样,vue的编译器也不一样
- vue2:
rollup-plugin-vue^5.1.9
+vue-template-compiler
- vue3:
rollup-plugin-vue^6.0.0
+@vue/compiler-sfc
rollup-plugin-vue 一定要在 @rollup/plugin-commonjs 之前调用,不然会报错
以vue3为例:
npm i rollup-plugin-vue @vue/compiler-sfc -D
<template>
<div class="wrap">
<button class="btn" @click="increase">outer age++</button>: {
{ age }}
</div>
</template>
<script>
import {
ref } from "vue";
export default {
name: 'HelloWorld',
setup() {
let age = ref(0)
const increase = () => {
age.value++
}
return {
age,
increase
}
}
}
</script>
<style lang="less" scoped>
.wrap {
border-top: 1px solid salmon;
padding-top: 10px;
.btn {
color: gold;
}
}
</style>
// babel.config.js
import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import vue from 'rollup-plugin-vue'
import postcss from 'rollup-plugin-postcss'
export default {
input: 'src/HelloWorld.vue',
output: {
file: 'dist/index.js',
format: 'es'
},
external: ['vue'],
plugins: [
resolve(),
vue(), // 一定要在 commonjs 之前调用,不然会报错
commonjs(),
postcss()
]
}
打包结果如下:
import {
ref, openBlock, createElementBlock, createElementVNode, createTextVNode, toDisplayString } from 'vue';
var script = {
name: 'HelloWorld',
setup() {
let age = ref(0);
const increase = () => {
age.value++;
};
return {
age,
increase
}
}
};
const _hoisted_1 = {
class: "wrap" };
function render(_ctx, _cache, $props, $setup, $data, $options) {
return (openBlock(), createElementBlock("div", _hoisted_1, [
createElementVNode("button", {
class: "btn",
onClick: _cache[0] || (_cache[0] = (...args) => ($setup.increase && $setup.increase(...args)))
}, "outer age++"),
createTextVNode(": " + toDisplayString($setup.age), 1 /* TEXT */)
]))
}
function styleInject(css, ref) {
if ( ref === void 0 ) ref = {
};
var insertAt = ref.insertAt;
if (!css || typeof document === 'undefined') {
return; }
var head = document.head || document.getElementsByTagName('head')[0];
var style = document.createElement('style');
style.type = 'text/css';
if (insertAt === 'top') {
if (head.firstChild) {
head.insertBefore(style, head.firstChild);
} else {
head.appendChild(style);
}
} else {
head.appendChild(style);
}
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
}
var css_248z = ".wrap[data-v-671062ce] {\n border-top: 1px solid salmon;\n padding-top: 10px;\n}\n.wrap[data-v-671062ce] .btn[data-v-671062ce] {\n color: gold;\n}\n";
styleInject(css_248z);
script.render = render;
script.__scopeId = "data-v-671062ce";
script.__file = "src/HelloWorld.vue";
export {
script as default };
经测试,打包结果是正确可使用的,你可以将上述打包结果运行在vue3下试试
rollup-plugin-terser
使用 rollup-plugin-terser 进行代码压缩
// babel.config.js
import {
rollup } from "rollup";
import {
terser } from "rollup-plugin-terser";
rollup({
input: "main.js",
plugins: [terser()],
});
@rollup/plugin-alias
@rollup/plugin-alias 在构建包时定义“别名”
// babel.config.js
import alias from '@rollup/plugin-alias';
module.exports = {
// ...
plugins: [
alias({
entries: [
{
find: '@', replacement: './src' },
{
find: '@common', replacement: './src/common' }
]
})
]
};
@rollup/plugin-strip
@rollup/plugin-strip 在打包时从代码中删除 debugger
、assert.equal
和 console.*
import strip from '@rollup/plugin-strip';
export default {
input: 'src/index.js',
output: {
dir: 'output',
format: 'es'
},
plugins: [
strip({
labels: ['unittest']
})
]
};
源码
import answer from 'the-answer'
export default function () {
unittest:
document.write(answer)
return 'the answer is ' + answer
}
console.log(11111)
console.error(new Error('系统错误'))
debugger
打包编译后,剔除了 console.*
、debugger
、labels
定义的字符所标记的语句,如下
// dist/index.js
var index$1 = 42;
function index () {
return 'the answer is ' + index$1
}
export {
index as default };
rollup-plugin-copy
rollup-plugin-copy 打包时复制复制文件和文件夹
// rollup.config.js
import copy from 'rollup-plugin-copy'
export default {
input: 'src/index.js',
output: {
file: 'dist/app.js',
format: 'cjs'
},
plugins: [
copy({
targets: [
{
src: 'src/index.html', dest: 'dist/public' },
{
src: ['assets/fonts/arial.woff', 'assets/fonts/arial.woff2'], dest: 'dist/public/fonts' },
{
src: 'assets/images/**/*', dest: 'dist/public/images' }
]
})
]
}
targets
-
Type:
Array
-
Default:
[]
要复制的目标数组。目标是具有属性的对象:
- src (
string
Array
): 要复制的路径 - dest (
string
Array
): 复制的一个或多个目的地 - rename (
string
Function
): 更改目标文件或文件夹名称 - transform (
Function
): 修改文件内容
rollup-plugin-clear
rollup-plugin-clear 打包前清除旧资源
// rollup.config.js
import clear from 'rollup-plugin-clear'
export default {
// ...
plugins: [
// ...
clear({
// 必填项,指出哪些目录应该被清理
targets: ['dist'],
// 可选,当在watch模式下进行重新编译时是否清除目录
watch: true // default: false
})
]
}
@rollup/plugin-image
@rollup/plugin-image 支持导入 JPG, PNG, GIF, SVG, 和 WebP 文件。图像使用base64编码,这意味着它们将比磁盘上的大小大33%,你应该只对小图片使用这种方法
// rollup.config.js
import image from '@rollup/plugin-image';
export default {
input: 'src/index.js',
output: {
dir: 'output',
format: 'es'
},
plugins: [image()]
};
// src/index.js
import lvfengxian from './static/lvfengxian.jpeg'
import rollup from './static/rollup.png'
import svg from './static/svg.svg'
import gif from './static/gif.gif'
console.log('lvfengxian', lvfengxian)
console.log('rollup', rollup)
console.log('svg', svg)
console.log('gif', gif)
const jpegImg = document.createElement('img')
const pngImg = document.createElement('img')
const svgImg = document.createElement('img')
const gifImg = document.createElement('img')
jpegImg.src = lvfengxian
pngImg.src = rollup
svgImg.src = svg
gifImg.src = gif
document.body.appendChild(jpegImg)
document.body.appendChild(pngImg)
document.body.appendChild(svgImg)
document.body.appendChild(gifImg)
@rollup/plugin-url
@rollup/plugin-url 不同于 @rollup/plugin-image
只能生成内联 data URI 形式,它在生成内联 data URI 和生成单独的文件之间自动选择
// rollup.config.js
import url from '@rollup/plugin-url';
export default {
input: 'src/index.js',
output: {
dir: 'output',
format: 'es'
},
plugins: [url()]
};
// src/index.js
import lvfengxian from './static/lvfengxian.jpeg' // 75.55kb
import rollup from './static/rollup.png' // 5.2kb
import svg from './static/svg.svg' // 3.12kb
import gif from './static/gif.gif' // 37.65kb
console.log('lvfengxian', lvfengxian)
console.log('rollup', rollup)
console.log('svg', svg)
console.log('gif', gif)
const jpegImg = document.createElement('img')
const pngImg = document.createElement('img')
const svgImg = document.createElement('img')
const gifImg = document.createElement('img')
jpegImg.src = lvfengxian
pngImg.src = rollup
svgImg.src = svg
gifImg.src = gif
document.body.appendChild(jpegImg)
document.body.appendChild(pngImg)
document.body.appendChild(svgImg)
document.body.appendChild(gifImg)
limit
- Type:
Number
- Default:
14336
(14kb) - 内联文件的文件大小限制。如果文件超过此限制,它将被复制到目标文件夹并提供散列文件名。如果
limit
设置为0
所有文件将被复制。
publicPath
-
Type:
String
-
Default: 空字符串
-
当文件超出
limit
限制被复制为单独的文件时,定义文件导入路径
emitFiles
- Type:
Boolean
- Default:
true
- 如果
false
, 将阻止此插件发出文件。当您使用 Rollup 同时发出客户端和服务器端包时,这很有用。
fileName
- Type:
String
- Default:
'[hash][extname]'
如果emitFiles
是true
,此选项可用于重命名发出的文件。它接受以下字符串替换:
[hash]
- 文件内容的哈希值[name]
- 导入文件的名称(不含文件扩展名)[extname]
- 导入文件的扩展名(包括前导.
)[dirname]
- 导入文件的父目录名(包括尾随/
)
destDir
- Type:
String
- Default: (empty string)
- 被复制文件的目标目录,通常用于根据 HTML 文件重新定位资源
@rollup/plugin-html
@rollup/plugin-html 将为你生成一个 HTML5 文件, 在 body 中使用 script
标签引入你所有 rollup
生成的 bundle。
// rollup.config.js
import html from '@rollup/plugin-html'
module.exports = {
input: 'src/index.js',
output: {
dir: 'output',
format: 'es'
},
plugins: [html()]
};
生成的 html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Rollup Bundle</title>
</head>
<body>
<script src="index.js" type="module"></script>
</body>
</html>
attributes
-
Type:
Object
-
Default:
{ html: { lang: 'en' }, link: null, script: null }
-
为
html
、link
、script
自动添加的属性
fileName
-
Type:
String
-
Default:
'index.html'
-
指定要发出的 HTML 的名称
meta
-
Type:
Array[...object]
-
Default:
[{ charset: 'utf-8' }]
-
指定用于创建
<meta>
元素的属性。对于每个数组项,为对象提供表示<meta>
元素属性名称和值的键值对。
title
-
Type:
String
-
Default:
'Rollup Bundle'
-
指定 HTML 文档标题
rollup-plugin-serve
rollup-plugin-serve 用于启动一个本地服务运行 rollup 打包结果
// rollup.config.js
import serve from 'rollup-plugin-serve'
export default {
input: 'src/main.js',
output: {
file: 'dist/bundle.js',
format: ...
},
plugins: [
serve('dist')
]
}
Options
// 默认情况下,它服务于当前项目文件夹。通过传递一个字符串来改变它:
serve('public') // 从该目录提供静态文件
// 配置
serve({
// 在浏览器中自动启动 (默认: false)
open: true,
// 打开浏览器时要导航到的页面。
// 当 open=false 时不会生效
// 记住以斜杠开头。
openPage: '/different/page',
// 在控制台中显示服务器地址(默认值:true)
verbose: false,
// 提供静态文件的文件夹
contentBase: '',
// 从多个文件夹中提供静态文件
contentBase: ['dist', 'static'],
// 设置为 true 以返回 index.html (200) 而不是错误页面 (404)
historyApiFallback: false,
// 回退页的路径
historyApiFallback: '/200.html',
// 设置服务器时使用的选项
host: 'localhost',
port: 10001,
// 默认情况下,服务器将通过HTTP (https: false)提供服务。可以选择通过HTTPS提供服务
https: {
key: fs.readFileSync('/path/to/server.key'),
cert: fs.readFileSync('/path/to/server.crt'),
ca: fs.readFileSync('/path/to/ca.pem')
},
// 自定义 headers
headers: {
'Access-Control-Allow-Origin': '*',
foo: 'bar'
},
// 设置自定义mime类型, usage https://github.com/broofa/mime#mimedefinetypemap-force--false
mimeTypes: {
'application/javascript': ['js_commonjs-proxy']
}
// 服务启动后的监听函数
onListening: function (server) {
const address = server.address()
const host = address.address === '::' ? 'localhost' : address.address
// by using a bound function, we can access options as `this`
const protocol = this.https ? 'https' : 'http'
console.log(`Server listening at ${
protocol}://${
host}:${
address.port}/`)
}
})
rollup-plugin-livereload
rollup-plugin-livereload 用于文件变化时,实时刷新页面
配合 -w/--watch
监听源文件是否有改动,如果有改动则自动重新打包
// rollup.config.js
import livereload from 'rollup-plugin-livereload'
export default {
input: 'src/main.js',
output: {
file: 'dist/bundle.js',
format: ...
},
plugins: [
livereload('dist')
]
}
npx rollup -wc
# 或
npx rollup -c -w
持续更新中