rollup打包js的注意点

24051次浏览

前言

rollup比较适合打包js的sdk或者封装的框架等,例如,vue源码就是rollup打包的。webpack比较适合打包一些应用,例如SPA或者同构项目等等。最近我们对rollup小试牛刀了一下。简单分享一些注意事项吧。

rollup基础

rollup基础知识及插件的一些使用,网上有不少资料,可以去查阅。

rollup中文网:https://www.rollupjs.com/guide/zh#-danger-zone-

问题记录吧

下面主要记录一下rollup使用过程中的一些报错及解决方案吧。

错误一:

You must supply 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 命令,来达到运行一次,打包所有的功能!

Tags: rollupjs

相关文章: