构建工具之间的比较

过去,将各种脚本连接在一起就足够了。但是,时代已经发生了变化,现在部署您的 JavaScript 代码可能是一项复杂的工作。随着单页应用程序(SPA)的兴起,这个问题已经更加复杂。他们往往依赖于许多庞大的库。

出于这个原因,有多种策略可以加载它们。您可以一次加载它们或考虑根据需要加载库。Webpack 支持许多这样的策略。

Node 和 npm(它的包管理器)的流行提供了更多的方案。在npm 开始流行之前,很难解决依赖关系。曾经有一段时间人们专门开了针对前端的包管理器,但最终还是 npm 赢了。现在依赖管理比以前舒服多了,尽管仍有许多挑战需要克服。

Task Runners

从历史上看,已经有很多构建工具。Make 也许是最知名的,即使是现在,它仍然是一个可行的选择。Grunt 和 Gulp 等专业的 Task Runner 是针对 JavaScript 开发人员的特殊需要创建的。通过 npm 提供的插件使得 task runner 功能强大且可扩展。甚至可以使用 npm scripts 作为 task runner。这很常见,特别是对于 webpack。

Make

Make 的历史很久远,因为它是在 1977 年首次发布的。尽管这是一个古老的工具,但它仍然保持着活力。Make 允许您为各种目的编写单独的任务。例如,您可以使用不同的任务来创建生成构建,压缩 JavaScript 或运行测试。很多其他的工具也都实现了类似的功能。

尽管 Make 主要用于 C 项目,但它并没有以任何方式与 C 强关联。James Coglan 详细讨论了如何使用 Make 处理 JavaScript。下面是 James 帖子中的部分代码:

Makefile 文件

PATH  := node_modules/.bin:$(PATH)
SHELL := /bin/bash

source_files := $(wildcard lib/*.coffee)
build_files  := $(source_files:%.coffee=build/%.js)
app_bundle   := build/app.js
spec_coffee  := $(wildcard spec/*.coffee)
spec_js      := $(spec_coffee:%.coffee=build/%.js)

libraries    := vendor/jquery.js

.PHONY: all clean test

all: $(app_bundle)

build/%.js: %.coffee
    coffee -co $(dir $@) $<

$(app_bundle): $(libraries) $(build_files)
    uglifyjs -cmo $@ $^

test: $(app_bundle) $(spec_js)
    phantomjs phantom.js

clean:
    rm -rf build

您可以使用 Make 特定语法和终端命令为任务建模,从而可以与 Webpack 集成。

npm scripts 作为 Task Runner

尽管 npm CLI 主要不是用作任务运行器,但是由于_package.json_ scripts 字段,它可以胜任这一功能。考虑以下示例:

package.json

"scripts": {
  "start": "webpack-dev-server --env development",
  "build": "webpack --env production",
  "build:stats": "webpack --env production --json > stats.json"
},

可以通过 npm run 列出这些脚本,然后使用 npm run <script> 执行它们。您还可以使用类似 test:watch 的约定命名脚本。

这种方法的问题在于它需要保持跨平台。您可能想要使用 rimraf 等实用程序,而不是 rm -rf

您可以通过 npm 脚本调用其他任务运行程序来隐藏实际的处理过程。这样,您可以在保持接口不同的同时重构工具。

Grunt

grunt,一个JavaScript任务处理器

Grunt 是前端开发人员的第一个著名任务选手。它的流行得益于它的插件架构。插件通常很复杂。因此,当配置变大时,很难理解发生了什么。

这是 Grunt 文档中的一个示例。在此配置中,您可以定义 linting 和 watcher 任务。当 watcher 任务运行时,它也会触发 lint 任务。这样,当您运行 Grunt 时,您在编辑源代码时会在终端中实时收到 lint 信息。

Gruntfile.js

module.exports = grunt => {
  grunt.initConfig({
    lint: {
      files: ["Gruntfile.js", "src/**/*.js", "test/**/*.js"],
      options: {
        globals: {
          jQuery: true,
        },
      },
    },
    watch: {
      files: ["<%= lint.files %>"],
      tasks: ["lint"],
    },
  });

  grunt.loadNpmTasks("grunt-contrib-jshint");
  grunt.loadNpmTasks("grunt-contrib-watch");

  grunt.registerTask("default", ["lint"]);
};

在实践中,您将有许多用于特定目的小任务,例如构建项目。Grunt 强大功能的一个重要组成部分就是隐藏了很多线索。

太过分了,这可能会有问题。很难理解幕后发生了什么。这与 Grunt 架构相关。

grunt-webpack 插件允许您在 Grunt 环境中使用 Webpack,同时将繁重的工作留给 Webpack。

Gulp

gulp,一个JavaScript任务运行器

Gulp 采取了不同的策略。您不必依赖每个插件的配置,而是处理实际代码。如果你熟悉 Unix 的管道,你会很喜欢 Gulp。通过 sources 匹配文件,filters 来操作这些文件,以及 sinks 传入管道构建结果。

这是一个缩写的示例 _Gulpfile,它改编自 Gulp 项目的 README 文件,让您更好地了解该 Gulp:

Gulpfile.js

const 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"],
};

// Not all tasks need to use streams.
// A gulpfile is another node program
// and you can use all packages available on npm.
gulp.task("clean", () => del(["build"]));
gulp.task("scripts", ["clean"], () =>
  // Minify and copy all JavaScript (except vendor scripts)
  // with source maps all the way down.
  gulp
    .src(paths.scripts)
    // Pipeline within pipeline
    .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(paths.scripts, ["scripts"]));

// The default task (called when you run `gulp` from CLI).
gulp.task("default", ["watch", "scripts"]);

鉴于是通过代码进行配置,如果遇到麻烦,你总是可以解决它。您可以将现有 Node 包打包为 Gulp 插件,依此类推。与 Grunt 相比,您可以更清楚地知道发生了什么。不过,你最终仍然会为简单任务编写大量样板文件。这就是新工具的用武之地。

webpack-stream 允许您在 Gulp 环境中使用 Webpack。

脚本加载器

有一段时间,RequireJS——一个脚本加载器——很受欢迎。其主要思想是提供一个异步模块定义并在此基础上实现构建。幸运的是,ES 标准已经赶上了,现在看来,了解 RequireJS 似乎只是出于好奇心。

RequireJS

RequireJS 可能是第一个真正受欢迎的脚本加载器。它首次正确地给出了 Web 上的 JavaScript 模块化方案。最吸引人的是 AMD,它引入了一个 define 包装器:

define(["./MyModule.js"], function (MyModule) {
  return function() {}; // Export at module root
});

// or
define(["./MyModule.js"], function (MyModule) {
  return {
    hello: function() {...}, // Export as a module function
  };
});

顺便说一句,它可以在 require 包装器中使用:

define(["require"], function (require) {
  var MyModule = require("./MyModule.js");

  return function() {...};
});

后一种方法消除了一部分杂乱,您仍然感觉有多余的代码。ES2015 和其他标准解决了这个问题。

Jamund Ferguson 撰写了一系列关于如何从 RequireJS 移植到 Webpack 的优秀博客。

JSPM

JSPM,一款浏览器端的包管理工具

JSPM 是一款包管理器,与其他工具完全不同。它附带了一个自己的命令行工具,用于将新包安装到项目中,创建生产包等等。它支持 SystemJS 插件,允许您将各种格式的包加载到项目中。

打包器

Task Runner 是高水平的伟大工具,它们允许您以跨平台方式执行操作。当您需要将各种资源拼接在一起并生成打包结果时,问题就开始了。打包器,如 Browserify、Brunch 或者 WebPack,都是出于这个原因而存在。它们对更底层的操作进行抽象。它们不是对文件进行操作,而是对模块和资源进行操作。

Browserify

Browserify,一款 JavaScript 打包工具

处理 JavaScript 模块一直是个问题。在 ES2015 之前,语言本身没有模块的概念。因此,当谈到浏览器环境时,我们一下就被拉扯到 90 年代了。于是,各种解决方案都被提了出来,这其中就包括 AMD

Browserify 是模块问题的一种解决方案。它允许将 CommonJS 模块打包起来。您可以将它和 Gulp 配合使用,您还可以找到许多更小的转换工具,使您可以超越基本用法。例如:watchify 提供了一个文件监视器,可以在开发期间为您自动打包,从而节省时间。

Browserify 生态系统由许多小模块组成,Browserify 始终坚持 Unix 哲学。采用 Browserify 比使用 Webpack 更加舒适,事实上,它是一个很好的替代方案。

splittable 集成了 Browserify、Babel 和 Closure Compiler,包括代码拆分、支持 ES2015 语法、Tree Shaking 等功能。bankai 是另一个可以考虑的选择。

ify-loadertransform-loader 允许您在 Webpack 中使用 Browserify 转换。

Brunch

Brunch,JavaScript 打包工具

与 Gulp 相比,Brunch 在更高的抽象层次上运作。它使用类似于Webpack 的声明方法。举个例子,请看从 Brunch 网站改编的以下配置:

module.exports = {
  files: {
    javascripts: {
      joinTo: {
        "vendor.js": /^(?!app)/,
        "app.js": /^app/,
      },
    },
    stylesheets: {
      joinTo: "app.css",
    },
  },
  plugins: {
    babel: {
      presets: ["react", "env"],
    },
    postcss: {
      processors: [require("autoprefixer")],
    },
  },
};

Brunch 自带了一些命令:brunch newbrunch watch --serverbrunch build --production。它包含很多开箱即用的功能,也可以使用插件进行扩展。

Brunch 有一个实验性的 Hot Module Replacement 运行时

Parcel

Parcel,web 应用打包器

Parcel 是一个高性能的打包器,与之前的产品不同,它不需要配置。以零配置方式在社区内流行了起来。其主要思想是你设置了一个 index.html,Parcel 将根据它开始捆绑过程。它支持开箱即用的热模块更换。

Parcel 有一整套零配置打包工具。这些工具包括 microbundlesfobilioviasbundle

Webpack

webpack,JavaScript 应用打包器

你可以说 Webpack 采用比 Browserify 更统一的方法。Browserify 由许多小的工具组成,但 Webpack 带有一个可提供大量开箱即用功能的核心。

Webpack 核心可以使用特定的 loader 和插件进行扩展。它可以控制如何解析模块,从而可以调整您的构建以匹配特定情况,解决 Webpack 核心无法正常处理的软件包。

与其他工具相比,Webpack 具备一定的复杂性,但它通过其广泛的功能集弥补了这一点。这是一个需要耐心的高级工具。一旦你理解了它背后的基本思想,Webpack 就会变得强大。

其他选择

您可以找到更多替代方案,如下所示:

  • pundle 宣城自己是下一代打包工具,尤其是性能方面,值得注意。
  • Rollup 专注于捆绑 ES2015 代码。Tree Shaking 是它的卖点之一,它也支持代码分割。您可以通过 rollup-loader 将 Rollup 与 Webpack 一起使用。
  • AssetGraph 采用完全不同的方法,并建立在 HTML 语义之上,使其成为超链接分析结构分析的理想选择。webpack-assetgraph-plugin 将webpack 和 AssetGraph 结合使用。
  • FuseBox 是一个专注于速度的打包器。它采用零配置方法,旨在开箱即用。
  • StealJS 是一个依赖加载器和构建工具,它专注于性能和易用性。
  • Flipbox 在统一界面后面包装许多了打包工具。
  • Blendid 是 Gulp 和打包工具的混合体,也通过管道的形式处理资源。

总结

从历史上看,有很多用于 JavaScript 的构建工具。每个工具都试图以其方式解决特定问题。标准已经开始迎头赶上,围绕基本语义的工作需要花费的精力越来越少。相反,工具可以在更高层次上竞争并推动更好的用户体验。通常,您可以将几个单独的解决方案一起使用。

回顾一下:

  • 任务运行器打包器解决了不同的问题。您可以使用两者获得类似的结果,但通常最好将它们一起使用以相互补充。
  • 较旧的工具,例如 Make 或 RequireJS,即使它们在 Web 开发中不像以前那样流行,它们仍然具有影响力。
  • Browserify 或 Webpack 等打包工具解决了一个重要问题,可帮助您管理复杂的 Web 应用程序。
  • 新兴技术从不同角度解决问题。有时它们构建在其他工具之上,有时它们可​​以一起使用。
用户头像
登录后发表评论