Rollup 插件系统 #
插件概述 #
Rollup 插件是一个对象,包含一个或多个属性、构建钩子和输出生成钩子。
text
┌─────────────────────────────────────────────────────────────┐
│ Rollup 插件系统 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 解析类插件 │ │ 转换类插件 │ │ 优化类插件 │ │
│ │ resolve │ │ babel │ │ terser │ │
│ │ commonjs │ │ typescript │ │ visualizer │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 资源类插件 │ │ 工具类插件 │ │ 开发类插件 │ │
│ │ image │ │ json │ │ serve │ │
│ │ postcss │ │ alias │ │ livereload │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
官方插件 #
@rollup/plugin-node-resolve #
解析 node_modules 中的模块:
bash
npm install @rollup/plugin-node-resolve --save-dev
javascript
import resolve from '@rollup/plugin-node-resolve';
export default {
plugins: [
resolve({
// 配置选项
browser: true, // 浏览器环境
dedupe: ['vue'], // 去重
extensions: ['.js', '.jsx', '.ts', '.tsx'],
mainFields: ['browser', 'module', 'main'],
preferBuiltins: false, // 不优先使用内置模块
moduleDirectories: ['node_modules', 'src'],
rootDir: process.cwd()
})
]
};
@rollup/plugin-commonjs #
转换 CommonJS 模块为 ES 模块:
bash
npm install @rollup/plugin-commonjs --save-dev
javascript
import commonjs from '@rollup/plugin-commonjs';
export default {
plugins: [
commonjs({
include: /node_modules/, // 包含的文件
exclude: [], // 排除的文件
extensions: ['.js', '.cjs'],
ignore: ['some-dependent'], // 忽略的模块
requireReturnsDefault: 'auto',
sourceMap: false,
transformMixedEsModules: false
})
]
};
@rollup/plugin-json #
导入 JSON 文件:
bash
npm install @rollup/plugin-json --save-dev
javascript
import json from '@rollup/plugin-json';
export default {
plugins: [
json({
include: 'node_modules/**',
exclude: ['node_modules/foo/**'],
preferConst: true,
indent: ' ',
compact: false,
namedExports: true
})
]
};
javascript
// 使用示例
import pkg from './package.json';
console.log(pkg.version);
@rollup/plugin-babel #
使用 Babel 转换代码:
bash
npm install @rollup/plugin-babel @babel/core --save-dev
javascript
import babel from '@rollup/plugin-babel';
export default {
plugins: [
babel({
babelHelpers: 'bundled',
exclude: 'node_modules/**',
extensions: ['.js', '.jsx', '.ts', '.tsx'],
presets: ['@babel/preset-env'],
plugins: ['@babel/plugin-transform-runtime']
})
]
};
@rollup/plugin-typescript #
编译 TypeScript:
bash
npm install @rollup/plugin-typescript typescript --save-dev
javascript
import typescript from '@rollup/plugin-typescript';
export default {
plugins: [
typescript({
tsconfig: './tsconfig.json',
include: ['src/**/*.ts', 'src/**/*.tsx'],
exclude: ['node_modules/**'],
declaration: true,
declarationDir: 'dist/types',
outDir: 'dist',
rootDir: 'src'
})
]
};
@rollup/plugin-terser #
压缩代码:
bash
npm install @rollup/plugin-terser --save-dev
javascript
import terser from '@rollup/plugin-terser';
export default {
plugins: [
terser({
compress: {
drop_console: true,
drop_debugger: true
},
mangle: true,
format: {
comments: false
},
toplevel: true,
ecma: 2020
})
]
};
@rollup/plugin-alias #
设置模块别名:
bash
npm install @rollup/plugin-alias --save-dev
javascript
import alias from '@rollup/plugin-alias';
export default {
plugins: [
alias({
entries: [
{ find: '@', replacement: 'src' },
{ find: '@utils', replacement: 'src/utils' },
{ find: '@components', replacement: 'src/components' },
{ find: /^@\/(.+)/, replacement: 'src/$1' }
],
resolve: ['.js', '.ts', '.jsx', '.tsx']
})
]
};
@rollup/plugin-replace #
替换代码中的变量:
bash
npm install @rollup/plugin-replace --save-dev
javascript
import replace from '@rollup/plugin-replace';
export default {
plugins: [
replace({
preventAssignment: true,
values: {
'process.env.NODE_ENV': JSON.stringify('production'),
'process.env.VERSION': JSON.stringify('1.0.0'),
__buildDate__: () => JSON.stringify(new Date()),
__buildVersion: 15
}
})
]
};
@rollup/plugin-virtual #
创建虚拟模块:
javascript
import virtual from '@rollup/plugin-virtual';
export default {
plugins: [
virtual({
entry: `
import { foo } from 'foo';
console.log(foo);
`,
foo: `
export const foo = 'Hello from virtual module';
`
})
],
input: 'entry'
};
@rollup/plugin-url #
导入文件为 URL 或 Base64:
bash
npm install @rollup/plugin-url --save-dev
javascript
import url from '@rollup/plugin-url';
export default {
plugins: [
url({
include: ['**/*.svg', '**/*.png', '**/*.jpg', '**/*.gif'],
exclude: ['node_modules/**'],
limit: 10 * 1024, // 小于 10KB 转 Base64
emitFiles: true,
fileName: 'assets/[name]-[hash][extname]',
publicPath: '/assets/'
})
]
};
常用第三方插件 #
rollup-plugin-postcss #
处理 CSS:
bash
npm install rollup-plugin-postcss postcss --save-dev
javascript
import postcss from 'rollup-plugin-postcss';
export default {
plugins: [
postcss({
extensions: ['.css', '.scss', '.sass', '.less'],
extract: true, // 提取到单独文件
minimize: true, // 压缩
sourceMap: true,
inject: false, // 不注入到 JS
plugins: [
require('autoprefixer'),
require('cssnano')
]
})
]
};
rollup-plugin-vue #
处理 Vue 组件:
bash
npm install rollup-plugin-vue @vue/compiler-sfc --save-dev
javascript
import vue from 'rollup-plugin-vue';
export default {
plugins: [
vue({
target: 'browser',
exposeFilename: false,
preprocessStyles: true,
css: false
})
]
};
rollup-plugin-svelte #
处理 Svelte 组件:
bash
npm install rollup-plugin-svelte svelte --save-dev
javascript
import svelte from 'rollup-plugin-svelte';
export default {
plugins: [
svelte({
compilerOptions: {
dev: !production
},
extensions: ['.svelte']
})
]
};
rollup-plugin-serve #
开发服务器:
bash
npm install rollup-plugin-serve --save-dev
javascript
import serve from 'rollup-plugin-serve';
export default {
plugins: [
serve({
contentBase: 'dist',
port: 3000,
host: 'localhost',
open: true,
headers: {
'Access-Control-Allow-Origin': '*'
}
})
]
};
rollup-plugin-livereload #
热重载:
bash
npm install rollup-plugin-livereload --save-dev
javascript
import livereload from 'rollup-plugin-livereload';
export default {
plugins: [
livereload({
watch: 'dist',
delay: 300,
clientUrl: 'localhost:35729'
})
]
};
rollup-plugin-copy #
复制文件:
bash
npm install rollup-plugin-copy --save-dev
javascript
import copy from 'rollup-plugin-copy';
export default {
plugins: [
copy({
targets: [
{ src: 'src/assets/*', dest: 'dist/assets' },
{ src: 'src/index.html', dest: 'dist' },
{ src: 'static/**/*', dest: 'dist' }
],
hook: 'writeBundle',
verbose: true
})
]
};
rollup-plugin-delete #
删除文件:
bash
npm install rollup-plugin-delete --save-dev
javascript
import del from 'rollup-plugin-delete';
export default {
plugins: [
del({
targets: 'dist/*',
runOnce: true
})
]
};
rollup-plugin-visualizer #
打包分析:
bash
npm install rollup-plugin-visualizer --save-dev
javascript
import { visualizer } from 'rollup-plugin-visualizer';
export default {
plugins: [
visualizer({
filename: 'dist/stats.html',
open: true,
gzipSize: true,
brotliSize: true
})
]
};
rollup-plugin-dts #
生成类型声明文件:
bash
npm install rollup-plugin-dts --save-dev
javascript
import dts from 'rollup-plugin-dts';
export default {
input: 'dist/types/index.d.ts',
output: {
file: 'dist/my-lib.d.ts',
format: 'es'
},
plugins: [dts()]
};
插件开发 #
基本结构 #
javascript
export default function myPlugin(options = {}) {
return {
name: 'my-plugin',
// 构建钩子
buildStart(options) {
console.log('Build started');
},
resolveId(source, importer) {
if (source === 'virtual-module') {
return source;
}
return null;
},
load(id) {
if (id === 'virtual-module') {
return 'export default "This is virtual"';
}
return null;
},
transform(code, id) {
if (id.endsWith('.custom')) {
return {
code: 'export default "Transformed"',
map: null
};
}
return null;
},
buildEnd() {
console.log('Build ended');
}
};
}
构建钩子 #
javascript
export default function myPlugin() {
return {
name: 'my-plugin',
// 选项设置
options(options) {
console.log('Options:', options);
return options;
},
// 构建开始
buildStart(options) {
console.log('Build started');
},
// 解析模块 ID
resolveId(source, importer, options) {
console.log('Resolving:', source);
return null; // 返回 null 使用默认解析
},
// 加载模块
load(id) {
console.log('Loading:', id);
return null; // 返回 null 使用默认加载
},
// 转换代码
transform(code, id) {
console.log('Transforming:', id);
return null; // 返回 null 不转换
},
// 模块解析完成
moduleParsed(moduleInfo) {
console.log('Module parsed:', moduleInfo.id);
},
// 构建结束
buildEnd(error) {
if (error) {
console.error('Build error:', error);
}
}
};
}
输出生成钩子 #
javascript
export default function myPlugin() {
return {
name: 'my-plugin',
// 输出生成开始
renderStart(outputOptions, inputOptions) {
console.log('Render started');
},
// Banner/Footer
banner() {
return '// My custom banner';
},
footer() {
return '// My custom footer';
},
// Intro/Outro
intro() {
return 'var ENV = "production";';
},
outro() {
return 'console.log("Done");';
},
// 渲染代码块
renderChunk(code, chunk, options) {
console.log('Rendering chunk:', chunk.fileName);
return null; // 返回 null 不修改
},
// 生成代码块
generateBundle(options, bundle, isWrite) {
console.log('Generating bundle');
for (const fileName in bundle) {
console.log('File:', fileName);
}
},
// 写入完成
writeBundle(options, bundle) {
console.log('Bundle written');
},
// 输出生成结束
renderDone() {
console.log('Render done');
}
};
}
实战示例:自定义插件 #
示例一:添加时间戳 #
javascript
function timestampPlugin() {
return {
name: 'timestamp',
banner() {
return `// Built at: ${new Date().toISOString()}`;
}
};
}
export default {
plugins: [timestampPlugin()]
};
示例二:环境变量注入 #
javascript
function envPlugin(env) {
return {
name: 'env-inject',
transform(code, id) {
if (id.endsWith('.js')) {
const envCode = Object.entries(env)
.map(([key, value]) => `const ${key} = ${JSON.stringify(value)};`)
.join('\n');
return {
code: envCode + '\n' + code,
map: null
};
}
return null;
}
};
}
export default {
plugins: [
envPlugin({
API_URL: 'https://api.example.com',
VERSION: '1.0.0'
})
]
};
示例三:文件复制 #
javascript
function copyPlugin(targets) {
const fs = require('fs');
const path = require('path');
return {
name: 'copy-files',
writeBundle() {
targets.forEach(({ src, dest }) => {
const srcPath = path.resolve(src);
const destPath = path.resolve(dest);
if (!fs.existsSync(destPath)) {
fs.mkdirSync(destPath, { recursive: true });
}
fs.copyFileSync(srcPath, path.join(destPath, path.basename(srcPath)));
console.log(`Copied: ${src} -> ${dest}`);
});
}
};
}
export default {
plugins: [
copyPlugin([
{ src: 'src/index.html', dest: 'dist' }
])
]
};
示例四:代码压缩统计 #
javascript
function sizePlugin() {
const sizes = new Map();
return {
name: 'size-report',
renderChunk(code, chunk) {
sizes.set(chunk.fileName, {
original: code.length,
compressed: require('zlib').gzipSync(code).length
});
return null;
},
writeBundle() {
console.log('\nBundle Size Report:');
console.log('File\t\tOriginal\tGzipped');
console.log('─'.repeat(50));
for (const [file, size] of sizes) {
console.log(
`${file}\t\t${(size.original / 1024).toFixed(2)}KB\t\t${(size.compressed / 1024).toFixed(2)}KB`
);
}
}
};
}
export default {
plugins: [sizePlugin()]
};
插件顺序 #
正确的插件顺序 #
javascript
export default {
plugins: [
// 1. 文件操作
del({ targets: 'dist/*' }),
copy({ targets: [...] }),
// 2. 模块解析
alias({ entries: [...] }),
resolve(),
commonjs(),
// 3. 代码转换
vue(),
typescript(),
babel({ babelHelpers: 'bundled' }),
postcss(),
// 4. 代码替换
replace({ ... }),
// 5. 代码优化
terser(),
// 6. 输出处理
visualizer(),
// 7. 开发工具
serve(),
livereload()
]
};
插件顺序原则 #
- 解析类插件:resolve、commonjs、alias
- 转换类插件:babel、typescript、vue、postcss
- 优化类插件:terser、replace
- 输出类插件:copy、visualizer
- 开发类插件:serve、livereload
插件调试 #
启用调试日志 #
javascript
function debugPlugin() {
return {
name: 'debug',
resolveId(source, importer) {
console.log(`[resolveId] ${source} from ${importer}`);
return null;
},
load(id) {
console.log(`[load] ${id}`);
return null;
},
transform(code, id) {
console.log(`[transform] ${id} (${code.length} bytes)`);
return null;
}
};
}
使用 this.error 和 this.warn #
javascript
function myPlugin() {
return {
name: 'my-plugin',
transform(code, id) {
if (code.includes('eval(')) {
this.warn('eval() is not recommended', { line: 1, column: 0 });
}
if (code.includes('dangerousFunction')) {
this.error('dangerousFunction is not allowed', { line: 1, column: 0 });
}
return null;
}
};
}
下一步 #
现在你已经掌握了 Rollup 插件系统,接下来学习 高级特性 了解代码分割、动态导入等高级功能!
最后更新:2026-03-28