作者:wmaker
https://segmentfault.com/a/1190000017183743
前言
在前端项目的规模和复杂性不断提升的情况下,各类构建思想和相应工具层出不穷。本文竭己所能对比了当下13个构建工具,包括 Browserify
、 Webpack
、 Rollup
、 Grunt
、 Gulp
和 Yeoman
6个广为流行的工具, FIS
、 Athena
、 WeFlow
和 Cooking
等4个国产工具,以及三大框架: React
, Vue
和 Angular
的官方脚手架。希望能在项目初期的构建工具选型上为大家提供些参考。
全览
构建工具可以分为三类:模块化打包类、任务流构建类和集合型工具类(脚手架)。其中最为突出的,当属用于模块化打包的 Webpack
和用于任务流构建的 Gulp
。下面是截至2018年11月28日某时某刻, GitHub
上各个工具的 Star
数目(听说 Star
数目可以造假?好生无聊的家伙们!)。
前端的构建一般包括JS转码(使用 Babel
转 ES6
或 TypeScript
自转等)、CSS转码( Less
或 Sass
转 Css
)、代码或资源的合并与压缩,基础检查和各类测试等等。这些虽与本文关系密切,但都不在讨论的范围之内。原因有二:一是实现这些功能的都是某些插件,不是工具本身,各类构建工具都是直接或间接(调用以自己的模式封装后的插件)使用它们的;二是本文介绍的是,构建方向上的类别和各类别里不同工具间的差异,与具体的操作无关。
模块化打包类
现在的前端项目基本是模块化的,原因就不在这多说。而模块化意味着分散,无法直接用于呈现,因此需要进行相应的打包形成一个整体。有些执行环境( Node
)能自动打包各个模块,而有些(浏览器)则因为技术或其它考虑需要自行操作。模块化打包工具就是为模块化项目在浏览器上的优化呈现而服务的。
模块化打包的核心是:找出依赖,打包成体。各类工具的基本运行思路便是根据已有配置,从某个文件开始,递归的找出所有与其相关的依赖模块,打包成某种类型的可直接在浏览器中运行的一个或多个新文件。这之中还可以优化输出,以实现代码分离、异步加载和长效缓存等高级功能。
Browserify
官网 | GitHub
正如其官网介绍的, Browserify
会递归的分析,项目中所有使用 require
引入的JS模块(包括 Node
内置模块)并打包,使得 Node
类项目能在浏览器上运行。不过对于与项目有关的其它资源,比如 Css
和图片等,依然需要手动管理。虽然网上已有人编写了支持此些功能的插件,但这不仅违背了设计初衷,也使配置变得杂乱。而且对于要求越来越高的单页面应用来说,它能提供的助力着实已显疲惫。
Webpack
官网 | 中文 | GitHub
稳定版已到 v4.26.0
,本文以此版本为据。另附加官方的对比文档。
Webpack
的设计思想新颖实用,社区活跃,功能强大全面,已经是针对前端各类资源的、目前最优秀的模块化管理和打包工具。它入门简单,基本的常用功能能很快上手,并用于实际开发。但精通不易,毕竟打包已是 web
开发中最重要的挑战之一,必然要耗费些许精力。学习尚且不易,介绍就更为困难,得要有一本书的厚度。所幸此节不是详细介绍,只是亮点阐述,善哉善哉。
入门已趋简单
掌握了构建的基本思路,任意工具的入门都是较为简单的(读者批:废话)。之所以强调 Webpack
入门简单,是为了减轻有意者学习之前的顾虑。一方面是它刚被推出时,由于自身的概念新颖而且文档不全面,使开发者处于懵懵懂懂的状态,总感觉离真谛还差些距离。另一方面是它的体系着实庞大,仔细想想都不免胆怯。笔者初次接触时便是这些个感受。
但现在不一样。吃土的日子已经远去,啃草的梦想还会远吗?大家准备好镰刀!
Webpack
第四版在入门上的方便性体现在三方面。一是基础功能高度集成和约定优于配置思想:安装好 Webpack
及其 CLI
后便可直接打包 JS
和 JSON
文件,与 Browserify
一样简单。二是官方文档详细(而且有基本同步的中文版),无论是概念的解析、实际运用的示例还是接口的展示都十分完备。三是现在使用和介绍 Webpack
的人已经很多了,因此网上的各路资料和相应问题的解决方案都十分丰富。你还在犹豫?
一切皆模块
如从官网上截取的图片所示,在 Webapck
眼中一切文件( .js
、 .css
、 .jpg
、 .woff
、 .csv
和 .ts
等除了某些用于下载的静态大文件外)都是模块,都能通过与 JS
相似的方式被打包,并安置于合适浏览器渲染的位置。真是十分优秀的立足点。以此思想便可囊括前端会使用到的几乎所有资源,可以十分方便的统一管理和安置,更为便捷和高效。
而且此思想就是为单页面应用而生的。在 Webpack
的另一层意境中,一个 asset
(各类资源)是一个模块,一个 component
是一个模块,一个 module
也是一个模块。而单页面应用的特点,不就是应用的更新不是整个页面的更新,而是某个 module
或 component
或 asset
的更新吗?十分的契合。
有人说 Webpack
的缺点在服务端渲染(或说多页面应用)上。喂喂,一来别人的目标本就不在此,二是多页面应用也不需要如此复杂的支持体系。
高效的构建性能
单页面应用或说需要构建才能展示的应用,相比多页面应用,从每次修改到重新呈现要多经历一个构建的阶段。实际操作中,如果项目庞大而构建性能不够优化,一个小小的修改(打印某值)都会消耗5秒以上的时间,对开发者来说真是个地狱!而优化的方法不外乎两点,一是开发者优化项目的构建思路,二是构建工具优化自身的构建性能。
Webpack
拥有较理想的构建性能。在开发阶段,当开启了 Webpack
的模块热替换之后(使用 webpack-dev-server
会自动开启),一旦检测到文件被修改,会在应用程序运行过程中通过冒泡捕获的方式最小化替换、添加或删除模块,而无需重新加载整个页面。类似 Dom
渲染中的回流:如果子元素发生的大小变化,会影响兄弟元素但不影响父元素,那么父元素及其它是无需重新绘制的。而且即便完全重新构建,也会保留先前的应用程序状态,减少等待时间。
活跃的社区
活跃的社区可以提升系统的丰富度,降低学习与使用的成本。
Webapck
社区十分活跃,应用于各种需求的插件都被一一封装而可直接使用(官方也统一展示和说明了一些常用的优秀的 Loader
和 Plugin
)。不单单是其它工具的高度协调,开发中的各个阶段:搭建本地服务器、集成测试等,以及与任务流工具( Gulp
、 Grunt
)的集成等等方面的解决或最优方案,都是丰富和全面的。基本上可以想到的需求,在这个社区中,都能直接借鉴他人已有的成果。
Rollup
官网 | 中文 | GitHub
Rollup
定位为一个 JS
模块打包器(明指 JS
),主要用来构建 JS
库,也可服务于一些无需代码拆分和动态导入的小型应用程序。能在 Webpack
已稳居打包之首的情况下杀出一条血路,得到 Vue
、 D3
、 Three
和 React
等著名库的青睐,想必其着手点和性能有过人之处。
Rollup
本身结构简单,需要的配置项也不多,再加文档全面,所以很容易上手并全部掌握。它使用 ES6
本身的 Module
语法作为自己的标准,而不是诸如 CommonJS
和 AMD
等以前的解决方案。这意味着按照 Module
标准编成的代码,不仅现在可以借助 Rollup
打包运行,未来更能在实现此标准的浏览器上直接运行。
通过 Module
的特性, Rollup
开创了 Tree-shaking
功能——清除没有在项目中使用到的代码。它基于显式的 import
和 export
语句的方式,通过静态分析,排除了任何未在实际中使用的代码,能极大的减少构建于已有模块的项目体积。再加上其构建基本不添加自身的控制代码,使打包后的文件真正的达到纯净二字。想想还有点痒痒,我挠挠裆部。
与 Webpack 对比
Rollup
和 Webpack
因其定位和专注点是可以共同存在并相互支持的。
正如 Rollup
官网所说的, Rollup
更适合构建独立的 JS
库,而 Webpack
为资源丰富的应用程序。虽然 Webpack
也增加了自己的 Tree-shaking
功能,但在编译后的输出代码中,简单地运行自动 minifier
检测未使用的变量,得到的结果是不如原生的静态分析。更何况 Webpack
生成的代码一定是经过自己包装后的代码——将每个模块封装在一个函数中,再置于一个包中,通过浏览器能使用的 require
方式逐一执行这些模块。
任务流构建类
基于任务的构建行为,是不在乎操作对象是否为模块化的。
这类工具的目标是通过配置来解放日常需要重复的工作——转化、合并压缩和单元测试等等。有人说:这些操作 Webpack
和 Rollup
不是也能做?是的,基本能做。实际上,在用模块化构建工具的开发中,很少会用到任务流构建工具。但这绝不是说任务流工具会被取代,也不会被取代,至少多页面应用需要。再说任务流工具是十分纯粹的自动化行为,与模块化打包工具立足点就不一样,何谈取代一说。
Grunt
官网 | 中文 | GitHub
Grunt
虽是老牌构建工具,但依然被许多知名项目如 WordPress
、 Twitter
和 Jquery
等使用,也拥有持续更新的完整生态圈和中文文档。它是通过配置驱动——通过获取到的 JSON
配置执行操作,来流水线式执行相应任务。虽然在学习成本和执行效率上不出众,但如果项目原本就是通过它自动化构建的,是没有必要迁移到其它工具的。
// Grunt 的配置驱动示例
module.exports = function(grunt) {
grunt.initConfig({
jshint: {
files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
options: {
globals: {
jQuery: true
}
}
},
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint']
}
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', ['jshint']);
};
Gulp
官网 | 中文 | GitHub
Gulp
是新型的构建工具,虽与 Grunt
的功能相同,但其构建过程却有三大优势。
代码驱动
代码驱动即通过执行实际代码驱动程序执行,与常见的配置驱动不同( Webpack
、 Rollup
和 Grunt
等都是配置驱动)。从任务流构建的角度上看,代码驱动相比配置驱动有三点好处:一是高度的灵活;二是没有过多的配置项,减少学习成本;三是更方便错误的断定和异常情况的调试。
// Gulp 的代码驱动示例
gulp.src('./client/templates/*.jade')
.pipe(jade())
.pipe(gulp.dest('./build/templates'))
.pipe(minify())
.pipe(gulp.dest('./build/minified_templates'));
Node流
Gulp
作为后来者,充分利用 NodeJS
流的思想进行 IO
操作,极大增加了大型项目的构建速度。比方说转化 Scss
成 Css
, Grunt
的操作流程是:读取 Scss
文件、转化成 Css
、存储到磁盘,读取 Css
、压缩处理最后存储到磁盘;而 Gulp
得操作是:读取 Scss
文件、转化成 Css
、压缩处理最后存储到磁盘。一步到位,无需多次的 IO
操作。
简单明了
Gulp
有十分精简的 API
。你能想到各种类型的任务,基本是通过仅有的五个可链式操作的方法实现的吗?不仅仅是学习和使用方便,编写后的功能也是一目了然。虽然代码驱动相比配置驱动,需要自己写的代码增加,但一是没增加难度只是函数名的多次重写,二是相对代码驱动的好处来说可以忽略。
集合型工具类
集合型工具类便是常说的脚手架,也可以看作是以模块化或任务流工具为主体的,各类常用工具的高度封装。它是一个开箱即可用的集合体,类似前后端同构时代的后端框架。它会根据你的选择,生成一个完整的、已配置好各类工具的、具有某些特定代码约定的项目框架。这些配置几乎包揽前端开发的整个流程,甚至可以集成自动化部署等后端接口。
官方框架
React CLI | Vue CLI | Angular CLI
集合型工具一般为单页面应用服务,而单页面应用需要使用某个前端框架。无论你是用 React
、 Vue
或 Angular
,还是其它框架,首先得想到它是否有官方脚手架。比如 Vue
有 VueCLI
。一般推荐有官方脚手架的直接使用官方的。因为现代前端框架一般不单独运行,需结合官方提供的其它工具,比如路由、状态管理等。而且各个框架及配件更新不断,每次更新都可能导致与其它插件的兼容问题,新的功能可能需要某些特定插件才能发挥作用。这是一项工程,仅靠个人或某些团体很难照顾周全的。而各个框架又都有意识的通过官方脚手架来充分展示新的特性,降低学习和使用的成本。我们何乐而不为呢?
Yeoman
官网 | GitHub
Yeoman
是一个专为现代前端而生的、灵活通用的脚手架工具。
它的运作方式和其它脚手架不同。在安装好 CLI
后,需要找到一个符合要求的 Generator
(一个 npm
包,相当于脚手架),使用 Yeoman
运行安装,生成初始化的项目。你也可以自行配置,使用 Yeoman
封装成符合特定需求的 Generator
,并发布出去。等到下次,其他人或你自己,需要生成符合此要求的项目时,便可以直接安装并使用 Yeoman
生成。
这样有明显的两点好处:一是节省体力。在开始一个有特定需求的新项目时,如果有老项目可借鉴,一般会直接复制相关文件。但这样的复制文件可能不纯粹,即增加体积又带来安全隐患。二是在社区的支持下,很多有特殊要求的脚手架,早已有人解决并发布成 Generator
,是没必要自己动手的。
国内其它
百度 - FIS - 官网 | GitHub
微信 - WeFlow - 官网 | GitHub
京东 - Athena - 官网 | GitHub
饿了么 - Cooking(名字与公司的性质相得益彰) - 官网 | GitHub
作为程序员或至各行各业,在与年龄增长速度相当的压力下,工资的高低自然成为日常性的评定标准。但在同行老友的酒桌上或某个太阳异常温煦下的小道上,能使自己为自己而不是其他事骄傲的,也肯定是“老子之前做过些什么”之类的实际付出而不是物质方面的获得。因此能够成为被公司支持的、被众多人使用的、开源框架维护团队中的程序员,多少是更为幸福的一类。
这些由国内各个前端团队开发的集合型脚手架,都是基于自用在实践中得到的最为符合本身需求的产品。里面的包含内容十分丰富,不仅仅是这以上提到的前端本职工作,还有与后端的集成方案或自动化部署配置等。且流程简化,开箱即可使用。不过这些笔者都没用过,也没有打算用。不是打趣,原因很现实,有识之士可以在文章下留言。不用却依然写出的原因倒是简单:宣传,宣传即赞许和期盼;凑数,凑到13种好立个多少浮夸的标题。
总结
个人观点,不喜请喷,但要和蔼可亲。
如果是使用某个前端框架开发应用程序,推荐框架官方的脚手架。如果是自己头脑发热想开源个 JS
库,推荐 Rollup
打包。如果不是模块化项目,又需要自动化处理一些事情,推荐 Gulp
作为构建工具。如果项目有特殊要求或作为核心的部件比较稀有,可以先查看 Yeoman
上是否有符合要求的 Generator
,没有就只能自食其力。最后如果你处在已有自己脚手架的公司(比如饿了么),可能要按规章制度使用 Cooking
为自己的仕途烹煮些吃食。肚子真饿,这种宣传饿了么会返优惠券吗?
最后,如果是自食其力的搭建前人没有的脚手架,推荐使用 Yeoman
发布,方便你我他。