为什么要用npm scripts替代gulp

现在前端自动化的配套工具估计都离不开gulp或者是grunt,有一些或许会用上webpack辅助用上最新的ES6语法等;但是不知道大家在使用gulp众多插件的时候有没有碰到过一些问题,比如:有一些插件你仅仅需要用到其中一点点的API、插件更新速度非常慢、有一些插件碰到bug的时候调试起来非常麻烦等。所以总结一下gulp或者grunt其实都会有以下问题:

  1. 依赖于插件作者
  2. 调试很不方便
  3. 插件文档说明不连贯

而如果直接使用npm scripts完全可以避免这些问题,在我们package.json里面的scripts属性直接定义需要执行的任务,比如npm startnpm test其实就是npm run startnpm run test的缩写,我们可以在scripts里面定义各种需要的任务,举个最简单的例子(清除dist目录):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1.用gulp插件来实现
var gulp = require('gulp');
var del = require('del');
gulp.task('clean', function() {
del(['./dist/**/*']).then(paths => {
console.log('Deleted files and folders:\n', paths.join('\n'));
});
});
# 2.用npm scripts来实现
# package.json配置
...
"scripts": {
clean: "rimraf ./dist"
},
"devDependencies": {
"rimraf": "^2.5.2"
}

从上面示例代码可以看出明显直接用npm scripts实现的同一个功能相对gulp要简单得多,当然这个功能比较简单,如果碰到复杂的一些任务肯定就有反对的声音了。那我们将细细将上面三点来阐述。

依赖于插件作者

当你需要使用到最新的或者不那么流行的技术时,根本就没有插件给你使用;或者一些插件已经过时了。最新Babel 6已经发布,很多API明显修改了,所以很多gulp插件根本不适用于最新版本。

这个时候你就必须等待作者来更新插件,或者你自己去fix这些问题,这会导致你不能及时用上最新版本的工具。相反,当你直接使用npm scripts的时候,你只需要直接找到可以实现的工具即可。这意味着当新版本的MochaBabelWebpackBrowserify发布的时候,你就可以马上用上这些版本。

就目前插件数量来说,没有什么可以打败npm包:

img

调试很不方便

由于gulp增加了一层抽象,所以会有潜在的bug

  1. 是否基础工具崩溃了?
  2. 是否Grunt/Gulp插件崩溃了?
  3. 是否配置文件出错了?
  4. 是否用了不稳定的版本?

而直接使用npm scripts直接避免了第2点跟第3点,而由于不使用那么多插件,那么包相对较少,第4点也很少会碰到。

插件文档说明不连贯

相比有用过很多插件的人都知道,一些核心的工具文档写得总比包装起来的Gulp插件要清晰得多。举个简单的例子来说,如果我需要用到gulp-eslint插件,那么就可能会不断在gulp-eslint的文档跟ESLint网站切换,必须对比看看两者存在些什么区别。

为什么我们总是忽略使用npm scripts而更青睐于Gulp

GulpGrunt之所以这么流行,主要有下面4个点:

  1. 开发者认为npm scripts需要能写命令行的技能
  2. 开发者认为npm scripts能处理的能力不足够
  3. 开发者觉得Gulp的流对于快速构建是很有必要的
  4. 开发者认为npm scripts不能跨平台运行

开发者认为npm scripts需要能写命令行的技能

其实你完全不需要精通于Unix或者Windows的命令行脚本,比如你不知道在Unix下面删除一个目录的命令是:rm -rf,这其实没啥问题,你完全可以使用rimraf,同时它也是跨平台的。在这里推荐一个工具包资源网站:libraries.io

开发者认为npm scripts能处理的能力不足够

npm scripts其实比你想象中的要强大,主要依赖于预处理和后置处理钩子,比如下面例子:

1
2
3
4
5
6
7
8
9
10
{
"name": "npm-scripts-demo",
"version": "1.0.0",
"description": "npm scripts demo",
"scripts": {
"prebuild": "echo I run before the build script",
"build": "cross-env NODE_ENV=production webpack",
"postbuild": "echo I run after the build script"
}
}

正如上面例子一样,prebuild定义的脚本会比build任务先执行,而postbuild定义的脚本会比build任务后执行,因为相对于build来说,增加了一个前缀prepost,所以当我执行npm run build的时候会自动地顺序执行prebuild -> build -> postbuild

同时你可以将一个大的任务不断拆分成小的任务,比如:

1
2
3
4
5
6
7
8
9
10
{
"name": "npm-scripts-demo",
"version": "1.0.0",
"description": "npm scripts demo",
"scripts": {
"clean": "rimraf ./dist && mkdir dist",
"prebuild": "npm run clean",
"build": "cross-env NODE_ENV=production webpack"
}
}

在上面例子中将clean任务抽离出来了,当你执行npm run build的时候,会先自动执行npm run prebuild任务,那就相当于执行了npm run clean任务了,注意上面的&&表示先后顺序执行,区别于&表示同时执行。

npm scripts的一些缺点

不得不承认,用npm scripts来写自动化构建任务还是存在一些不足:不能在JSON文件里面写注释。有一些方法可以弥补这方面的不足:

  1. 写功能相对小而独立并且命名好的脚本名字
  2. 脚本跟文档分离(将文档写进READ.md)
  3. 直接分离脚本写进Makefile等独立的文件

推荐使用第一种,脚本名字本来就应该能够直接描述功能。

一些参考