前端构建和工作流工具种类繁多 : Grunt , Gulp , Broccoli和Jake等等。 这些工具几乎可以使您发现自己在项目中重复执行的几乎所有操作自动化,从缩小和连接源文件到运行测试或编译代码。 但是问题是,您是否需要它们? 您是否真的要为您的项目引入额外的依赖关系? 答案是不!”。 有一个免费的替代方案可以为您完成大多数任务,并且与Node.js捆绑在一起。 当然我在说npm 。
在本文中,我们将讨论npm可以用作构建工具的功能。 如果您想在开始前快速阅读npm,请参阅npm的初学者指南 。 如果您想继续学习,可以在GitHub上找到本文中使用的代码 。
npm脚本
为了开始讨论,我们将为新的演示项目创建目录,我们将其称为“ buildtool”。 完成后,我们将移至该文件夹,然后运行命令npm init
创建package.json
文件:
$ mkdir ~/buildtool && cd ~/buildtool
$ npm init
您将被问到几个问题。 随意跳过全部或部分内容,因为您将package.json
文件的最终内容替换为以下内容:
{
" name ": "buildtool" ,
" version ": "1.0.0" ,
" description ": "npm as a build tool" ,
" dependencies ": {} ,
" devDependencies ": {} ,
" scripts ": {
" info ": "echo 'npm as a build tool'"
} ,
" author ": "SitePoint" ,
" license ": "ISC"
}
正如你所看到的,我们有一个scripts
与对象property
称为info
。 info
的值将作为命令在Shell中执行。 通过运行以下命令,我们可以看到scripts
属性(也称为command )和在项目中定义的值的列表:
$ npm run
如果在我们的项目文件夹中运行上一个命令,则应看到以下结果:
Scripts available in buildtool via `npm run-script`:
info
echo 'npm as a build tool'
如果要运行特定属性,可以运行以下命令:
$ npm run <property>
因此,要运行我们在package.json
文件中定义的info
命令,我们必须编写:
$ npm run info
它将产生以下输出:
$ npm run info
> buildtool@ 1.0 . 0 info /home/sitepoint/buildtool
> echo 'npm as a build tool'
npm as a build tool
如果只需要info
的输出,则可以使用-s
标志, 使npm的输出静音 :
$ npm run info -s
npm as a build tool
到目前为止,我们仅使用了简单的echo
,但这是一个非常强大的功能。 命令行上的所有内容都对我们可用,我们在这里可以很有创造力。 因此,让我们以到目前为止所介绍的内容为基础,并安装一些packages
以创建一些通用的工作流程。
常见工作流程
我们要实现的第一件事是对JavaScript文件的整理功能。 这涉及到运行一个程序,该程序将分析我们的代码中是否存在潜在错误。 我们将为此使用JSHint ,因此第一步是通过npm安装软件包:
$ npm install jshint --save-dev
执行此命令后,您将看到一个名为node_modules
的新子文件夹。 这是JSHint的下载位置。 此外,我们还需要为项目创建以下文件夹结构:
├── assets
│ ├── css
│ │ └── main .css
│ └── scripts
│ └── main .js
├── dist
├── package .json
├── node_modules
└── test
└── test .js
在Unix系统上,可以使用以下命令完成此操作:
$ mkdir -p assets/css assets/scripts test && touch assets/css/main.css assets/scripts/main.js test/test.js
林亭
现在,我们将在main.js
文件中强加一些语法错误。 目前文件为空,因此将其打开并粘贴以下内容:
"use strict" ;
var Author = new function (name) {
this .name = name || "Anonymous" ;
this .articles = new Array ();
}
Author.prototype.writeArticle = function (title) {
this .articles.push(title);
};
Author.prototype.listArticles = function () {
return this .name + " has written: " + this .articles.join( ", " );
};
exports.Author = Author;
var peter = new Author( "Peter" );
peter.writeArticle( "A Beginners Guide to npm" );
peter.writeArticle( "Using npm as a build tool" );
peter.listArticles();
希望这段代码的意图很清楚-我们正在声明一个构造函数,其目的是创建新的Author
对象。 我们还将一些方法附加到Author
的prototype
属性上,这将使我们能够存储和列出作者编写的文章。 注意exports
语句,它将使我们的代码在定义它的模块之外可用。 如果您想了解更多有关此的信息,请务必阅读: 了解Node.js中的module.exports和exports 。
接下来,我们必须在package.json
的scripts
对象中添加一个property
,该property
将触发jshint
。 为此,我们将创建一个lint
属性,如下所示:
"scripts" : {
" info ": "echo 'npm as a build tool'" ,
" lint ": "echo '=> linting' && jshint assets/scripts/*.js"
}
在这里,我们利用&&
运算符来链接命令和文件通配符(星号),这些通配符被视为通配符,在这种情况下,它将匹配任何以script
目录结尾的.js
文件。
注意 :Windows命令行不支持glob,但是当给定命令行参数(例如*.js
,Windows会将其逐字传递给调用应用程序。 这意味着供应商可以安装兼容性库以提供Windows glob类功能。 JSHint为此使用minimatch库。
现在让我们看一下代码:
npm run lint -s
这将产生以下输出:
=> linting
assets/scripts/main.js: line 1 , col 1 , Use the function form of "use strict" .
assets/scripts/main.js: line 5 , col 28 , The array literal notation [] is preferable.
assets/scripts/main.js: line 3 , col 14 , Weird construction. Is 'new' necessary?
assets/scripts/main.js: line 6 , col 1 , Missing '()' invoking a constructor.
assets/scripts/main.js: line 6 , col 2 , Missing semicolon.
assets/scripts/main.js: line 16 , col 1 , 'exports' is not defined.
6 errors
有用。 让我们清理这些错误,重新运行linter来确保,然后进行一些测试:
( function () {
"use strict" ;
var Author = function (name) {
this .name = name || "Anonymous" ;
this .articles = [];
};
Author.prototype.writeArticle = function (title) {
this .articles.push(title);
};
Author.prototype.listArticles = function () {
return this .name + " has written: " + this .articles.join( ", " );
};
exports.Author = Author;
var peter = new Author( "Peter" );
peter.writeArticle( "A Beginners Guide to npm" );
peter.writeArticle( "Using npm as a build tool" );
peter.listArticles();
})();
注意我们如何将所有内容包装在立即调用的函数表达式中 。
npm run lint -s
=> linting
没有错误。 我们很好!
测试中
首先,我们需要安装mocha软件包 。 Mocha是针对Node.js和浏览器的简单但灵活的JavaScript测试框架。 如果您想了解更多有关本文的内容,那么这篇文章是个不错的起点: 使用Mocha&Chai进行基本前端测试
npm install mocha --save-dev
接下来,我们将创建一些简单的测试来测试我们先前编写的方法。 打开test.js
并添加以下内容(请注意require
语句,该语句使我们的代码可用于mocha):
var assert = require ( "assert" );
var Author = require ( "../assets/scripts/main.js" ).Author;
describe( "Author" , function () {
describe( "constructor" , function () {
it( "should have a default name" , function () {
var author = new Author();
assert.equal( "Anonymous" , author.name);
});
});
describe( "#writeArticle" , function () {
it( "should store articles" , function () {
var author = new Author();
assert.equal( 0 , author.articles.length);
author.writeArticle( "test article" );
assert.equal( 1 , author.articles.length);
});
});
describe( "#listArticles" , function () {
it( "should list articles" , function () {
var author = new Author( "Jim" );
author.writeArticle( "a great article" );
assert.equal( "Jim has written: a great article" , author.listArticles());
});
});
});
现在,将test
任务添加到package.json
:
"scripts": {
"info": "echo 'npm as a build tool'",
"lint": "echo '=> linting' && jshint assets/scripts/*.js",
"test": "echo '=> testing' && mocha test/"
}
npm有一些方便的快捷方式,即npm test
, npm start
和npm stop
。 这些都是它们的run
等效项的别名,这意味着我们只需要运行npm test
来使摩卡付诸行动:
$ npm test -s
=> testing
Author
constructor
✓ should have a default name
#writeArticle
✓ should store articles
#listArticles
✓ should list articles
3 passing ( 5 ms)
前后挂钩
如果我们要运行测试套件,并且由于语法错误而立即将其保释,那将不是很有效。 幸运的是,npm为我们提供了pre
挂钩和post
挂钩,因此,如果您运行npm run test
,它将在完成时首先执行npm run pretest
和npm run posttest
。 在这种情况下,我们要在test
脚本之前运行lint
脚本。 以下pretest
脚本使这成为可能。
"scripts" : {
" info ": "echo 'npm as a build tool'" ,
" lint ": "echo '=> linting' && jshint assets/scripts/*.js" ,
" test ": "echo '=> testing' && mocha test/" ,
" pretest ": "npm run lint -s"
}
想象一下,以前我们没有纠正脚本中的语法错误。 在这种情况下,上述pretest
脚本将因非零退出代码而失败,并且test
脚本将无法运行。 这正是我们想要的行为。
$ npm test -s
=> linting
assets/scripts/main.js: line 1 , col 1 , Use the function form of "use strict" .
...
6 errors
使用main.js
的更正代码:
=> linting
=> testing
Author
constructor
✓ should have a default name
#writeArticle
✓ should store articles
#listArticles
✓ should list articles
3 passing ( 6 ms)
我们是绿色的!
代码压缩
在本节中,我们需要将dist
目录以及几个子目录和文件添加到我们的项目中。 文件夹结构如下所示:
├── dist
│ └── public
│ ├── css
│ ├── index .html
│ └── js
在Unix机器上重新创建此命令的命令是:
mkdir -p dist/public/css dist/public/js && touch dist/public/index.html
index.html
的内容很简单。
<!DOCTYPE html>
< html >
< head >
< meta charset = "utf-8" />
< title > npm as a build tool </ title >
< link href = 'css/main.min.css' rel = 'stylesheet' >
</ head >
< body >
< h2 > npm as a build tool </ h2 >
< script src = 'js/main.min.js' > </ script >
</ body >
</ html >
当前main.js
尚未缩小。 这是应该的,因为它是我们正在处理的文件,我们需要能够读取它。 但是,在将其上传到实时服务器之前,我们需要减小其大小并将其放置在dist/public/js
目录中。 为此,我们可以安装uglify-js软件包并创建一个新脚本。
$ npm install uglify-js --save-dev
现在,我们可以在package.json
创建一个新的minify:js
脚本:
"scripts" : {
" info ": "echo 'npm as a build tool'" ,
" lint ": "echo '=> linting' && jshint assets/scripts/*.js" ,
" test ": "echo '=> testing' && mocha test/" ,
" minify:js ": "echo '=> minify:js' && uglifyjs assets/scripts/main.js -o dist/public/js/main.min.js" ,
" pretest ": "npm run lint -s"
}
运行:
$ npm run minify:js -s
=> minify:js
脚本会在正确的目标位置创建文件的缩小版本。 我们将使用clean-css包对CSS文件执行相同的操作。
$ npm install clean-css --save-dev
并创建minify:css
脚本。
"scripts" : {
" info ": "echo 'npm as a build tool'" ,
" lint ": "echo '=> linting' && jshint assets/scripts/*.js" ,
" test ": "echo '=> testing' && mocha test/" ,
" minify:js ": "echo '=> minify:js' && uglifyjs assets/scripts/main.js -o dist/public/js/main.min.js" ,
" minify:css ": "echo '=> minify:css' && cleancss assets/css/main.css -o dist/public/css/main.min.css" ,
" pretest ": "npm run lint -s"
}
让我们run
脚本。
$ npm run minify:css -s
=> minify:css
留意变化
Grunt,Gulp和他们的同龄人最擅长的事情之一是,观察一组文件,并在检测到其中任何一个文件已更改时重新运行特定任务。 这在诸如此类的情况下特别有用,因为手动重新运行缩小脚本会很麻烦。
好消息是,您也可以在npm中使用watch这样的软件包来执行此操作,该软件包旨在使对文件和目录树的监视的管理更加容易。
$ npm install watch --save-dev
然后在package.json中,您需要指定检测到更改时要运行的任务。 在这种情况下,JavaScript和CSS最小化:
"scripts" : {
...
" watch ": "watch 'npm run minify:js && npm run minify:css' assets/scripts/ assets/css/"
}
使用以下命令启动脚本:
$ npm run watch
现在,每当assets/scripts/
或assets/css/
任何文件更改时,都会自动调用缩小脚本。
构建脚本
到现在为止,我们已经可以将几个脚本链接在一起以创建一个build
脚本,该脚本应该执行以下操作:整理,测试和缩小。 毕竟,一次又一次地单独运行这些任务会很痛苦。 要创建此构建脚本,请更改package.json
的脚本对象,从而:
"scripts" : {
" info ": "echo 'npm as a build tool'" ,
" lint ": "echo '=> linting' && jshint assets/scripts/*.js" ,
" test ": "echo '=> testing' && mocha test/" ,
" minify:js ": "echo '=> minify:js' && uglifyjs assets/scripts/main.js -o dist/public/js/jquery.min.js" ,
" minify:css ": "echo '=> minify:css' && cleancss assets/css/main.css -o dist/public/css/main.min.css" ,
" build ": "echo '=> building' && npm run test -s && npm run minify:js -s && npm run minify:css -s" ,
" pretest ": "npm run lint -s"
}
运行build
脚本将为我们提供以下输出。
$ npm run build -s
=> building
=> linting
=> testing
Author
constructor
✓ should have a default name
#writeArticle
✓ should store articles
#listArticles
✓ should list articles
3 passing ( 6 ms)
=> minify:js
=> minify:css
服务器脚本
运行build
脚本后,如果可以在dist
启动内容服务器并在浏览器中进行检查,那就太好了。 我们可以使用http-server软件包来做到这一点。
$ npm install http-server -save-dev
我们制作一个server
脚本。
"scripts" : {
...
" server ": "http-server dist/public/" ,
}
现在我们可以run
服务器了。
$ npm run server
Starting up http-server, serving dist/public/ on: http://0.0.0.0:8080
Hit CTRL-C to stop the server
_
当然可以将server
脚本添加到build
脚本中,但是我将其作为练习留给读者。
结论
希望本文已经证明npm作为构建工具可以多么灵活和强大。 下次启动新项目时,请尽量不要直接使用Gulp或Grunt等工具-尝试仅使用npm来解决您的需求。 您可能会感到惊喜。
如果您有任何问题或意见,我们很高兴在下面的帖子中听到。
From: https://www.sitepoint.com/guide-to-npm-as-a-build-tool/