也许你并不需要 Gulp, Grunt ?

jopen 8年前

我已经在我的项目中使用 npm scripts 有六个月。在之前,我一直使用 Gulp , Grunt ,它们帮了我很多,让我的任务完成得更加高效,也自动帮我处理很多繁琐的任务。 然而,我开始感觉到我好想是在跟工具战斗而不是关乎自己的代码。

Grunt, Gulp, Broccoli,, Brunch 等等工具都要求你的任务符合它们定出的规则,每个工具都要你花时间去了解。并且一旦代码复杂性提高了,你需要花更多精力去处理这些工具。

这些构建工具依靠的插件外面包裹了一层命令行工具,在核心工具外面多了一层抽象,这也意味着可能存在隐患。

下面三个问题我遇到过好多次

  • 如果这个工具下没有你想要的插件,那你就太不幸了(除非你自己写一个)
  • 你尝试使用的插件包裹着你想用的另一个工具的老版本,那么会导致特性和文档对不上号。
  • 错误不是一直都能被很好处理,如果一个插件出错了,它可能不会把错误抛出,这会导致你很痛苦,因为不知道如何 Debug 。

但是,记住

我想说:如果你对当前使用的构建工具非常满意,并且你要的它都能给你,咁你keep住就嘚啦。 只是因为 npm scripts 越来越重要,而不是说你必须跳船。把注意力集中于编写程序而不是用在学习工具。如果你也觉得你是在和工具战斗,那么我建议你尝试使用 npm scripts

如果你真的想开始使用 npm scripts,那么请继续看下去!你会看到很多例子,并且我建好了 npm-build-boilerplate 给你作为出发点。开始吧!

编写 npm scripts

我们都花了大把时间在 package.json 文件上,我们的依赖和 scripts 都在这,这是我 boilerplate 项目的精简版 package.json

{   "name": "npm-build-boilerplate",   "version": "1.0.0",   "scripts": {     ...   },   "devDependencies": {     ...   } } 

我们的 scripts 都会写在 scripts 下面,而devDependencies 下存放的是我们项目的依赖。
开始之前,想看看这个项目的结构:

项目结构

SCSS => CSS

我是 SCSS 的狂热爱好者,所以我迫不及待地想跟你介绍。我使用 node-sass 来帮我做这项任务,

npm install --save-dev node-sass 

使用上面的命令安装好 node-sass, 那么它会出现在你的 package.json 中的 devDependencies里面,这样对于其他想跑你代码的人就方便多了。安装完成之后,我们来使用它:

node-sass --output-style compressed -o dist/css src/scss 

这行命令干了这样的事: node-sass 同学 (你是学计算机的吧?) 去把 src/scss 下的所有 SCSS 文件都编成 CSS 然后放在 dist/css 下面,对了,帮我压缩一下(compressed)

但是大领导通常都懒得说这么长的话,它跟 scripts 说 Just do IT:

"scripts": {   "scss": "node-sass --output-style compressed -o dist/css src/scss" } 

然后大领导就只需要喊口号即可:

npm run scss 

这样,大领导(你)就完成了编译 Sass 的任务(just kidding)。

接下的的部分,任何时候你都可以像上面那样创建 npm scripts

只需要替换把 scss 替换成你的命令即可。

正如你所见,很多命令行工具都需要一大把选项供你选择。比如这里是 node-sass options 下面是多个选项的配置:

"scripts": {   "scss": "node-sass --output-style nested --indent-type tab --indent-width 4 -o dist/css src/scss" } 

Autoprefix CSS with PostCSS

现在我们使用 Autoprefixer & PostCSS 来自动添加前缀,我们可以同时安装多个模块:

npm install --save-dev postcss-cli autoprefixer 

我们安装了两个模块,因为 PostCSS 默认什么都不做,他依赖于像Autoprefixer 这类插件。

现在我们都把两个工具添加到 devDependencies 中,在 scripts 对象中再添加一个任务:

"scripts": {   ...   "autoprefixer": "postcss -u autoprefixer -r dist/css/*" } 

这个任务说,postcss 你用 autoprefixer 帮我把 dist/css/* 下的所有 css 文件给处理一下,那具体要适应什么浏览器你也要告诉 Autoprefix:

"autoprefixer": "postcss -u autoprefixer --autoprefixer.browsers '> 5%, ie 9' -r dist/css/*" 

同样的,postcss-cli, autoprefixer也有很多可选项

Linting JavaScript

保持良好的编程风格和规范是错误最小化和开发效率的保障,Linting 帮助我们自动地检查,我们可以使用 eslint

再一次,我们安装 package,这一次使用快捷方式:

npm i -D eslint 

同下面的方式一样

npm install --save-dev eslint 

安装完,我们使用 eslint 设置一些执行我们代码的基本规则,执行下面代码进入指引:

eslint --init 

我建议你选择”Answer questions about your style” 并且回答它问的问题,这会生成一个新的文件在你的项目根部。

再次丰富 scripts 对象:

"scripts": {    ...    "lint": "eslint src/js"  } 

这13个字符的命令会查找在 src/js 下的所有js文件,并根据配置重新执行一遍。另外你会被这些配置逼疯的。

Uglifying JavaScript files

接下来是合并压缩 JavaScript文件,我们选择 uglify-js 来做:

npm i -D uglify-js 

然后再次配置 package.json :

"scripts": {   ...   "uglify": "mkdir -p dist/js && uglifyjs src/js/*.js -m -o dist/js/app.js" } 

值得一提的是 npm scripts 本质上是命令行的别名,这意味着你可以在 scripts 中使用标准命令行代码,这里用到了 mkdir 和 &&

前半部分,mkdir -p dist/js 的意思是如果(-p)不存在这样的目录结构,则创建一个新的执行成功之后(&&) 再执行后半部分 uglifyjs src/js/*.js -m -o dist/js/app.js" 这部分是告诉 uglifyjs 使用 “mangle” (-m) 命令,应用在 src/js 的所有js 上,并产出到 dist/js/app.js

让我们再丰富下 uglify , 创建一个 compress 版本的 dist/js/app.js (使用 -c )

"scripts": {   ...   "uglify": "mkdir -p dist/js && uglifyjs src/js/*.js -m -o dist/js/app.js && uglifyjs src/js/*.js -m -c -o dist/js/app.min.js" } 

Compressing Images

接下来看看压缩图片。根据 httparchive.org , 前1000 个热门网站的页面大小是 1.9mb ,而其中图片就占了 1.1mb ,所以页面提速的首要因素是压缩图片体积。

安装 imagemin-cli

npm i -D imagemin-cli 

Imagemin 几乎可以压缩所有类型的图片,包括 GIF,JPG,SVG。你可以传递一个文件夹路径,然后它会处理其中的所有图片:

"scripts": {   ...   "imagemin": "imagemin src/images dist/images -p", } 

这个任务是压缩所有在 src/images 下的图片,并把结果输出到 dist/images。-p 的意思是 尽量创建 “progressive” 的图片。

SVG Sprites

近几年 SVG 越来越受欢迎,因为它几乎兼容所有设备,可以用 CSS 调整,视觉效果良好。然而很多 SVG 编辑器会残留多余和不必要的代码。我们使用 svgo 来移除。

你可以把压缩 SVG 和拼接精灵图放在一块,拼接精灵图我们使用svg-sprite-generator

npm i -D svgo svg-sprite-generator 

现在你应该很熟悉这种模式了吧。

"scripts": {   ...   "icons": "svgo -f src/images/icons && mkdir -p dist/images && svg-sprite-generate -d src/images/icons -o dist/images/icons.svg" } 

icons 这个任务做了三件事,传递了一个文件夹路径(-f)给 svgo, 然后如果没有(-p) dist/images 这个文件夹则创建一个,接着就拼接精灵图,结果保存到(-d) src/images/icons, 产出(-o)叫做 dist/images/icons.svg

Serve and Automatically Inject Changes with BrowserSync

最后一个问题是 BrowserSync,他可以帮我们做的是:开启一个本地服务器,自动更新文件,自动在浏览器中同步点击,滚动效果。

npm i -D browser-sync 
"scripts": {   ...   "serve": "browser-sync start --server --files 'dist/css/*.css, dist/js/*.js'" } 

这个任务打开一个本地服务(—server),以当前路径作为根目录,—files 参数告诉 Browsersync 监听 dist 下的所有css 和 js文件,一有更新,马上自动刷新页面。

Grouping tasks

有了上面的代码,我们现在能够:

  • 编译 SCSS 并且自动添加前缀
  • 检查并压缩 JavaScript
  • 压缩图片
  • 将一个 SVG 文件夹转成一张 SVG 精灵图
  • 创建一个本地服务器,并实时自动刷新浏览器

Combining CSS tasks

我们可以把 CSS 相关的任务合并在一起:

"scripts": {   ...   "build:css": "npm run scss && npm run autoprefixer" } 

当你执行 npm run build:css ,它会在执行 scss 任务成功之后在执行 autoprefixer 。
其他任务也类似,我们也可以这样做

"scripts": {   ...   "build:js": "npm run lint && npm run uglify",   "build:images": "npm run imagemin && npm run icons",   "build:all": "npm run build:css && npm run build:js && npm run build:images" } 

Watching for changes

我们现在还需要监听文件的变动,并自动执行相应的任务,我推荐使用 onchange,安装:

npm i -D onchange 

添加任务:

"scripts": {   ...   "watch:css": "onchange 'src/scss/*.scss' -- npm run build:css",   "watch:js": "onchange 'src/js/*.js' -- npm run build:js", } 

任务是给 onchange 传递一个需要监听的文件路径,我们需要 onchange 自动执行的命令则放在 — 后面。

Let’s add one more watch command to finish off our npm scripts build process.

安装多一个 package, parallelshell

npm i -D parallelshell 

再一次,添加新的任务到 scripts 对象上:

"scripts": {   ...   "watch:all": "parallelshell 'npm run serve' 'npm run watch:css' 'npm run watch:js'" } 

parallelshell 允许我们同时执行多个任务,为什么不使用 && 呢?

因为 && 的执行顺序必须是前面的执行完了才轮到后面的任务,但是 watch 类型的任务永远不会完成啊~

现在 watch:all 任务开启服务器,并监听代码,一旦有变动,马上刷新浏览器 perfect!

Other useful tasks

npm 有很多其他任务。我们再写一个总的启动任务:

"scripts": {   ...   "postinstall": "npm run watch:all" } 

npm install 之后就执行一下 postinstall 吧,这在团队开发中是很好的体验,克隆你代码的人执行 npm install 然后 马上 watch: all

Wrap Up

噢,我们做到了(终于翻译完了,好长的文章。。。),希望看完这篇文章你能学会使用 npm scripts

对了,我把代码都放在 npm-build-boilerplate 上了,欢迎交流。

译者的废话

最近看到关于 npm scripts 的字眼比较多,然后就找了篇文章翻译。

本文根据@Damon Bauer的文章所译,整篇译文带有我们自己的理解和意思,如果有译得不好的地方或者不对之处,还请大家指点。英文出处:Why npm Scripts?

如需转载,请注明出处: http://w3ctrain.com/2016/02/27/why-npm-scripts/ ,欢迎加入前端Q群( 467969149 )