Rollup 面试题
基础概念
Rollup 是什么?它的核心特点是什么?
Rollup 是一个 JavaScript 模块打包器,专注于将小块代码编译成更大、更复杂的代码库。
核心特点:
- Tree Shaking:自动移除未使用的代码,效果最好
- ES Modules 优先:原生支持 ES modules
- 代码体积小:生成的代码更小、更高效
- 适合库开发:输出代码质量高
- 配置简单:相比 Webpack 配置更简洁
Rollup 和 Webpack 的主要区别是什么?
| 特性 | Rollup | Webpack |
|---|---|---|
| 定位 | 库开发 | 应用开发 |
| Tree Shaking | 默认启用,效果最好 | 需要配置 |
| 代码体积 | 更小 | 较大 |
| HMR | 需要插件 | 内置 |
| 配置复杂度 | 简单 | 复杂 |
| 插件生态 | 较小 | 丰富 |
| 适用场景 | 库、框架 | 大型应用 |
Rollup 的工作原理是什么?
工作流程:
- 解析入口文件:读取入口文件,解析为 AST
- 依赖分析:递归分析所有依赖模块
- Tree Shaking:静态分析,移除未使用的代码
- 代码转换:使用插件转换代码(Babel、TypeScript 等)
- 代码合并:将所有模块合并成单个文件或多个 chunk
- 代码生成:生成目标格式的代码(ES、CommonJS、UMD 等)
为什么 Rollup 的 Tree Shaking 效果最好?
原因:
- ES Modules 优先:ES modules 是静态的,可以在编译时分析
- 静态分析:在打包前就能确定哪些代码被使用
- 精确标记:可以精确标记和移除未使用的代码
- 无副作用假设:默认假设代码无副作用,可以安全移除
配置相关
Rollup 如何配置多入口?
export default {
input: {
main: 'src/index.js',
utils: 'src/utils.js',
},
output: {
dir: 'dist',
format: 'es',
entryFileNames: '[name].js',
},
};
Rollup 如何配置代码分割?
export default {
input: 'src/index.js',
output: {
dir: 'dist',
format: 'es',
manualChunks: {
vendor: ['react', 'react-dom'],
utils: ['./src/utils/helper1', './src/utils/helper2'],
},
},
};
Rollup 如何处理外部依赖?
export default {
external: [
'react',
'react-dom',
/^lodash/, // 正则匹配
(id) => id.includes('node_modules'), // 函数匹配
],
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM',
},
},
};
Rollup 的输出格式有哪些?如何选择?
输出格式:
- es:ES modules,适合现代浏览器和 Node.js
- cjs:CommonJS,适合 Node.js
- umd:通用模块定义,支持浏览器和 Node.js
- iife:立即执行函数,适合浏览器直接使用
- amd:AMD 格式
- system:SystemJS 格式
选择建议:
- 库开发:同时输出
es和cjs - 浏览器使用:
umd或iife - Node.js 使用:
cjs
插件系统
Rollup 常用插件有哪些?
核心插件:
@rollup/plugin-node-resolve:解析 node_modules 中的模块@rollup/plugin-commonjs:将 CommonJS 转换为 ES modules@rollup/plugin-json:导入 JSON 文件
编译插件:
@rollup/plugin-babel:Babel 转换@rollup/plugin-typescript:TypeScript 支持rollup-plugin-esbuild:esbuild 转换(更快)
其他插件:
@rollup/plugin-replace:替换代码中的变量@rollup/plugin-alias:路径别名rollup-plugin-terser:代码压缩rollup-plugin-postcss:CSS 处理
如何编写一个 Rollup 插件?
export default function myPlugin(options = {}) {
return {
name: 'my-plugin',
// 构建开始时
buildStart() {
console.log('Build started');
},
// 解析导入
resolveId(id) {
if (id === 'virtual-module') {
return id;
}
},
// 加载模块
load(id) {
if (id === 'virtual-module') {
return 'export default "virtual"';
}
},
// 转换代码
transform(code, id) {
if (id.endsWith('.js')) {
return code.replace('foo', 'bar');
}
},
// 生成代码
generateBundle(options, bundle) {
// 修改输出文件
},
};
}
Tree Shaking
Rollup 的 Tree Shaking 原理是什么?
原理:
- 静态分析:分析 ES modules 的导入导出
- 标记使用:标记哪些导出被使用
- 移除未使用:移除未被使用的代码
- 副作用检测:检测代码副作用,安全移除
条件:
- 使用 ES modules 语法
- 代码无副作用或标记为纯函数
- 使用
/*#__PURE__*/标记副作用
如何确保 Tree Shaking 生效?
方法:
- 使用 ES modules 语法
- 避免副作用
- 使用
/*#__PURE__*/标记 - 配置
treeshake选项
export default {
treeshake: {
moduleSideEffects: false, // 假设模块无副作用
propertyReadSideEffects: false,
tryCatchDeoptimization: false,
},
};
Tree Shaking 的局限性是什么?
局限性:
- 动态导入:无法静态分析动态导入
- 副作用代码:无法移除有副作用的代码
- CommonJS:对 CommonJS 支持有限
- 运行时行为:无法分析运行时行为
性能优化
如何提升 Rollup 构建速度?
优化方法:
- 使用 esbuild:替代 Babel,速度提升 10-100 倍
- 并行构建:使用 Promise.all 并行构建多个格式
- 缓存:使用 Rollup 的缓存机制
- 减少插件:只使用必要的插件
- 外部依赖:合理配置 external
import { esbuild } from 'rollup-plugin-esbuild';
export default {
plugins: [
esbuild({
target: 'es2015',
minify: true,
}),
],
};
Rollup 如何处理大型项目?
策略:
- 代码分割:使用 manualChunks
- 外部依赖:将大型库设为 external
- 并行处理:使用多进程
- 增量构建:使用缓存
常见问题
Rollup 如何处理 CommonJS 模块?
处理方式:
- 使用
@rollup/plugin-commonjs插件 - 将 CommonJS 转换为 ES modules
import commonjs from '@rollup/plugin-commonjs';
export default {
plugins: [
commonjs({
include: /node_modules/,
transformMixedEsModules: true,
}),
],
};
Rollup 支持 HMR 吗?
支持方式:
- 需要额外插件,如
rollup-plugin-serve+rollup-plugin-livereload - 或使用开发工具如
rollup-plugin-dev
import serve from 'rollup-plugin-serve';
import livereload from 'rollup-plugin-livereload';
export default {
plugins: [
serve({
open: true,
contentBase: 'dist',
}),
livereload('dist'),
],
};
Rollup 如何处理 CSS?
处理方式: 使用 rollup-plugin-postcss 插件
import postcss from 'rollup-plugin-postcss';
export default {
plugins: [
postcss({
extract: true, // 提取 CSS
minimize: true, // 压缩
plugins: [
require('autoprefixer'),
],
}),
],
};
Rollup 如何处理 TypeScript?
处理方式:
- 使用
@rollup/plugin-typescript - 或使用
rollup-plugin-esbuild(更快)
import typescript from '@rollup/plugin-typescript';
export default {
plugins: [
typescript({
tsconfig: './tsconfig.json',
declaration: true,
declarationDir: './dist/types',
}),
],
};
Rollup 如何处理环境变量?
处理方式: 使用 @rollup/plugin-replace
import replace from '@rollup/plugin-replace';
export default {
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
__VERSION__: JSON.stringify(require('./package.json').version),
}),
],
};
库开发
如何用 Rollup 开发一个库?
配置示例:
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import { terser } from 'rollup-plugin-terser';
import pkg from './package.json';
export default {
input: 'src/index.ts',
output: [
{
file: pkg.main, // CommonJS
format: 'cjs',
sourcemap: true,
},
{
file: pkg.module, // ES modules
format: 'es',
sourcemap: true,
},
{
file: pkg.browser, // UMD
format: 'umd',
name: 'MyLibrary',
sourcemap: true,
},
],
plugins: [
resolve(),
commonjs(),
typescript({
declaration: true,
declarationDir: './dist/types',
}),
terser(),
],
external: [
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {}),
],
};
为什么库开发推荐使用 Rollup?
原因:
- Tree Shaking 效果好:生成的代码更小
- 代码质量高:输出代码更接近手写代码
- 多格式支持:可以同时输出多种格式
- 配置简单:相比 Webpack 配置更简单
- 性能好:构建速度快
Rollup 如何生成类型声明文件?
方式: 使用 @rollup/plugin-typescript 插件
import typescript from '@rollup/plugin-typescript';
export default {
plugins: [
typescript({
declaration: true,
declarationDir: './dist/types',
}),
],
};
最佳实践
Rollup 最佳实践
- 使用 ES modules:充分利用 Tree Shaking
- 合理使用 external:避免打包大型依赖
- 多格式输出:同时输出 ES、CommonJS、UMD
- 类型声明:生成 TypeScript 类型声明文件
- 代码压缩:生产环境启用压缩
- Source Map:开发环境生成 Source Map
Rollup 常见错误及解决方案
问题1:CommonJS 模块无法解析
- 使用
@rollup/plugin-commonjs插件
问题2:Node.js 内置模块报错
- 在
resolve插件中设置preferBuiltins: false
问题3:动态导入问题
- 使用
@rollup/plugin-dynamic-import-vars
问题4:循环依赖
- Rollup 会自动处理,但建议重构代码
问题5:Tree Shaking 不生效
- 确保使用 ES modules 语法
- 检查是否有副作用
- 配置
treeshake选项
与其他工具对比
Rollup vs Webpack
| 特性 | Rollup | Webpack |
|---|---|---|
| Tree Shaking | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 代码体积 | 最小 | 较大 |
| HMR | 需插件 | 内置 |
| 配置 | 简单 | 复杂 |
| 适用场景 | 库开发 | 应用开发 |
Rollup vs Vite
| 特性 | Rollup | Vite |
|---|---|---|
| 开发模式 | 需要打包 | 无需打包 |
| 启动速度 | 较慢 | 极快 |
| 生产构建 | Rollup | Rollup |
| 适用场景 | 库开发 | 应用开发 |
什么时候选择 Rollup?
选择 Rollup 如果:
- 开发库或框架
- 需要 Tree Shaking 效果最好
- 追求代码体积最小
- 项目相对简单
- 不需要复杂的开发服务器功能