#0 系列目录#javascript
一个应用开发到必定阶段,广泛会遇到一个问题。当功能愈来愈多,代码量愈来愈大,bug修复愈来愈频繁,开发人员一波一波的交替,…..应该用会向着愈来愈不可控发展。咱们不能再准确估计新功能的开发时间,也不知道一个bug修复后是否会引起另外一个bug出现。全部的程序开发,都会面临着这样的问题。css
C/C++程序经过makefile管理编译测试打包的过程,Java程序经过Maven,Ant实现项目构建管理功能,Python有pip,Ruby有gem。在Nodejs的领域,咱们一样须要一个项目构建工具,这就是Grunt。Grunt能够执行像压缩, 编译, 单元测试, 代码检查以及打包发布的任务
。html
#1 Grunt介绍# Grunt是一个自动化的项目构建工具. 若是你须要重复的执行像压缩, 编译, 单元测试, 代码检查以及打包发布的任务. 那么你能够使用Grunt来处理这些任务, 你所须要作的只是配置好Grunt, 这样能很大程度的简化你的工做.java
若是在团队中使用Grunt, 你只须要与其余人员约定好使用Grunt应该规避的问题, 就可以很方便的自动化的处理大部分的常见工做任务, 你所付出的努力几乎为0.node
#2 Grunt安装# Grunt和Grunt插件都是经过npm, Node.js包管理器安装和管理的.jquery
##2.1 安装grunt-cli## grunt-cli并不grunt,grunt-cli的做用是管理本地各版本的grunt,让命令行能够直接执行grunt命令。git
下面全局安装grunt-cli(-g)github
~ D:\workspace\javascript>npm install -g grunt-cli D:\toolkit\nodejs\grunt -> D:\toolkit\nodejs\node_modules\grunt-cli\bin\grunt grunt-cli@0.1.9 D:\toolkit\nodejs\node_modules\grunt-cli ├── resolve@0.3.1 ├── nopt@1.0.10 (abbrev@1.0.4) └── findup-sync@0.1.2 (lodash@1.0.1, glob@3.1.21)
咱们看到grunt-cli彷佛作了一个软件连接,把grunt脚本复制到nodejs安装根目录里。web
##2.2 全局安装grunt##shell
~ D:\workspace\javascript>npm install -g grunt ~ D:\workspace\javascript>grunt grunt-cli: The grunt command line interface. (v0.1.9) Fatal error: Unable to find local grunt. If you're seeing this message, either a Gruntfile wasn't found or grunt hasn't been installed locally to your project. For more information about installing and configuring grunt, please see the Getting Started guide: http://gruntjs.com/getting-started
执行grunt命令,咱们发现系统报错了,提示不能加载本地库。由于,grunt命令执行,是须要当前目录中包括package.json和Gruntfile.js两个文件
。
package.json,是npm项目配置文件
Gruntfile.js,是专门用来配置grunt的配置文件
##2.3 建立一个express3的项目##
~ D:\workspace\javascript>express -e nodejs-grunt ~ D:\workspace\javascript>cd nodejs-grunt && npm install ~ D:\workspace\javascript\nodejs-grunt>npm install grunt --save-dev
安装-save-dev,就能够,直接把grunt做为devDependencies写入的package.json中。
~ vi package.json { "name": "nodejs-grunt", "version": "0.0.1", "private": true, "scripts": { "start": "node app.js" }, "dependencies": { "express": "3.2.2", "ejs": "*" }, "devDependencies": { "grunt": "~0.4.1", } }
而后,咱们再执行grunt,系统提示缺乏Gruntfile文件
~ D:\workspace\javascript\nodejs-grunt>grunt A valid Gruntfile could not be found. Please see the getting started guide for more information on how to configure grunt: http://gruntjs.com/getting-started Fatal error: Unable to find Gruntfile.
建立Gruntfile文件:
~ vi Gruntfile.js module.exports = function(grunt) { // Project configuration. grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), uglify: { options: { banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n' }, build: { src: 'src/<%= pkg.name %>.js', dest: 'build/<%= pkg.name %>.min.js' } } }); // Load the plugin that provides the "uglify" task. grunt.loadNpmTasks('grunt-contrib-uglify'); // Default task(s). grunt.registerTask('default', ['uglify']); };
再次运行grunt,这时提示是grunt-contrib-uglify包找不到,是Gruntfile.js配置文件中的错误了。
~ D:\workspace\javascript\nodejs-grunt>grunt >> Local Npm module "grunt-contrib-uglify" not found. Is it installed? Warning: Task "uglify" not found. Use --force to continue.
咱们编辑package.json, 在devDependencies中增长grunt-contrib-uglify的依赖库:
~ vi package.json { "name": "application-name", "version": "0.0.1", "private": true, "scripts": { "start": "node app.js" }, "dependencies": { "express": "3.2.2", "ejs": "*" }, "devDependencies": { "grunt": "~0.4.1" "grunt-contrib-uglify": "~0.1.1" } } ~ D:\workspace\javascript\nodejs-grunt>npm install
咱们建立两个目录src和build,和nodejs-grunt.js的文件
~ D:\workspace\javascript\nodejs-grunt>mkdir src ~ D:\workspace\javascript\nodejs-grunt>mkdir build ~ D:\workspace\javascript\nodejs-grunt>vi src/nodejs-grunt.js var sayHello = function(name){ return "Hello " + name; }
咱们再执行grunt:
~ D:\workspace\javascript\nodejs-grunt>grunt Running "uglify:build" (uglify) task File "build/nodejs-grunt.min.js" created. Uncompressed size: 59 bytes. Compressed size: 40 bytes gzipped (43 bytes minified). Done, without errors.
grunt运行正常,而且执行了uglify:build的任务。打开build/nodejs-grunt.min.js文件:
~ D:\workspace\javascript\nodejs-grunt>vi build/nodejs-grunt.min.js /*! nodejs-grunt 2013-08-17 */ var sayHello=function(l){return"Hello "+l};
#3 Grunt使用# 咱们能够经过help帮助,看一下grunt怎么用。
~ D:\workspace\javascript\nodejs-grunt>grunt --help Grunt: The JavaScript Task Runner (v0.4.1) Usage grunt [options] [task [task ...]] Options --help, -h Display this help text. --base Specify an alternate base path. By default, all file paths are relative to the Gruntfile. (grunt.file.setBase) * --no-color Disable colored output. --gruntfile Specify an alternate Gruntfile. By default, grunt looks in the current or parent directories for the nearest Gruntfile.js or Gruntfile.coffee file. --debug, -d Enable debugging mode for tasks that support it. --stack Print a stack trace when exiting with a warning or fatal error. --force, -f A way to force your way past warnings. Want a suggestion? Don't use this option, fix your code. --tasks Additional directory paths to scan for task and "extra" files. (grunt.loadTasks) * --npm Npm-installed grunt plugins to scan for task and "extra" files. (grunt.loadNpmTasks) * --no-write Disable writing files (dry run). --verbose, -v Verbose mode. A lot more information output. --version, -V Print the grunt version. Combine with --verbose for more info. --completion Output shell auto-completion rules. See the grunt-cli documentation for more information. Options marked with * have methods exposed via the grunt API and should instead be specified inside the Gruntfile wherever possible. Available tasks uglify Minify files with UglifyJS. * default Alias for "uglify" task. Tasks run in the order specified. Arguments may be passed to tasks that accept them by using colons, like "lint:files". Tasks marked with * are "multi tasks" and will iterate over all sub-targets if no argument is specified. The list of available tasks may change based on tasks directories or grunt plugins specified in the Gruntfile or via command-line options. For more information, see http://gruntjs.com/
有两方面是咱们须要注意的:
Options: grunt支持的命令
Available tasks: 当目录可执行的任务
#4 Grunt经常使用插件#
grunt-contrib-uglify:压缩js代码
grunt-contrib-concat:合并js文件
grunt-contrib-qunit:单元测试
grunt-contrib-jshint:js代码检查
grunt-contrib-watch:监控文件修改并从新执行注册的任务
grunt-contrib-uglify:是执行压缩JS代码的任务。
grunt-contrib-concat:是执行合并文件的任务。
插件安装及更新到配置
~ D:\workspace\javascript\nodejs-grunt>npm install grunt-contrib-concat --save-dev
修改Gruntfile.js文件:
grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), concat:{ options: { //定义一个字符串插入每一个文件之间用于链接输出 separator: ';' }, dist: { src: ['src/*.js'], dest: 'build/<%= pkg.name %>.cat.js' } }, }); grunt.loadNpmTasks('grunt-contrib-concat'); grunt.registerTask('default', ['uglify','concat']);
在src目录,新增长文件src/sayBye.js
~ vi src/sayBye.js var sayBye = function(name){ return "Bye " + name; }
执行concat任务
~ D:\workspace\javascript\nodejs-grunt>grunt concat Running "concat:dist" (concat) task File "build/nodejs-grunt.cat.js" created. Done, without errors.
查看生成的文件build/nodejs-grunt.cat.js
~ vi build/nodejs-grunt.cat.js var sayHello = function(name){ return "Hello " + name; };var sayBye = function(name){ return "Bye " + name; }
插件安装及更新到配置:
~ D:\workspace\javascript\nodejs-grunt>npm install grunt-contrib-qunit --save-dev
修改Gruntfile.js文件:
grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), qunit: { files: ['test/*.html'] } }); grunt.loadNpmTasks('grunt-contrib-qunit'); grunt.registerTask('default', ['uglify','concat','qunit']);
建立一个test目录,并编写用于测试的qunit.html文件:
~ mkdir test ~ vi test/qunit.html <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <link rel="stylesheet" href="http://github.com/jquery/qunit/raw/master/qunit/qunit.css" type="text/css" media="screen" /> <script type="text/javascript" src="http://github.com/jquery/qunit/raw/master/qunit/qunit.js"></script> <script> test("hello", function() { ok(true, "world"); }); </script> </head> <body> <h1 id="qunit-header">QUnit example</h1> <h2 id="qunit-banner"></h2> <h2 id="qunit-userAgent"></h2> <ol id="qunit-tests"></ol> </body> </html>
执行qunit命令:
~ D:\workspace\javascript\nodejs-grunt>grunt qunit Running "qunit:files" (qunit) task Testing test/qunit.html .OK >> 1 assertions passed (67ms) Done, without errors.
插件安装及更新到配置:
~ D:\workspace\javascript\nodejs-grunt>npm install grunt-contrib-jshint --save-dev
修改Gruntfile.js文件:
grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), jshint: { files: ['gruntfile.js', 'src/*.js', 'build/*.js'], options: { globals: { exports: true } } } }); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.registerTask('default', ['uglify','concat','qunit','jshint']);
执行jshint代码检查:
~ D:\workspace\javascript\nodejs-grunt>grunt jshint Running "jshint:files" (jshint) task Linting src/nodejs-grunt.js ...ERROR [L3:C2] W033: Missing semicolon. } Linting build/nodejs-grunt.cat.js ...ERROR [L5:C2] W033: Missing semicolon. } Linting build/nodejs-grunt.min.js ...ERROR [L2:C42] W033: Missing semicolon. var sayHello=function(l){return"Hello "+l}; Warning: Task "jshint:files" failed. Use --force to continue. Aborted due to warnings.
好多的错误啊,细看一下,都是”丢失分号”的错误。
~ vi src/sayBye.js var sayBye = function(name){ return "Bye " + name; };
插件安装及更新到配置:
~ D:\workspace\javascript\nodejs-grunt>npm install grunt-contrib-watch --save-dev
修改Gruntfile.js文件:
grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), watch: { files: ['<%= jshint.files %>'], tasks: ['jshint', 'qunit'] } }); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.registerTask('default', ['uglify','concat','qunit','jshint']);
执行watch任务:
~ D:\workspace\javascript\nodejs-grunt>grunt watch Running "watch" task Waiting...OK #手动修改src/sayBye.js文件,下面watch的任务被触发 >> File "src\sayBye.js" changed. Running "jshint:files" (jshint) task Linting src/sayBye.js ...ERROR [L3:C2] W033: Missing semicolon. } Linting build/nodejs-grunt.cat.js ...ERROR [L3:C3] W032: Unnecessary semicolon. };;var sayBye = function(name){ Linting build/nodejs-grunt.min.js ...ERROR [L2:C42] W033: Missing semicolon. var sayHello=function(l){return"Hello "+l}; Warning: Task "jshint:files" failed. Use --force to continue. Aborted due to warnings. Completed in 0.770s at Sat Aug 17 2013 20:49:15 GMT+0800 (中国标准时间) - Waiting...