在之前,人们习惯将脚本写在一起。 但时代已经改变,现在将JavaScript代码分开来可能是一个复杂的工作。 随着单页应用程序(SPA)的兴起,这个问题已经升级。他们倾向于依靠一些有用的系统(来解决这个问题)
出于这个原因,有多种策略来加载它们。您可以立即加载它们,或者考虑需要它们时加载。Webpack支持许多这样的策略。
Node和npm的流行,给它的包管理器提供了更多的使用环境。在npm普及之前,很难使用依赖项。有一段时间,人们开发出了前端特定的包管理器,但npm最终赢得了胜利。现在依赖管理比以前更容易了,尽管还需要克服一些挑战。
任务运行程序与打包
历史上,已经有很多构建工具。 Make可能是最着名的,它仍然是一个可行的选择。 专门的任务运行程序,如Grunt和Gulp,是专门为JavaScript开发人员创建的。 通过npm提供的插件使得任务运行程序都强大而且可扩展。 甚至可以使用npm脚本作为任务运行程序。 这很常见,特别是webpack。
任务运行程序是高水平的伟大工具。 它们允许您以跨平台方式执行操作。 当您需要将各种资源拼接在一起并生产时,问题就会开始。 出于此原因,存在资源整合程序,如Browserify,Brunch或webpack。
有一段时间,‘RequireJS’很受欢迎。 它的核心是提供一个异步模块的方法并建立在此之上。 AMD的格式在后面将会有更详细的介绍。 幸运的是,这些标准已经赶上了,而且RequireJS似乎是一个很好的启发。
Make
就像1977年最初发布的那样,‘Make’回来了。尽管它是一个旧工具,但它仍然是相关的。 Make允许您为各种目的编写单独的任务。 例如,您可以有不同的任务来创建生产构建,压缩JavaScript或运行测试。 您可以在许多其他工具中找到相同的方法。
尽管Make主要用于C项目,但它并不以任何方式与C绑定。 James Coglan详细讨论了‘如何在JavaScript中使用Mark’。 看一下下面的詹姆斯帖子里介绍的压缩代码的方法:
Makefile
1 | PATH := node_modules/.bin:$(PATH) |
使用Make,您可以使用Make-specific语法和终端命令为您的任务建模,使其可以与webpack集成。
RequireJS
‘RequireJS’可能是第一个成为真正受欢迎的脚本加载程序。 它首先正确地引入了模块化JavaScript。 其最大的吸引力是AMD。 它引入了一个定义包装器:
1 | define(['./MyModule.js'], function (MyModule) { |
顺便说一下,可以在包装器中使用require:1
2
3
4
5define(['require'], function (require) {
var MyModule = require('./MyModule.js');
return function() {...};
});
后一种方法更简洁一点。 但您仍然会遇到多余的代码。 ES6等标准解决了这个问题。
Jamund Ferguson撰写了一篇关于‘如何从RequireJS移植到webpack’的优秀博客系列。
之前有写过关于Require的博客‘RequireJS的使用’
npm脚本作为自动化构建工具
即使npm CLI(命令行界面)并非主要用于作为任务运行的程序,由于有package.json的脚本字段是之成为可能。 考虑下面的例子:
package.json
1 | "scripts": { |
这些脚本可以使用npm run列出,然后使用npm run script执行。 您还可以使用诸如test:watch这样的约定命名空间。 这种方法可以使它保持跨平台。
取代使用rm -rf,您可能更希望使用诸如rimraf等实用程序。 在这里可以调用其他自动化构建工具来隐藏你正在使用的具体细节。 这样,您可以在保持界面相同的情况下使用重构工具。
Grunt
‘Grunt’在前端开发人员中是最受欢迎的。它的插件架构有助于它的流行,插件本身通常是复杂的,因此,当配置增加时,很难理解到底发生了什么。
以下是‘Grunt文档的示例’。 在此配置中,您定义一个linting和一个观察任务。 当watch任务运行时,它也会触发lint任务。 这样,当您运行Grunt时,您可以在编辑源代码时在终端中实时发出警告。
Gruntfile.js
1 | module.exports = (grunt) => { |
在实践中,您将有许多小的任务用于特定目的,例如构建项目。 Grunt有用的一个重要部分是它隐藏了大量的细节。
从远来说,这可能会有问题。从Grunt的构建过程,你很难理解它引擎工作的具体情况。
‘grunt-webpack’插件允许您在Grunt环境中使用webpack,同时将使用等级提升到Webpack。
Gulp
‘Gulp’采取不同的方法。 您不需要依赖每个插件的配置,而是处理实际的代码。 Gulp建立在管道概念之上。 如果你熟悉Unix,这里也是一样的。 您需要遵循以下概念:
- 来源匹配文件。
- 对来源执行操作的过滤器(例如,转换为JavaScript)
- 接收模块库(例如,您的构建目录)在哪里管理构建结果。
这是一个示例的Gulpfile,可以让您更好地了解从项目的README中获取的方法。 它被缩写为一个接口:
Gulpfile.js1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47const gulp = require('gulp');
const coffee = require('gulp-coffee');
const concat = require('gulp-concat');
const uglify = require('gulp-uglify');
const sourcemaps = require('gulp-sourcemaps');
const del = require('del');
const paths = {
scripts: ['client/js/**/*.coffee', '!client/external/**/*.coffee']
};
// 并不是所有的任务都需要使用流
// 一个gulpfile是另一个节点程序
// 你也可以在npm上使用所有的软件包
gulp.task(
'clean',
del.bind(null, ['build']
);
gulp.task(
'scripts',
['clean'],
() => (
// 压缩和复制所有的JavaScript(除了供应商脚本)
// 源代码一路下来
gulp.src(paths.scripts)
// 管道内
.pipe(sourcemaps.init())
.pipe(coffee())
.pipe(uglify())
.pipe(concat('all.min.js'))
.pipe(sourcemaps.write())
.pipe(gulp.dest('build/js'))
)
);
// 文件更改时重新运行任务
gulp.task(
'watch',
gulp.watch.bind(null, paths.scripts, ['scripts'])
);
// 默认任务(从CLI运行`gulp`时调用)
gulp.task(
'default',
['watch', 'scripts']
);
鉴于配置是代码,如果遇到麻烦,您总是可以将其删除。 您可以将现有的节点包作为Gulp插件,等等。 与Grunt相比,您可以更清楚地了解发生了什么。 尽管如此,你仍然最终写了很多模板作为闲时任务。 那就是更新的方法。
‘webpack-stream’允许您在Gulp环境中使用webpack。
‘Fly’是与Gulp类似的工具。 它依赖于ES6发生器。
Browserify
处理JavaScript模块一直是一个问题。 js语言本身没有模块的概念,直到ES6。 Ergo,这个语言在90年代被用在浏览器环境中。 已经提出了包括AMD在内的各种解决方案。
‘Browserify’是模块问题的一个解决方案。 它可以将CommonJS模块捆绑在一起。 您可以将其与Gulp挂钩,您可以找到较小的转换工具,使您可以超越基本用法。 例如,watchify提供了一个在开发空闲的工作期间为您创建捆绑包的文件监视器。
Browserify生态系统由很多小模块组成。 这样,Browserify就符合Unix的理念。 Browserify比webpack更容易采用,实际上它是一个很好的替代品。
‘Splittable’是一个Browserify包装器,允许代码分割,支持ES6开箱即用,Tree shaking等等。
JSPM
使用‘JSPM’与以前的工具截然不同。 它附带了一个自己的命令行工具,用于将新的软件包安装到项目中,创建一个生产包,等等。 它支持‘SystemJS插件’,可以将各种格式加载到项目中。
Brunch
与Gulp相比,Brunch在更高层次的抽象上运作。 它使用类似于webpack的声明方法。 以示例为例,您可以考虑从Brunch网站改编以下配置:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21module.exports = {
files: {
javascripts: {
joinTo: {
'vendor.js': /^(?!app)/,
'app.js': /^app/,
},
},
stylesheets: {
joinTo: 'app.css',
},
},
plugins: {
babel: {
presets: ['es2015', 'react'],
},
postcss: {
processors: [require('autoprefixer')],
},
},
};
Brunch包括像brunch new, brunch watch –server, and brunch build –production。 它包含了很多创造性的功能,可以使用插件扩展。
Webpack
您可以说‘Webpack’采用比Browserify更单一的方法。 Browserify由多个小工具组成,而Webpack提供了一个核心,它提供了很多创造性的功能。
Webpack核心可以使用特定的加载程序和插件进行扩展。 它可以控制如何解决模块,使您可以调整您的构建以匹配特定情况和解决无法正常运行的软件包。
与其他工具相比,Webpack具有初始复杂性,但通过其广泛的功能集成可以弥补这一点。 这是一个需要耐心的高级工具。 但是一旦了解了背后的基本思路,webpack就变得很强大。
结语
历史上已经有很多JavaScript的构建工具。 每个人都试图以自己的方式解决一个特定的问题。 这些标准已经开始迎头赶上,基本语义的要求也更少了。 相反,工具可以在更高层次上竞争,并推动更好的用户体验。 通常,您可以一起使用几个单独的解决方案。
总的来说:
- 自动化构建工具和打包工具解决不同的问题。 您可以通过两者实现类似的结果,但通常最好将它们一起使用来相互补充。
- 较早的工具(如Make或RequireJS)仍然具有影响力,即使它们在前端开发中不如以往那样受欢迎。
- Bundinner如Browserify或webpack解决了一个重要的问题,并帮助您管理复杂的Web应用程序。
- 一些新兴技术从不同的角度解决问题。 有时候它们建立在其他工具之上,有时它们可以一起使用。