前言
rollup 比较适合打包 JS 的 sdk 或者封装的框架等, 例如, vue 源码就是 rollup 打包的. webpack 比较适合打包一些应用, 例如 SPA 或者同构项目等等. 最近我们对 rollup 小试牛刀了一下. 简单分享一些注意事项吧.
rollup 基础
rollup 基础知识及插件的一些使用, 网上有不少资料, 可以去查阅.
rollup 中文网: https://www.rollupjs.com/guide/zh#-danger-zone-
问题记录吧
下面主要记录一下 rollup 使用过程中的一些报错及解决方案吧.
错误一:
You must supply http://options.name for IIFE bundles
出现这个报错, 是要在 options 中指定 name, 例如如下:
- output: {
- file: resolve(`js/haorooms.${type}.js`),
- format: type,
- name: 'haorooms',
- banner
- },
错误二
[!] (commonjs plugin) SyntaxError: Unexpected token
出现这个问题, 可能有几个原因
1,rollup-plugin-commonjs 未引入, 或者加载循序不对
我们知道, webpack loader 是有加载循序的 (从右到左, 从下到上),rollup 虽然没有严格的加载循序, 但是我通常是将 commonjs 放到 babel 编译之后. 如下:
- babel({
- exclude: 'node_modules/**', // 排除 node_modules 下的文件
- runtimeHelpers: true
- }),
- commonjs(),
2, 缺少 babel 类 我按照 babel 类熟悉编译插件作为这个项目的依赖.
NPM install --save-dev babel-plugin-transform-class-properties
.babelrc 配置如下:
- {
- "presets": [
- ["env", {
- "modules": false,
- "targets": {
- "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
- }
- }],
- "stage-2"
- ],
- "plugins": ["transform-runtime", "external-helpers","transform-class-properties"]
- }
注: 上面的配置是 babel 7.0 以下的方式, 假如 babel7.0 以上, 用另外的方式配置, 参见: https://babeljs.io/docs/en/v7-migration
https://blog.zfanw.com/babel-js/
错误三
code: 'BAD_BUNDLE_TRANSFORMER', plugin: 'uglify'
这个问题比较坑爹, 其实我用 rollup 打包 demo 代码是没有问题的, 但是打包我的 JS 就有问题了, 好奇怪, 后来我发现是 swiper 的问题, 因为我依赖了 swiper. 关于 swiper 打包, 在 webpack 中也有问题, 通常 babel 打包之后, 并不会把 swiper 的 es6 语法转换. 有时候 webapck 也会报错, 大致是
dom7 undefined ..
webpack 解决方案如下:
- resolve: {
- extensions: ['.js', '.vue', '.json'],
- alias: {
- 'swiper': 'swiper/dist/js/swiper.js',
- '@': resolve('src')
- }
- },
指定一个别名
但是发现 rollup 中好像没有这个方式, 无奈, 我在引入 swiper 的时候如下处理
import Swiper from 'swiper/dist/js/swiper.js'
这样打包的时候就不会有问题了.
错误四
process not defined
这个错误是在打包成功之后, 浏览器运行发现的, 发现打包之后的代码中有 process.env.NODE_ENV
解决方案:
- import replace from 'rollup-plugin-replace'
- const env = process.env.NODE_ENV
- plugins: [
- replace({
- 'process.env.NODE_ENV': JSON.stringify(env)
- }),
- ]
把 process.env.NODE_ENV 这个替换掉
rollup 批量打包的方式
我们用 rollup 打包, 一般都会打如下方式
amd - 异步模块定义, 用于像 RequireJS 这样的模块加载器
cjs - CommonJS, 适用于 Node 和 Browserify/Webpack
es - 将软件包保存为 ES 模块文件
iife - 一个自动执行的功能, 适合作为 < script > 标签.(如果要为应用程序创建一个捆绑包, 您可能想要使用它, 因为它会使文件大小变小.)
umd - 通用模块定义, 以 amd,cjs 和 iife 为一体
我是用 node, 循环读取的方式, 配置如下:
- const babel = require('rollup-plugin-babel')
- const node = require('rollup-plugin-node-resolve')
- const commonjs = require('rollup-plugin-commonjs')
- const JSON = require('rollup-plugin-json')
- const replace = require('rollup-plugin-replace')
- const uglify = require('rollup-plugin-uglify')
- // 新增 rollup-plugin-postCSS 插件
- const postcss = require('rollup-plugin-postcss')
- // 新增 postcss plugins
- const simplevars = require('postcss-simple-vars')
- const nested = require('postcss-nested')
- const cssnext = require('postcss-cssnext')
- const cssnano = require('cssnano')
- const version = process.env.VERSION || require('../package.json').version
- const path = require('path')
- const fs = require('fs')
- const ora = require('ora')
- const terser = require('terser')
- const zlib = require('zlib')
- const spinner = ora('building for production...')
- spinner.start()
- const rollup = require('rollup')
- if (!fs.existsSync('dist')) {
- fs.mkdirSync('dist')
- }
- function resolve(dir) {
- return path.join(__dirname, '..', dir)
- }
- const banner =
- '/*!\n' +
- ` * haoroomsjssdk v${version}\n` +
- ` * (c) 2017-${new Date().getFullYear()}\n` +
- '* Released under the MIT License.\n' +
- '*/'
- // amd - 异步模块定义, 用于像 RequireJS 这样的模块加载器
- // cjs - CommonJS, 适用于 Node 和 Browserify/Webpack
- // es - 将软件包保存为 ES 模块文件
- // iife - 一个自动执行的功能, 适合作为 < script > 标签.(如果要为应用程序创建一个捆绑包, 您可能想要使用它, 因为它会使文件大小变小.)
- // umd - 通用模块定义, 以 amd,cjs 和 iife 为一体
- const buildArray = ['amd', 'cjs', 'iife', 'umd']
- let allConfig = []
- const env = process.env.NODE_ENV
- let baseConfig = {
- plugins: [
- postcss({
- extensions: ['.css'],
- plugins: [
- simplevars(),
- nested(),
- cssnext({ warnForDuplicates: false }),
- cssnano()
- ]
- }),
- node({
- jsnext: true, // 该属性是指定将 Node 包转换为 ES2015 模块
- // main 和 browser 属性将使插件决定将那些文件应用到 bundle 中
- main: true, // Default: true
- browser: true // Default: false
- }),
- JSON(),
- babel({
- exclude: 'node_modules/**', // 排除 node_modules 下的文件
- runtimeHelpers: true
- }),
- commonjs(),
- replace({
- 'process.env.NODE_ENV': JSON.stringify(env)
- }),
- (env === 'production' && uglify())
- ]
- }
- buildArray.forEach(item => {
- let outputs = {
- input: resolve('src/haorooms.js'),
- output: {
- file: resolve(`js/haorooms.${item}.min.js`),
- format: item,
- name: 'haorooms',
- banner
- }
- }
- allConfig.push(Object.assign({}, baseConfig, outputs))
- })
- function build (builds) {
- let built = 0
- const total = builds.length
- const next = () => {
- buildEntry(builds[built]).then(() => {
- built++
- if (built <total) {
- next()
- } else {
- spinner.stop()
- }
- }).catch(logError)
- }
- next()
- }
- function buildEntry (config) {
- const output = config.output
- const { file, banner } = output
- const isProd = /min\.JS$/.test(file)
- return rollup.rollup(config)
- .then(bundle => bundle.generate(output))
- .then(({ code }) => {
- if (isProd) {
- const minified = (banner ? banner + '\n' : '') + terser.minify(code, {
- output: {
- ascii_only: true
- },
- compress: {
- pure_funcs: ['makeMap']
- }
- }).code
- return write(file, minified, true)
- } else {
- return write(file, code)
- }
- })
- }
- function write (dest, code, zip) {
- return new Promise((resolve, reject) => {
- function report (extra) {
- console.log(blue(path.relative(process.cwd(), dest)) + '' + getSize(code) + (extra ||''))
- resolve()
- }
- fs.writeFile(dest, code, err => {
- if (err) return reject(err)
- if (zip) {
- zlib.gzip(code, (err, zipped) => {
- if (err) return reject(err)
- report('(gzipped:' + getSize(zipped) + ')')
- })
- } else {
- report()
- }
- })
- })
- }
- function getSize (code) {
- return (code.length / 1024).toFixed(2) + 'kb'
- }
- function logError (e) {
- console.log(e)
- }
- function blue (str) {
- return '\x1b[1m\x1b[34m' + str + '\x1b[39m\x1b[22m'
- }
- build(allConfig)
运行的时候直接如下:
cross-env NODE_ENV=production node 上面的文件名
备注: cross-env 可以指定环境变量等
另外一种方式是用 NPM run all
参见地址: https://www.npmjs.com/package/npm-run-all
可以用这个来运行多个 NPM 命令, 来达到运行一次, 打包所有的功能!
来源: https://juejin.im/post/5beae896f265da61682aebae