为什么要用npm scripts替代gulp

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



依赖于插件作者
调试很不方便
插件文档说明不连贯
而如果直接使用npm scripts完全可以避免这些问题,在我们package.json里面的scripts属性直接定义需要执行的任务,比如npm start和npm test其实就是npm run start和npm 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要简单得多,当然这个功能比较简单,如果碰到复杂的一些任务肯定就有反对的声音了。那我们将细细将上面三点来阐述。 <!-- more --> 依赖于插件作者 当你需要使用到最新的或者不那么流行的技术时,根本就没有插件给你使用;或者一些插件已经过时了。最新Babel 6已经发布,很多API明显修改了,所以很多gulp插件根本不适用于最新版本。


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



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



img



调试很不方便
由于gulp增加了一层抽象,所以会有潜在的bug:



是否基础工具崩溃了?
是否Grunt/Gulp插件崩溃了?
是否配置文件出错了?
是否用了不稳定的版本?
而直接使用npm scripts直接避免了第2点跟第3点,而由于不使用那么多插件,那么包相对较少,第4点也很少会碰到。



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



为什么我们总是忽略使用npm scripts而更青睐于Gulp
Gulp和Grunt之所以这么流行,主要有下面4个点:



开发者认为npm scripts需要能写命令行的技能
开发者认为npm scripts能处理的能力不足够
开发者觉得Gulp的流对于快速构建是很有必要的
开发者认为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来说,增加了一个前缀pre和post,所以当我执行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文件里面写注释。有一些方法可以弥补这方面的不足:



写功能相对小而独立并且命名好的脚本名字
脚本跟文档分离(将文档写进READ.md)
直接分离脚本写进Makefile等独立的文件
推荐使用第一种,脚本名字本来就应该能够直接描述功能。



一些参考
Task automation with npm run – James Holliday
Advanced front-end automation with npm scripts – Kate Hudson
How to use npm as a build tool – Kieth Girkel
Introduction to npm as a Build Tool – Marcus Hammarberg
Gulp is awesome, but do we really need it? – Gonto
NPM Scripts for Build Tooling – Andrew Burgess



指令分析
package.json里面



“dev”: “node build/dev-server.js”,



“build”: “node build/build.js”,



意思:运行”npm run dev”的时候执行的是build/dev-server.js文件,



运行”npm run build”的时候执行的是build/build.js文件。



build文件夹分析
build/dev-server.js
npm run dev 执行的文件build/dev-server.js文件,执行了:



检查node和npm的版本
引入相关插件和配置
创建express服务器和webpack编译器
配置开发中间件(webpack-dev-middleware)和热重载中间件(webpack-hot-middleware)
挂载代理服务和中间件
配置静态资源
启动服务器监听特定端口(8080)
自动打开浏览器并打开特定网址(localhost:8080)



说明: express服务器提供静态文件服务,不过它还使用了http-proxy-middleware,一个http请求代理的中间件。前端开发过程中需要使用到后台的API的话,可以通过配置proxyTable来将相应的后台请求代理到专用的API服务器。



build/webpack.base.conf.js
dev-server依赖的webpack配置是webpack.dev.conf.js文件,



测试环境下使用的是webpack.prod.conf.js



webpack.dev.conf.js中又引用了webpack.base.conf.js



webpack.base.conf.js主要完成了下面这些事情:



配置webpack编译入口
配置webpack输出路径和命名规则
配置模块resolve规则
配置不同类型模块的处理规则
这个配置里面只配置了.js、.vue、图片、字体等几类文件的处理规则,如果需要处理其他文件可以在module.rules里面配置。



build/webpack.dev.conf.js
在webpack.base.conf的基础上增加完善了开发环境下面的配置,主要包括下面几件事情:



将hot-reload相关的代码添加到entry chunks
合并基础的webpack配置
使用styleLoaders
配置Source Maps
配置webpack插件



build/check-versions.js和build/dev-client.js
最后是build文件夹下面两个比较简单的文件,



dev-client.js似乎没有使用到,代码也比较简单,这里不多讲。



check-version.js完成对node和npm的版本检测



build/utils.js和build/vue-loader.conf.js



webpack配置文件中使用到了utils.js和vue-loader.conf.js这两个文件,utils主要完成下面3件事:



配置静态资源路径
生成cssLoaders用于加载.vue文件中的样式
生成styleLoaders用于加载不在.vue文件中的单独存在的样式文件



vue-loader.conf则只配置了css加载器以及编译css之后自动添加前缀。



build/build.js
构建环境下的配置,



build.js主要完成下面几件事:



loading动画
删除创建目标文件夹
webpack编译
输出信息



build/webpack.prod.conf.js



构建的时候用到的webpack配置来自webpack.prod.conf.js,该配置同样是在webpack.base.conf基础上的进一步完善。主要完成下面几件事情:



合并基础的webpack配置
使用styleLoaders
配置webpack的输出
配置webpack插件
gzip模式下的webpack插件配置
webpack-bundle分析
说明: webpack插件里面多了丑化压缩代码以及抽离css文件等插件。



config文件夹分析
config/index.js



config文件夹下最主要的文件就是index.js了,



在这里面描述了开发和构建两种环境下的配置,前面的build文件夹下也有不少文件引用了index.js里面的配置。



config/dev.env.js、config/prod.env.js和config/test.env.js



这三个文件就简单设置了环境变量而已,没什么特别的。



npm run build之后生成的dist如何扔到服务器运行(npm run build之后如何本地运行)



运行npm run build之后,会生成一个dist文件夹


Category web