之前翻译过一篇文章,介绍了通过 ES2015 的解构赋值语法引入模块,可以让打包工具(browserify)最终编译出来的代码量最小化。

殊不知在 webpack 1.X 版本是无法利用该特性来避免引入冗余模块代码的,导致打出来的 bundle 文件大小难免略有臃肿。

今天则向大家介绍一个当红炸子鸡——Rollup.js,通过它可以让你的 bundle 最小化,有效减少文件请求大小——以至于连 vue 都迅速地转投它来打包模块。

Tree-shaking

在 Rollup 编译模块的过程中,通过 Tree-shacking 的方式来剔除各模块中最终未被引用到的方法,通过仅保留被调用到的代码块来缩减 bundle 的大小。

我们来看下官网的例子。

页面入口文件 main.js:

import { cube } from './maths.js';
console.log( cube( 5 ) ); // 125,即5的立方值

被引如的 math.js 模块如下:

// 注意这个方法在入口文件里没有被调用过
//最终会被 Rollup 剔除
export function square ( x ) {
return x * x;
} //入口文件需要调用到的求立方值的方法
export function cube ( x ) {
return x * x * x;
}

通过 Rollup 打包之后如下:

'use strict';

function cube ( x ) {
// rewrite this as `square( x ) * x`
// and see what happens!
return x * x * x;
} console.log( cube( 5 ) ); //

可以很明显地体会到 Tree-shaking 的作用 —— Math 模块里有个从未用到的 square 方法,咱们在 bundle 文件里把它消灭掉了。

另外 TS 会抽取引用到的模块内容,将它们置于同一个作用域下,进而直接用变量名就可以访问各个模块的接口;而不像 webpack 这样每个模块外还要包一层函数定义,再通过合并进去的 define/require 相互调用。

当然这种方法需要 ES2015 的解构赋值语法来配合,多亏了它,Rollup 才能有效地对模块内容进行可靠的静态分析。

使用方式

安装自然不用说,走 npm 的老套路:

npm i rollup

执行打包的方式也是简单到爆:

rollup src/main.js -o rel/bundle.js

这意味着将入口文件 src/main.js 打包为 rel/bundle.js 文件。

很多时候我们开发走的 ES2015 模块语法,但最终编译出来的模块希望它能走 commonjs 语法,只需要加上 -f cjs 运行时参数(f for format)即可:

rollup src/main.js -o rel/bundle.js -f cjs

当然,如果你想编译为其它格式,可以把 cjs 更换为:

amd /  es6 / iife / umd

我们分别来个参考~ 假设入口文件 src/main.js 如下:

var name = 'VaJoy';

function main () {
console.log(name);
} export default main;

编译为各种模式后的bundle:

//////////////////////////////commonjs(-f cjs)
'use strict'; var name = 'VaJoy'; function main () {
console.log(name);
} module.exports = main; //////////////////////////////AMD(-f amd)
define(function () { 'use strict'; var name = 'VaJoy'; function main () {
console.log(name);
} return main; }); //ES2015/ES6(-f es6)
var name = 'VaJoy'; function main () {
console.log(name);
} export default main; //////////////////////////////Global(-f iife)
//注意该方法需要通过配置文件形式来执行(见下一节)
var main = (function () {
'use strict'; var name = 'VaJoy'; function main () {
console.log(name);
} return main; }()); //////////////////////////////UMD(-f umd)
//注意该方法需要通过配置文件形式来执行(见下一节)
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.main = factory());
}(this, function () { 'use strict'; var name = 'VaJoy'; function main () {
console.log(name);
} return main; }));

配置文件

和 webpack 一样,rollup 也支持通过配置文件来实现更灵活的功能。

我们在项目根目录新建一个 rollup.config.js :

export default {
entry: 'src/main.js',
format: 'cjs',
dest: 'rel/bundle.js' // 输出文件
};

然后执行

rollup -c

即可通过默认配置文件(rollup.config.js)所设置的信息来进行打包。

如果你的配置文件另有其名(例如“rollup.config.dev.js”),在后面加上配置文件名即可:

rollup -c rollup.config.dev.js

Rollup 也支持使用插件,写到配置对象的 plugin 里即可,这里我们以 rollup-plugin-babel 为例:

import babel from 'rollup-plugin-babel';

export default {
entry: 'src/main.js',
format: 'cjs',
plugins: [ babel() ],
dest: 'rel/bundle.js'
};

比较不爽的是,babel 的预设不像 webpack 可以直接写在配置文件里,而还是得独立写个“src/.babelrc”(注意我们可以写在 src 下,而不是非得放在项目根目录下)

{
"presets": ["es2015-rollup"]
}

注意咱得确保安装了 rollup-plugin-babel 和 babel 预设 babel-preset-es2015-rollup

npm i rollup-plugin-babel babel-preset-es2015-rollup

这时候就能配合 babel 一起把 ES6 的模块编译为 ES5 的 bundle 了。

更多有趣的插件可以在rollup 项目组织里找,貌似没有 webpack 那样专门有个插件列表页汇总,这点找起来不太方便。

Rollup 也支持直接在模块中来被调用执行,这样很方便跟 grunt/gulp 等工具进行协作。

如我们修改 rollup.config.dev.js 内容为:

var rollup = require( 'rollup' );
var babel = require('rollup-plugin-babel'); rollup.rollup({
entry: 'src/main.js',
plugins: [ babel() ]
}).then( function ( bundle ) {
bundle.write({
format: 'umd',
moduleName: 'main', //umd或iife模式下,若入口文件含 export,必须加上该属性
dest: 'rel/bundle.js'
});
});

然后用 node 直接执行

node rollup.config.dev.js

可以得到一样的执行结果。

注意 “rollup.rollup()”返回一个带着 bundle 作为 resolve 回调参数的 Promise 对象,我们常规直接使用语法糖 bundle.write 来打包输出文件:

    bundle.write({
format: 'umd',
moduleName: 'main',
dest: 'rel/bundle.js'
});

其等价于

  var result = bundle.generate({ //生成一个 bundle + sourcemap
format: 'umd',
moduleName: 'main',
dest: 'rel/bundle.js',
}); fs.writeFileSync( 'rel/bundle.js', result.code );

SourceMap

为了方便调试编译后的文件,rollup 肯定不会忘记添加 source map 功能,而且其配置也非常简单:

    {
format: 'umd',
moduleName: 'main',
dest: 'rel/bundle.js',
sourceMap: true //加上这里即可
}

这样编译后,rollup 会自动生成一个 rel/bundle.js.map 关联到 rel/bundle.js 中。

也可以将其直接内联在 bundle 里而不是独立生成一个 map 文件:

    {
format: 'umd',
moduleName: 'main',
dest: 'rel/bundle.js',
sourceMap: 'inline'
}

若希望 map 文件可以自定义位置和名称,就得使用上面稍微提到的 bundle.generate 方法了:

  var result = bundle.generate({ //生成一个 bundle + sourcemap
//...
}); fs.writeFileSync( 'rel/bundle.js', result.code );
fs.writeFileSync( 'map/bundle.js.map', result.map.toString() );

issue

Rollup 虽然利用 ES6 的特性帮咱节省了不少文件大小,但它并没有类似 webpack 的 -p 参数帮你压缩混淆文件。

因此即使是官方文档也推荐配合使用 UglifyJS 来进一步缩小 bundle 体积。

另外 webpack2 已经出来好几款 beta 版本了,同样也加上了对 Tree-shaking 的支持,相信 webpack2 出来后,Rollup 的热度会大大消减。

共勉~

冗余代码都走开——前端模块打包利器 Rollup.js 入门的更多相关文章

  1. webpack前端模块打包器

    webpack前端模块打包器 学习网址: https://doc.webpack-china.org/concepts/ http://www.runoob.com/w3cnote/webpack-t ...

  2. 前端知识(一)04 Vue.js入门-谷粒学院

    目录 一.介绍 1.Vue.js 是什么 2.初识Vue.js 二.基本语法 1.基本数据渲染和指令 2.双向数据绑定 3.事件 4.修饰符 5.条件渲染 6.列表渲染 7.实例生命周期 一.介绍 1 ...

  3. 前端打包利器:webpack工具

    一.什么是WebPack,为什么要使用它? 1.为什要使用WebPack 现今的很多网页其实可以看做是功能丰富的应用,它们拥有着复杂的JavaScript代码和一大堆依赖包.为了简化开发的复杂度,前端 ...

  4. 一份关于webpack2和模块打包的新手指南

    webpack已成为现代Web开发中最重要的工具之一.它是一个用于JavaScript的模块打包工具,但是它也可以转换所有的前端资源,例如HTML和CSS,甚至是图片.它可以让你更好地控制应用程序所产 ...

  5. webpack前端模块加载工具

    最近在看许多React的资料,发现了大部分的项目都是用webpack行模块化管理的工具.这次也是借着写了一个React-Todos的小应用,对webPack最基本实用的功能体验了一番,顺带做个小记录. ...

  6. webpack前言:前端模块系统的演进

    前端开发和其他开发工作的主要区别,首先是前端是基于多语言.多层次的编码和组织工作,其次前端产品的交付是基于浏览器,这些资源是通过增量加载的方式运行到浏览器端,如何在开发环境组织好这些碎片化的代码和资源 ...

  7. 一份关于webpack2和模块打包的新手指南(一)

    webpack已成为现代Web开发中最重要的工具之一.它是一个用于JavaScript的模块打包工具,但是它也可以转换所有的前端资源,例如HTML和CSS,甚至是图片.它可以让你更好地控制应用程序所产 ...

  8. 异步 map 和模块打包

    概述 本文是我在查资料的时候学到的一些东西,记录下来,供以后开发时参考,相信对其他人也有用. 参考资料: 异步函数 - 提高 Promise 的易用性 深入 CommonJs 与 ES6 Module ...

  9. 探索 模块打包 exports和require 与 export和import 的用法和区别

    菜单快捷导航: CommonJS 之 exports和require用法 ES6 Module 之 export 和 import 用法 CommonJS和ES6 Module的区别 循环依赖 和 解 ...

随机推荐

  1. Database Replay和Consolidated Database replay

    简介 在数据库的迁移和升级场景中,我们经常会遇到一个问题:在做压力测试时,如何模拟真实的业务压力,解决这个问题的方法有很多,比如:应用方开发模拟程序或者使用压力测试工具模拟,如load runner, ...

  2. jQuery之ajax实现篇

    jQuery的ajax方法非常好用,这么好的东西,你想拥有一个属于自己的ajax么?接下来,我们来自己做一个简单的ajax吧. 实现功能 由于jq中的ajax方法是用了内置的deferred模块,是P ...

  3. 从0开始搭建SQL Server AlwaysOn 第二篇(配置故障转移集群)

    从0开始搭建SQL Server AlwaysOn 第二篇(配置故障转移集群) 第一篇http://www.cnblogs.com/lyhabc/p/4678330.html第二篇http://www ...

  4. 发布:.NET开发人员必备的可视化调试工具(你值的拥有)

    1:如何使用 1:点击下载:.NET可视化调试工具 (更新于2016-12-29 19:11:00) (终于彻底兼容了部分VS环境下无法使用的问题) 2:解压RAR后执行:CYQ.VisualierS ...

  5. 如何在高并发环境下设计出无锁的数据库操作(Java版本)

    一个在线2k的游戏,每秒钟并发都吓死人.传统的hibernate直接插库基本上是不可行的.我就一步步推导出一个无锁的数据库操作. 1. 并发中如何无锁. 一个很简单的思路,把并发转化成为单线程.Jav ...

  6. .NET平台开源项目速览(13)机器学习组件Accord.NET框架功能介绍

    Accord.NET Framework是在AForge.NET项目的基础上封装和进一步开发而来.因为AForge.NET更注重与一些底层和广度,而Accord.NET Framework更注重与机器 ...

  7. Android学习路线总结,绝对干货

    title: Android学习路线总结,绝对干货 tags: Android学习路线,Android学习资料,怎么学习android grammar_cjkRuby: true --- 一.前言 不 ...

  8. C#多线程之线程池篇3

    在上一篇C#多线程之线程池篇2中,我们主要学习了线程池和并行度以及如何实现取消选项的相关知识.在这一篇中,我们主要学习如何使用等待句柄和超时.使用计时器和使用BackgroundWorker组件的相关 ...

  9. DDD 领域驱动设计-两个实体的碰撞火花

    上一篇:<DDD 领域驱动设计-领域模型中的用户设计?> 开源地址:https://github.com/yuezhongxin/CNBlogs.Apply.Sample(代码已更新) 在 ...

  10. 异步 HttpContext.Current 为空null 另一种解决方法

    1.场景 在导入通讯录过程中,把导入的失败.成功的号码数进行统计,然后保存到session中,客户端通过轮询显示状态. 在实现过程中,使用的async调用方法,出现HttpContext.Curren ...