rollup介绍
rollup 是一个 JavaScript 模块打包器,可以将小块代码编译成大块复杂的代码,例如 library 或应用程序。Rollup 对代码模块使用新的标准化格式,这些标准都包含在 JavaScript 的 ES6 版本中,而不是像CommonJS 和 AMD这种特殊解决方案。( 也有插件可以处理成umd模式 )
rollup最大的亮点就是Tree-shaking,即可以静态分析代码中的 import,并排除任何未使用的代码。这允许我们架构于现有工具和模块之上,而不会增加额外的依赖或使项目的大小膨胀。如果用webpack做,虽然可以实现tree-shaking,但是需要自己配置并且打包出来的代码非常臃肿,所以对于库文件和UI组件,rollup更加适合。
一,安装依赖
npm i rollup –save-dev
二,配置文件
rollup 的配置文件是在 rollup.config.js 中配置的,由于rollup 本身会处理 这个配置文件,所以我们可以使用 ESmodules 语法来写
我们执行 rollup –config 就可以生成打包后的文件了,我们只有带了 –config ,rollup才会执行我们的配置文件,默认是不会执行我们的配置文件的
external属性
使用rollup打包,我们在自己的库中需要使用第三方库,例如lodash等,又不想在最终生成的打包文件中出现jquery。这个时候我们就需要使用external属性。比如我们使用了lodash
三,rollup插件
- rollup-plugin-terser 用于压缩js代码
@rollup/plugin-node-resolve
让rollup
能够识别node_modules的第三方模块。@rollup/plugin-commonjs
将 CommonJS 的模块转换为 ES2015 供rollup
处理。
利用babel来编译es6代码
首先我们先安装babel相关模块:
npm i core-js @babel/core @babel/preset-env @babel/plugin-transform-runtime
设置.babelrc文件
module.exports = {
presets: [[“env”, { “modules”: false }]],
plugins:[
[“@babel/plugin-syntax-dynamic-import”],
[“external-helpers”]
]
}
@babel/preset-env可以根据配置的目标浏览器或者运行环境来自动将ES2015+的代码转换为es5。需要注意的是,我们设置”modules”: false,否则 Babel 会在 Rollup 有机会做处理之前,将我们的模块转成 CommonJS,导致 Rollup 的一些处理失败。
为了解决多个地方使用相同代码导致打包重复的问题,我们需要在.babelrc的plugins里配置@babel/plugin-transform-runtime,同时我们需要修改rollup的配置文件:
babel({
exclude: 'node_modules/**',
runtimeHelpers: true // 使plugin-tramsform-runtime生效
})
rollup 的优势和缺点
优点:
- 输出结果更加扁平,执行效率更高,
- 会自动移除未引用的代码(内置了 tree shaking)
- 打包结果的可读性比较好
缺点:
- 加载第三方模块比较复杂
- 模块被打包到一个函数中,不能实现 HMR (html-modules-replace)模块热替换
- 浏览器环境中,代码拆分功能必须依赖 amd 库,
打包器选择:
当我们使用第三方插件比较多时,我们选用webpack,当我们写一些自定义插件,库,或者框架的时候(vue、react…),我们使用rollup 作为模块打包器,webpack 大而全,rollup 小而美
注意事项
- node版本最好是10.0.0以上
- 插件的引用顺序可能会引起报错
遇到的坑
1.Babel 7.0.0-beta.56 has dropped support for the ‘helpersNamespace’ utility.
解决方案:
安装babel 6.x
npm install –save-dev rollup-plugin-babel@3
npm install –save-dev babel-core
npm install –save-dev babel-upgrade
总体思路:
- 编写多个组件,暴露各自独立入口
- 编写整体入口文件
- 编写打包脚本
- 编写打包入口文件列表或者自动获取
- 遍历入口分别使用rollup.rolup方法进行打包
- 通过内置generate方法获取打包后的数据,fs.writeFileSync创建对应文件
- 添加自定义打包script(注意运行打包脚本前需要先删除出口文件夹)
- 配置package.json相应字段
- 运行命令,打包发布
遍历
// 打包模式列表 const formatTypeList = [ { format: 'umd' , min: false , suffix: '.js' }, // { format: 'cjs', min: true, suffix: '.common.min.js' }, // { format: 'umd', min: false, suffix: '.umd.js' }, // { format: 'umd', min: true, suffix: '.umd.min.js' } // { format: 'es', min: false, suffix: '.js' } // { format: 'es', min: true, suffix: '.es.min.js' } ] // 创建目录 fs.mkdirSync( 'lib' ) fs.mkdirSync(getAssetsPath( 'style' )) let packages = [] formatTypeList.forEach(({ format, min, suffix } = {}) => { entries.forEach(({ input, output , module }) => { packages.push({ min, format,suffix,module, input, output: `${output}`}) }) }) build(packages) |
打包
const { output, suffix, input, format, module , min } = config const inputOptions = { input, external: Object.keys(external), plugins : plugins // plugins: min ? plugins.push(terser()) : plugins } const fullName = output + suffix const file = getAssetsPath(fullName) const outOptions = { file, format, name: module, globals: external } console.log(rollup); const bundle = await new rollup.rollup(inputOptions) let { output: outputData } = await bundle.generate(outOptions) await write({ output: outputData, fileName: output, format, fullName, file }) |
创建文件
for (const { type, code, source } of output) { if (type === 'asset' ) { const cssFileName = `${fileName}.css` const filePath = getAssetsPath(`/style/${cssFileName}`) // rollup.write(filePath,source.toString()) !fsExistsSync(filePath) && fs.writeFileSync(filePath,source.toString()) } else { const filePath = file let codeSource = code.replace(/\s?const\s/g, ' var ' ) fs.writeFileSync(filePath, codeSource) } } |