Press "Enter" to skip to content

JavaScript构建工具的选择

本文略译自《Choosing a JavaScript Build Tool – Babel, Browserify, Webpack, Grunt and Gulp

当我们开启一支新的JavaScript项目时,首要做的一件事之一,就是为项目建立一个构建系统(build system)。然而,由于市面有太多的选择,如果没有经验,针对某个项目构建的特性而「挑选工具」的任务本身,成了一项“开发任务”。

项目构建与源码编译

软件成品的制作可能需要多种原材料,源码(及其编译)只是其中最大的一部分而已,还可能会有多种数据资源;另外,为保证质量,源码(及其编译)也会多种处理,例如语法检测,功能测试等。软件成品的制作的所有任务为「项目构建」。

想像一下,如果存有一种简易的规则,我们遵循这些规则能正确的选择构建工具,无需耗费挑选时间,直接进入开发阶段,这不是很棒?事实上,我有过五年的使用不同自动化构建系统的经验,我掌握这样的简易规则。我知道在什么的情况下使用哪一种构建工具,读完这篇文章,你也可以。

构建工具选择规则

根据「项目规模」和「构建任务」性质:

  • 小型项目不需太多构建工具,只需要必选的 ES6编译器
  • 单页应用(Single Page Apps)可能因为规模较大(结构复杂)需要模块打包器( module bundler)
  • 剩下的软件产品的数据处理、部署或发布的任务则需一通用的自动化任务工具(task runner )

同一个「构建任务」可能还有多个工具的选择,以上的任务对应的具体工具包:

  • ES6+代码编译和前向模拟(polyfilling),使用Babel
  • 将你的JS文件和依赖打包成静态资源(static assets),使用Webpack
  • 剩下的诸如清除缓存(avoid caching),web发布等自动化构建任务,使用Gulp

下面解释选择这些工具的原由或根据。

工具特性与选择理由

原由或根据很简单,是基于这些构建工具的特性,包括优点和不足。我们先从必选的ES6编译器开始说。

EM:同一任务,不同的工具,那么不同工具间一定有优劣点,有固有的特性,如果新工具覆盖了旧技术优点,克服了缺点,旧技术一定会被淘汰

Compilers

ES5版 JavaScript被广泛浏览器支持但并不是完美的语言。ES6 和 ES7版本加了很多被赞誉的新语言特性,可惜没用,如果浏览器的支持不跟进的话。

幸好的是,大量知名企业的工程师联合起来开发了一种编译器,可以将新版本JS程序转译成为旧版!有更甚的是,超出新版JS( 例如Microsoft’s TypeScript)的功能也可被编译。如果我们只需要使用新版JS,那么我们有一极佳的选择,就是Babel。

Babel

如果读完本文,你得选一支工具必须学的话,它一定是Babel。

Babel 没有什么神奇的,它就是个转换工具。不过,Babel最让人兴奋的特色,就是它让你自己「定制转换方式」(以插件的形式),毕竟转译设计空间很大,没有一种固定标准。社区已经创建了很多现成的插件。举个特例,Babel有ES7转译插件,包括async/await和decorators等ES7特性。它也有React JSX插件。

然而,Babel只专注于代码转译,它不能完成「Web构建的其它任务」。例如,它不会打包ES6模块,这就是为什么你的构建任务还需module bundler的原因。

Module bundlers

无论代码规模怎样,大多数项目都会在多个文件之间拆分其代码(和数据),这些“模块”需要手动维护。显然计算机可以做得更好,这就是为什么你应该使用工具,自动将所有模块文件打包到单个文件中的原因。「打包」是因为被拆开了

虽然互联网催生了一系列令人困惑的模块打包工具,但有两个工具主导了整个格局:Browserify和Webpack。

Browserify

Browserify 的目标,是将Node的npm上的代码库(或者叫包)“移植”到浏览器端。虽然,Browserify也能将一个前端app包装成Node 程序,这个不常见。

EM:因为Node的源码仓库有很多通用代码,“移植”这个想法其实是很自然的事。浏览器没有内置require,我总可以事前在构建里(build)预处理吧。

Node的哲学(创建一个中心库)对于协作是很有好处的;使用Browserify打包(把依赖的代码预先处理进主程序)Web app是很方便的,你可以使用现成的Node的包,也可以像写给Node的模块一样(使用CJS格式),给浏览器写通用模块。当然,有一点不足的是,Web app的组成有多种数据资源,而Node app一般不需要,例如CSS,图片或字体等。

像很多前端构建工具一样,Browserify也尝试提供插件功能,克服不足。通过安装插件,Browserify可以:

  • transform ES6 to ES5
  • bundle CSS
  • split your bundle into multiple files

然而,Browserify的主要设计为针对移植Node 包,和为浏览器写CJS模块,以上这些额外的插件功能使Browserify的使用很不自然,配置很繁琐。

如果你需要编写单页面程序(Single Page App),你可能需要选用专门设计来「打包Web app 各种资源」的工具,例如webpack。

Webpack

Webpack 和 Browserify 有都有打包模块功能,然而Webpack的打包功能更全面。Webpack 可将「有依赖关系的JS模块」打包在一起,并且Webpack没规定(模块之间)依赖是什么样的依赖,只要是JS模块就行。

EM:也就是,有了Webpack,你可像编写Node app 一样,编写模块化web app。

“只要是JS模块”意味着不包括CSS和图片资源吗?不完全是这个意思。“JS模块”更确切的说,是Web模块。Webpack(通loader扩展)可以“打包”web app任何资源/文件。

EM:初学者可能对打包非JS源码模块难以理解。模块设计分「拆分」与「打包」两步。模块一般的概念是指代码行为的模块,但是可以泛化为程序任何组成,包括行为的数据,例如图片资源。模块、拆分和打包具体是什么取决模块是什么,怎么处理。例如,拆分的形式表现是模块使用了import或require等指令(非必要,这取决的打包过程,例如浏览器可视两个script标签为模块),图片资源是一种数据模块,「拆分」只是使用import指令加上保存图片资源在指定位,打包只是将图片变量替换来实际URI就完了。看一下官方对Loader的解释:

Loaders can transform files from a different language (like TypeScript) to JavaScript or inline images as data URLs.

Loaders的一般意义是说,它是针对特定资源文件的转换功能(打包功能),例如默认的.js文件,.css文件等都有相应的打包Loaders,读懂模块关的依赖关系,然后进行打包。另外,loader还支持分步链式(chainable)处理,例如你可先将.scss文件打包成css,再打包成JS模块,最后将所有的“模块”,打成一个bundle。

webpack提供的全面的打包功能,近乎完美(的让开发者像编写JAVA那样写JS程序),如果问Webpack有什么缺点,社区提供很多流行的项目模板(boilerplate projects )都有复杂的配置文件,这可能可吓到很多人。不过,别担心,我写了一个指南,只需要26行代码就可以配置好Webpack 和 Babel

现在你已经知道怎样使用Module bundlers 将各种「资源模块」打包成浏览器适用的资源文件,例如bundle.js,app.css……那接下来怎么处理这些模块文件?项目构建的自动化任务(task runners )进入视野。

Task runners

什么叫自动化任务(Task runners)工具?什么样的任务(tasks)?其实定义很简单,「任务」与执行任务的人一一对应,人的任何作业都可以称为任务。而本文的「自动化任务」特定由项目构建者执行日常开发构建任务——通过命令行执行软件构建的任务——要么发布产品,要么生成测试版进行测试。

不分清任务与工作者一对一,开发者、项构建者和Webpack(bundler)的关系会让人困惑。例如,我们完成可以(作为开发者)手动打包JS模块,由于这个任务非常的麻烦,所以我们通过使用 Webpack(安装和配置,然后执行CLI)代劳,然而,构建者还是要手动调用Webpack,调用Webpack本身也是一种任务(构建的一步)。

其它构建任务包括例如,在HTML中插入你的资源的 <script>标签,发布新构建好的新版本到服务器等。

以下是常见的web项目构建任务:

  • generating HTML from templates and content files
  • compressing new and modified images
  • compiling Sass to CSS code
  • removing console and debugger statements from scripts
  • transpiling ES6 to cross-browser-compatible ES5 code
  • code linting and validation
  • concatenating and minifying CSS and JavaScript files
  • deploying files to development, staging and production servers

读到这里,你可能会问,任务自动化工具?我一直在用GNU Make啊!很好,如果你已经有顺手的工具,一点毛病没有。但是如果你还没有特别喜爱的工具,那么很值得学一种其他JS开发者都在使用的自动化工具。例如,Grunt 或 Gulp。

Grunt

Grunt 是最早的JS项目构建自动化工具,当你第一次安装的后你会发现,Grunt居然不会执行任务,因为Grunt只是一个用JS写的执行器,具体任务需另外定义——通过配置对象(configuration objects 一个JS对象),一种类似HTML/CSS的声明的编程范式。

这是一种常见的软件设计,Grunt 设计得尽可能的小,具体功能通过插件扩展——从文件监控、拷贝,至合并文件,都是插件。

NPM库有数千个Grunt插件可用,所以你几乎不用自己编写插件,总找到一个适用的。不过问题最大是,如果你真的找不到合适插件,你不能用JS代码代替,你必须自己编写Grunt插件。然而,为项目编写和维护Grunt插件不是件愉快的事。

Gulp

和Grunt一样,Gulp也是JS自动化工具。Grunt 和 Gulp 最大的不同,是Grunt是声明式编程,而Gulp是命令式的——用JS函数来定义构建任务。Gulp的技术核心是用 streams 和 promises来表征 task,独立且细粒度的streams可以让你具有很大灵活性来编写你的task。

和Grunt一样,Gulp也一个丰富的插件库可用。由于你可以用JS来写构建任务,你会发现完全可以在你的构建任务中插入node模块来执行一些中间处理。

Gulp使用的最大问题在于,掌握 streams 和 promises 需要一些时间,当然这些时间花的是值得的,因为当你熟练之后,你可以更有针对性更高效的编写构建任务。

参考

  • https://www.sitepoint.com/introduction-gulp-js/
  • https://www.sitepoint.com/webpack-beginner-guide/
  • Webpack: When To Use And Why
  • https://github.com/baixing/FE-Blog/issues/7
  • https://github.com/craigbuckler/gulp-tutorial
  • webpack中图片打包: https://www.jianshu.com/p/8025178d246b
  • https://www.ibm.com/developerworks/cn/web/wa-using-gulp-to-build-lightweight-frontend-environment/index.html 使用 Gulp 搭建轻量级前端开发环境
  • https://julienrenaux.fr/2014/05/25/introduction-to-gulp-js-with-practical-examples/
  • https://www.sitepoint.com/automate-css-tasks-gulp/
  • 前端构建大法 Gulp 系列 (三):gulp的4个API 让你成为gulp专家

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *