rollup打包js的注意點
前言
rollup比較適合打包js的sdk或者封裝的框架等,例如,vue原始碼就是rollup打包的。webpack比較適合打包一些應用,例如SPA或者同構專案等等。最近我們對rollup小試牛刀了一下。簡單分享一些注意事項吧。
rollup基礎
rollup基礎知識及外掛的一些使用,網上有不少資料,可以去查閱。
rollup中文網:ofollow,noindex" target="_blank">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 命令,來達到執行一次,打包所有的功能!