Rollup也是一款ESModule打包器,可以将项目中的细小模块打包成整块代码,使得划分的模块可以更好的运行在浏览器环境或者是Nodejs环境。Rollup与Webpack作用非常类似,不过Rollup更为小巧。webpack结合插件可以完成前端工程化的绝大多数工作,而Rollup仅仅是一款ESM打包器,没有其他功能,例如Rollup中并不支持类似HMR这种高级特性。Rollup并不是要与Webpack全面竞争,而是提供一个充分利用ESM各项特性的高效打包器。

一、rollup使用

./src/message.js

  1. export default {
  2. hi: 'Hey Guys, I am jal '
  3. }

./src/logger.js

  1. export const log = msg => {
  2. console.log('---Info----')
  3. console.log(msg)
  4. console.log('-----------')
  5. }
  6. export const error = msg => {
  7. console.error('---Error-----')
  8. console.error(mes)
  9. console.error('-------------')
  10. }

./src/index.js

  1. import {log} from './logger'
  2. import messages from './message'
  3. const msg = messages.hi
  4. log(msg)

安装Rollup:yarn add rollup --dev
运行:yarn rollup ./src/index.js --format iife --file dist/bundle.js
./dist/bundle.js

  1. (function () {
  2. 'use strict';
  3. const log = msg => {
  4. console.log('---Info----');
  5. console.log(msg);
  6. console.log('-----------');
  7. };
  8. var messages = {
  9. hi: 'Hey Guys, I am jal '
  10. };
  11. const msg = messages.hi;
  12. log(msg);
  13. }());

Rollup默认会开启TreeShaking优化输出结果。
配置文件:rollup.config.js

  1. export default {
  2. input: './src/index.js',
  3. output: {
  4. file: 'dist/bundle.js',
  5. format: 'iife'
  6. }
  7. }

运行:yarn rollup --config , 指定配置文件:yarn rollup --config rollup.config.js

二、rollup插件

Rollup自身的功能就是对ESM进行合并打包,如果需要更高级的功能,如加载其他类型资源模块、导入CommonJS模块、编译ES新特性等,Rollup支持使用插件的方式扩展实现,插件是Rollup唯一的扩展方式。
通过导入json文件学习如何使用Rollup插件。
安装插件rollup-plugin-json, 运行:yarn add rollup-plugin-json —dev

Rollup.config.js

  1. import json from 'rollup-plugin-json'
  2. export default {
  3. input: './src/index.js',
  4. output: {
  5. file: 'dist/bundle.js',
  6. format: 'iife'
  7. },
  8. plugins: [
  9. json()
  10. ]
  11. }

./src/index.js

  1. import {log} from './logger'
  2. import messages from './message'
  3. import {name, version} from '../package.json'
  4. const msg = messages.hi
  5. log(msg)
  6. log(name)
  7. log(version)

./dist/bundle.js

  1. (function () {
  2. 'use strict';
  3. const log = msg => {
  4. console.log('---Info----');
  5. console.log(msg);
  6. console.log('-----------');
  7. };
  8. var messages = {
  9. hi: 'Hey Guys, I am jal '
  10. };
  11. var name = "Rollup-test";
  12. var version = "1.0.0";
  13. const msg = messages.hi;
  14. log(msg);
  15. log(name);
  16. log(version);
  17. }());

json中用到的属性被打包进来了,没用到的属性被TreeShaking移除掉了。

三、rollup 加载npm依赖

Rollup不能像webpack那样通过模块名称加载npm模块,为了抹平差异,Rollup官方提供了一个插件rollup-plugin-node-resolve,通过这个插件,就可以在代码中使用模块名称导入模块。
安装插件:yarn add rollup-plugin-node-resolve —dev

Rollup.config.js

  1. import json from 'rollup-plugin-json'
  2. import resolve from 'rollup-plugin-node-resolve'
  3. export default {
  4. input: './src/index.js',
  5. output: {
  6. file: 'dist/bundle.js',
  7. format: 'iife'
  8. },
  9. plugins: [
  10. json(),
  11. resolve()
  12. ]
  13. }

./src/index.js

  1. import _ from 'lodash-es' // lodash模块的ESM版本
  2. import {log} from './logger'
  3. import messages from './message'
  4. import {name, version} from '../package.json'
  5. const msg = messages.hi
  6. log(msg)
  7. log(name)
  8. log(version)
  9. log(_.camelCase('hello world'))

四、Rollup加载CommonJS模块

安装插件: yarn add rollup-plugin-commonjs
Rollup.config.js

  1. import json from 'rollup-plugin-json'
  2. import resolve from 'rollup-plugin-node-resolve'
  3. import commonjs from 'rollup-plugin-commonjs'
  4. export default {
  5. input: './src/index.js',
  6. output: {
  7. file: 'dist/bundle.js',
  8. format: 'iife'
  9. },
  10. plugins: [
  11. json(),
  12. resolve(),
  13. commonjs()
  14. ]
  15. }

./src/index.js

  1. import _ from 'lodash-es' // lodash模块的ESM版本
  2. import {log} from './logger'
  3. import messages from './message'
  4. import {name, version} from '../package.json'
  5. import cjs from './cjs.module'
  6. const msg = messages.hi
  7. log(msg)
  8. log(name)
  9. log(version)
  10. log(_.camelCase('hello world'))
  11. log(cjs)

./dist/bundle.js

  1. // ...
  2. var cjs_module = {
  3. foo: 'bor'
  4. };
  5. // ...
  6. log(cjs_module);
  7. // ...

五、Rollup代码拆分 : 动态导入

使用动态导入的方式实现模块的按需加载,Rollup内部会自动去处理代码的拆分,也就是分包。
代码拆分的话要改成amd格式输出打包结果
Rollup.config.js

  1. import json from 'rollup-plugin-json'
  2. import resolve from 'rollup-plugin-node-resolve'
  3. import commonjs from 'rollup-plugin-commonjs'
  4. export default {
  5. input: './src/index.js',
  6. output: {
  7. // file: 'dist/bundle.js',
  8. // format: 'iife',
  9. dir: 'dist',
  10. format: 'amd'
  11. },
  12. plugins: [
  13. json(),
  14. resolve(),
  15. commonjs()
  16. ]
  17. }

./src/index.js

  1. import('./logger').then(({ log }) => {
  2. log('code splitting~')
  3. })

六、Rollup多入口打包

rollup.config.js

  1. import json from 'rollup-plugin-json'
  2. import resolve from 'rollup-plugin-node-resolve'
  3. import commonjs from 'rollup-plugin-commonjs'
  4. export default {
  5. // input: './src/index.js',
  6. // input: ['src/index.js', 'src/album.js'], // 多入口打包
  7. input: { // 这种写法也可以进行多入口打包
  8. foo: 'src/index.js',
  9. bar: 'src/album.js'
  10. },
  11. output: {
  12. // file: 'dist/bundle.js',
  13. // format: 'iife',
  14. dir: 'dist', // 动态导入时会分包成多文件
  15. format: 'amd' // 动态导入不支持iife
  16. },
  17. plugins: [
  18. json(),
  19. resolve(),
  20. commonjs()
  21. ]
  22. }

注意此时生成的js文件就要以AMD标准的require方式引入

  1. <script src="https://unpkg.com/requirejs@2.3.6/require.js" data-main="foo.js"></script>

Rollup、Webpack选用原则

(1) Rollup优势

  • 输出结果更加扁平,执行效率更高
  • 自动移除未引用的代码
  • 打包结果依然完全可读

(2) Rollup缺点

  • 加载非ESM的第三方模块比较复杂
  • 模块最终都会被打包到一个函数中,无法实现HMR
  • 浏览器环境中,代码拆分功能依赖AMD库
  • 如果我们正在开发应用程序,需要引入大量的第三方库,代码量又大,需要分包打包,Rollup的作用则会比较欠缺。

如果我们正在开发一个框架或者类库,Rollup的这些优点则非常有必要,缺点则可以忽略。所以大多数知名框架/库都在使用Rollup作为模块打包器。
总结:Webpack大而全,Rollup小而美。
选择标准:

  • 开发应用程序选择Webpack
  • 开发框架/库使用Rollup


Parcel

安装Parcel:yarn add parcel-bundler --dev
./src/index.html Parcel中的入口文件是HTML文件

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>Parcel Test</title>
  7. </head>
  8. <body>
  9. <script src="./main.js"></script>
  10. </body>
  11. </html>

./src/main.js

// import $ from 'jquery' // 自动安装依赖
import foo from './foo'
import './style.css'
import img from './1.png'
foo.bar()

// 动态导入,自动拆分模块
import('jquery').then($=>{
  $(document.body).append('<h1>Hello</h1>')
  $(document.body).append(`<img src="${img}" />`)
})

if(module.hot) {
  module.hot.accept(() => {
    console.log('hmr') // 模块热替换
  })
}

./src/foo.js

export default {
  bar: () => {
    console.log('foo jal111 ..')
  }
}

./src/style.css

body {
  background-color: pink;
}

./src/1.png
执行命令:yarn parcel src/index.html 会自动启动一个http服务,并且监听文件的变化,自动开启了模块热替换功能,依赖文件也是自动安装,整个过程都是零配置。
如何以生产模式进行打包:yarn parcel build src/index.html

对于相同体量的项目进行打包,Parcel会比Webpack快很多,因为在Parcel内部使用的是多进程同时去工作,充分发挥了多核CPU的性能,而Webpack中可以使用happypack插件实现这一点。
Parcel首个版本发布于2017年,当时Webpack使用上过于繁琐。Parcel真正意义上实现了完全零配置,而且Parcel构建速度更快。
而现在大多数项目还是使用Webpack作为打包器,可能是因为Webpack有更好的生态、Webpack越来越好用。