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