[译]为何我要离开gulp和grunt转投npm脚本的怀抱

原文连接:https://medium.freecodecamp.com/why-i-left-gulp-and-grunt-for-npm-scripts-3d6853dd22b8#.n7m1855ufjavascript

做者:Cory House前端

------------------------------------------------------------------------------------------------------------------------------java

我知道你在想什么。 什么?!Gulp不是才刚把Grunt干掉吗? 为何咱们不能在Javascript的地盘上消停一下子? 我据说过, 可是。。。node

 

我发现Gulp和Grunt都是些没必要要的抽象。npm脚本已经足够强大并且每每更容易忍受。react

 

让咱们从一个例子开始webpack

我是一个Gulp的超级粉丝。可是在个人上个项目中, 最终个人gulp文件中有几百行代码和一大堆Gulp插件。我曾经为了用Gulp整合这堆东西而挣扎不已,有Webpack、Browsersync、hot reloading、Mocha等等。为何? 好吧,有些插件的文档没覆盖到个人使用场景。有些插件只公开了部分我须要的API。有个插件有一个奇怪的bug,它能监视的文件数很小。另外一个插件则在命令行中输出信息时掉色了。git

 

这些都是能够解决的问题,可是当我直接使用这些工具时从不会发生这些问题。es6

 

最近我注意到不少开源项目都仅仅使用npm脚本。我决定退一步从新审视本身。 我真的须要Gulp吗? 原来并非。github

 

我决定在个人新开源项目中尝试只使用npm脚本。我建立了一个富开发环境,并仅用npm脚本为React应用建立了一个进程。想知道这看起来像什么吗? 你能够查看React弹弓项目。 在Pluralsight平台(一家美国软件开发在线教育平台)上我解决了用npm脚本为“用ES6编写React和Redux”这个目标建立进程的问题。web

 

让我惊喜的是,相比Gulp,如今我更乐于使用npm脚本。这就是缘由。

 

Gulp和Grunt作错什么了?

随着时间的推移,我注意到像Gulp和Grunt这样的任务执行器都具备的三个核心问题:

1. 依赖插件制做者

2. 调试困难

3. 滞后的文档

让咱们依次思考这三个问题。

 

问题#1: 依赖插件制做者

当你使用一个新的或不流行的技术时,可能根本没有对应的插件。 当有插件出现时,这个技术可能已通过时了。 举个例子, Babel6刚刚发布不久。 它的API变化很大,不少Gulp插件不兼容这个最新版本。当使用Gulp时,我曾由于我须要的Gulp插件还没更新而被卡住了。

 

使用Gulp或者Grunt的话,你必需要等插件维护者提供更新,或者你本身来修复这些问题。这会延缓你应用最新的现代化工具的能力。相对的,当我使用npm脚本时,我直接使用工具,而不须要一个额外的抽象层。这表示当Mocha、Istanbul、Babel、Webpack、Browserify等工具的最新版本发布时,我有办法立刻应用这些最新的版本。

 

在选择方面,没什么能够战胜npm:

当你使用npm脚本时,你不须要搜索任何Grunt或Gulp插件。你直接在超过227,000个npm包中选择便可。

 

公平起见,若是你须要的Grunt或Gulp插件变得不可用了,你固然能够直接应用npm包。但今后你就再也不须要为了那些特殊的任务而借助Gulp或Grunt了。

 

问题#2: 调试困难

在集成失败时,在Grunt和Gulp中进行调试会让人很泄气。 当你在一个额外的抽象层下工做的时候,可能引起bug的潜在缘由将更多:

1. 是否是基础工具坏了?

2. 是否是Grunt/Gulp组件坏了?

3. 是否是个人配置错了?

4. 是否是我用了不兼容的版本?

使用npm脚本能够根除第二点. 我以为第三点不多见,由于我一般是直接调用工具的命令行接口。 最后,自从我直接使用npm代替任务执行器的抽象概念后,我项目中包的数量减小了,第四点也不多见了。

 

问题#3:滞后的文档

我须要的核心工具的文档几乎老是比相应的Grunt和Gulp组件的要好。例如, 若是我使用gulp-eslint,到头来我须要将时间分别花在gulp-eslint文档和ESLint网站上。 我不得不在抽象的组件和工具之间来回切换上下文环境。 Gulp和Grunt中的核心矛盾是这个:

只了解工具是远远不够的。Gulp和Grunt还要求你理解插件的抽象概念。

 

大多数的工具提供清晰、强大和具有良好文档的命令行接口。看看ESLint的CLI文档就是一个很好的例子。我发如今npm脚本中读取和实现一个简短的命令行调用会更清晰、低冲突且更易于调试(在把那些抽象层移除后)。

 

如今我认为我已经找到了痛点, 问题是, 为何咱们以为咱们须要像Gulp和Grunt那样的任务执行器?

 

为何咱们忽略了npm也能够编译?

我认为有四个关键的误解致使Gulp和Grunt变得如此流行:

1. 人们认为使用npm脚本须要很高的命令行水平

2. 人们认为npm脚本不够强大

3. 人们认为Gulp的流对快速构建来讲是必须的

4. 人们认为npm脚本不能跨平台运行

让咱们一个个解决这些误解。

 

误解#1:npm scripts须要很高的命令行水平

你不须要知道你的操做系统的不少命令行知识也能够享用到npm脚本的力量。固然,grep、sed、awk和pipes命令是值得终身学习的技能,但你没必要为了使用npm脚本而成为一个Unix或windows的命令行大师。 你能够改成在npm中写上千行书写良好的脚原本完成你的工做。

 

例如,你可能不知道在Unix中强制删除的命令行是:rm -rf。不要紧。 你可使用rimraf来完成相同的工做(并且它能够跨平台使用)。大多数npm包是在假设你对你的操做系统命令行知识知之甚少的前提下提供接口。当你要用某个功能的时候,只须要在npm上搜索你须要的包,阅读文档并学习便可。过去我老是搜索Gulp插件。如今我只搜索npm包。 有一个很是好的资源网站:libraries.io

 

误解#2: npm scripts不够强大

npm基本仅靠本身已经强大得使人惊讶。这些是常规的pre和post钩子:

{
  "name": "npm-scripts-example",
  "version": "1.0.0",
  "description": "npm scripts example",
  "scripts": {
    "prebuild": "echo I run before the build script",
    "build": "cross-env NODE_ENV=production webpack",
    "postbuild": "echo I run after the build script"
  }
}

 Github上的package.json连接

 

全部你要作的就是遵循约定。 以上脚本会基于他们的前缀按顺序执行。 prebuild脚本会在build脚本以前执行,由于它有相同的名字,可是用了”pre“前缀。 postbuild脚本会在build脚本以后执行,由于它有”post“前缀。 因此, 若是我建立的脚本命名为prebuild、build和postbuild, 当我敲入‘npm run build’命令时,它们会自动按顺序执行。

 

你也能够经过用一个脚本调用另外一个脚本的方法来将大问题分解:

{
  "name": "npm-scripts-example",
  "version": "1.0.0",
  "description": "npm scripts example",
  "scripts": {
    "clean": "rimraf ./dist && mkdir dist",
    "prebuild": "npm run clean",
    "build": "cross-env NODE_ENV=production webpack"
  }
}

Github上的package.json连接

 

在这个例子中,prebuild任务调用clean任务。这容许你将你的脚本分解得更小、命名更恰当、单一职责、一行内完成。

 

你能够用&&操做符在一行命令中同步调用多个脚本。 在上述的clean步骤中的脚本会一个接一个执行。这种简洁真的会让你笑出声,若是你曾为了让任务列表能在Gulp中按顺序执行而苦苦挣扎过。

 

并且若是一个命令实在太复杂,你老是能够调用一个单独的文件:

{
  "name": "npm-scripts-example",
  "version": "1.0.0",
  "description": "npm scripts example",
  "scripts": {
    "build": "node build.js"
  }
}

Github上的package.json连接

 

以上代码中,我正在build任务中调用一个单独的脚本。这个脚本将会经过Node执行, 并且所以我能够应用任何我须要的npm包, 和应用全部javascript中的功能。

 

我能够继续了, 但核心功能都记录在这里。还有,这里也有一个短小的关于将npm做为一个构建工具的Pluralsight课程. 或者,查看React弹弓项目并将其看成全部这些行为的一个例子。

 

误解#3: Gulp的流是快速构建所必需的

Gulp能够从Grunt上快速取得市场主导权,是由于Gulp的内存流比Grunt的基于文件的作法快得多。可是你并不须要Gulp来享用流的强大。 事实上, 流功能早就已经集成到Unix和Windows的命令行中。 管道(|)操做符能够将一个命令的输出以流的形式做为另外一个命令的输入。 而重定向(>)操做符能够将输出重定向到一个文件中。

 

因此, 举个例子, 在Unix中我可使用'grep'读取一个文件的内容,并将其输出重定向到一个新文件中:

grep ‘Cory House’ bigFile.txt > linesThatHaveMyName.txt

 

上面的作法就是流。没有任何中间文件被建立。

(想知道上述命令在跨平台的状况下该怎么作? 请读下去。。。)

 

你一样可使用'&'操做符在Unix上同时执行两个命令:

npm run script1.js & npm run script2.js

 

以上这两个脚本会在同时执行。为了跨平台的同时执行脚本,可使用npm-run-all。这就导向了下一个误解。。。

 

误解#4: npm脚本不能跨平台运行

不少项目都依赖于特定操做系统,因此跨平台的顾虑并不算重要。但若是你须要跨平台运行,npm脚本依然能够工做得很好。数不清的开源项目就是证据。接下来就是怎么作了。

 

你的操做系统的命令行会运行你的npm脚本。 因此在Linux和OSX,你的npm脚本在一个Unix命令行上运行。 在Windows,npm脚本在Windows命令行上运行。所以,若是你想你的构建脚本能够在全部平台上运行,你须要同时让Unix和Windows开心。 这里有三个方法:

 

方法1: 使用跨平台的命令。存在数量惊人的跨平台命令。 这里只是一小部分:

&& chain tasks (Run one task after another)
< input file contents to a command
> redirect command output to a file
| redirect command output to another command

 

方法2: 使用node包。 你能够用node包代替shell命令。例如, 使用rimraf代替'rm -rf‘. 使用cross-env在跨平台方式下设置环境变量。 在google、npm或libraries.io中搜索你想要作什么,几乎确定会有一个node包能够实现你的需求并且仍是跨平台的。并且若是你的命令行调用已经太长,你能够在单独脚本中调用Node包,就像这样:

node scriptName.js

 

上述脚本是普通的古老的javascript文件,经过Node执行。 而且由于你只是在命令行上调用一个脚本, 你不止能够调用js文件。你能够运行任何你的操做系统能执行的脚本, 例如Bash、Python、Ruby或Powershell等等。

 

方法3: 使用ShellJS。 ShellJS是一个npm包, 它能够经过Node运行Unix命令。 这就给了你在任何平台上执行Unix命令的能力, 包括Windows。

 

我在React弹弓项目中同时使用了方法#1和#2。

 

痛点

诚然,npm脚本也有一些缺点: JSON规范并不支持注释, 因此你没法在package.json文件中添加注释。 这里有几个方法能够围绕这个限制开展工做:

1. 简短、命名良好、目的单一的脚本

2. 单独对脚本提供文档(例如在一个README.md文件中)

3. 调用单独的js文件

 

我倾向于选项#1. 若是你将每一个脚本都分解成只有单一职责, 将不多再须要注释。脚本的名字能够彻底描述其意图,就好像全部简短且命名良好的函数同样。就像我在《简洁代码:编写人能看懂的代码》中的讨论同样,短小且单一职责的函数不多须要注释。当我以为注释是必要的时,我使用选项#3并将脚本移到单独的文件中。这让我在须要时可使用javascript的全部能力。

 

Package.json也不支持变量。这听起来像是一个大问题,但因为如下两个缘由,它再也不是问题。 首先,最一般的须要变量的状况是要解决环境问题,但这你能够在命令行中设置。其次,若是你由于其余缘由须要用到变量,你彻底能够调用一个单独的js文件。 在React-starter-kit项目中你能够找到该作法的一个优雅的例子。

 

最后,在建立很是长且很复杂命令行参数时,这依然是有风险的,由于这些命令很是难理解。 代码审查和勤于重构是一个很好的方法,能够保证npm脚本都被分解成简短、命名良好且目的单一的函数,这能让全部人都能理解这些脚本。 若是脚本复杂到须要注释,你应该能够很容易的将单个脚本重构成多个命名良好的脚本,或直接将其提取到一个单独的文件中。

 

增长抽象概念必须理由充分

Gulp和Grunt都是将我使用的工具进行再次抽象。 抽象概念是有用的,可是抽象概念须要成本。 它们可能形成内存泄漏。 它们让咱们依赖于插件维护者和他们的文档。并且他们经过增长依赖项的数量来增长了复杂度。 我已经决定像Gulp和Grunt这样的任务执行器的抽象概念我不再须要了。

 

想要更多细节? 在Pluralsight平台(一家美国软件开发在线教育平台)上我解决了用npm scripts为“用ES6编写React和Redux”这个目标建立进程的问题。

 

评论? 能够在文章底部、RedditHacker News上进行评论。

 

最后, 我离第一个建议这么作的人已经很遥远。 下面是一些很是棒的连接:

· 用npm run完成任务自动化 -- James Holliday

· 使用npm脚本实现进阶前端自动化 -- Kate Hudson

· 如何将npm用成一个构建工具 -- Kieth Cirkel

· npm做为构建工具介绍 -- Marcus Hammarberg

· Gulp很是棒,可是咱们真的须要它吗? -- Gonto

· NPM脚本之于构建工具 -- Andrew Burgess

        …………

 

 

Cory House是《用React和Flux建立应用》、《ES6下的React和Redux》、《简洁代码:编写人能看懂的代码》和其余多个Pluralsight上的课程的做者。 他是VinSolutions 的软件架构师,而且经过像前端开发和简洁代码这些软件实践来训练国际化的软件工程师。Cory是一位微软的MVP、Telerik开发专家同时仍是outlierdeveloper.com网站的创始人。

 

---------------------

谢谢观看。 翻译的不对欢迎指正。

2016.08.23

相关文章
相关标签/搜索