Yeoman 生成的 Angular 脚手架提供了 27 个任务配置和 3 个自定义任务。这三个自定义任务分贝为:
build 编译产品化的版本。
serve 编译,让后启动一个 web 服务器。
test 执行应用的单元测试。
default 构建一个优化过的,产品化的应用版本。
module .exports = function (grunt) {
grunt.registerTask( 'serve' , 'Compile then start a connect web server' , function (target) {
if (target === 'dist' ) {
return grunt.task.run([ 'build' , 'connect:dist:keepalive' ]);
}
grunt.task.run([
'clean:server' ,
'wiredep' ,
'concurrent:server' ,
'autoprefixer' ,
'connect:livereload' ,
'watch'
]);
});
grunt.registerTask( 'test' , [
'clean:server' ,
'concurrent:test' ,
'autoprefixer' ,
'connect:test' ,
'karma'
]);
grunt.registerTask( 'build' , [
'clean:dist' ,
'wiredep' ,
'useminPrepare' ,
'concurrent:dist' ,
'autoprefixer' ,
'concat' ,
'ngAnnotate' ,
'copy:dist' ,
'cdnify' ,
'cssmin' ,
'uglify' ,
'filerev' ,
'usemin' ,
'htmlmin'
]);
grunt.registerTask( 'default' , [
'newer:jshint' ,
'test' ,
'build'
]);
}
serve
grunt.registerTask( 'serve' , 'Compile then start a connect web server' , function (target) {
if (target === 'dist' ) {
return grunt.task.run([ 'build' , 'connect:dist:keepalive' ]);
}
grunt.task.run([
'clean:server' ,
'wiredep' ,
'concurrent:server' ,
'autoprefixer' ,
'connect:livereload' ,
'watch'
]);
});
如果 serve 的目标是 dist ,那么就运行 build,并且为 build 后的版本启动 web 服务器,这个服务器是持久运行的。主要用于测试发布版本。
在调试阶段,一般使用非 dist 目标,这个目标运行的任务为:
clean:server,
wiredep,
concurrent:server,
autoprefixer,
connect:livereload,
watch
clean:server
gruntjs/grunt-contrib-clean 用于清除文件和文件夹。
clean 的任务配置为:
clean: {
dist: {
files: [{
dot: true ,
src: [
'.tmp' ,
'<%= yeoman.dist %>/{,*/}*' ,
'!<%= yeoman.dist %>/.git*'
]
}]
},
server: '.tmp'
}
serve 任务只是用了 clean:server 目标,即删除 .tmp 目录。
wiredep
stephenplusplus/grunt-wiredep 插件基于 taptapship/wiredep 。用来根据 bower.json 在指定文件的占位符中注入 JavaScript 或者 CSS 依赖。
wiredep 的配置为:
wiredep: {
app: {
src: [ '<%= yeoman.app %>/index.html' ],
ignorePath: /..//
}
}
index.html 中两个占位符:
运行 grunt:wiredep 后,这两个占位符会根据 bower.json 中的依赖关系正确填充,如;
< link rel = "stylesheet" href = "bower_components/bootstrap/dist/css/bootstrap.css" />
< script src = "bower_components/jquery/dist/jquery.js" >
< script src = "bower_components/angular/angular.js" >
< script src = "bower_components/angular-touch/angular-touch.js" >
< script src = "bower_components/angular-bootstrap/ui-bootstrap-tpls.js" >
< script src = "bower_components/angular-ui-router/release/angular-ui-router.js" >
< script src = "bower_components/bootstrap/dist/js/bootstrap.js" >
bower.json 为:
{
" name ": "argo" ,
" version ": "0.0.0" ,
" dependencies ": {
" angular ": "~1.2.0" ,
" angular-touch ": "~1.2.0" ,
" angular-bootstrap ": "~0.11.2" ,
" angular-ui-router ": "~0.2.11" ,
" bootstrap ": "~3.2.0"
},
" devDependencies ": {
" angular-mocks ": "~1.2.0" ,
" angular-scenario ": "~1.2.0"
},
" appPath ": "app"
}
concurrent:server
sindresorhus/grunt-concurrent 用于运行并行任务。对于耗时的任务(如 Coffee and Sass)或者运行 多个阻塞任务 (如 nodemon and watch ) 时很有用。
concurrent 任务配置为:
concurrent: {
server: [
'copy:styles'
]
}
concurrent:server 目标中,并行运行 copy:styles 。
copy:styles
gruntjs/grunt-contrib-copy 用于拷贝文件。
copy: {
styles: {
expand: true ,
cwd: '<%= yeoman.app %>/styles' ,
dest: '.tmp/styles/' ,
src: '{,*/}*.css'
}
}
expand: true 是为了启用扩展属性, cwd 修改 dest 和 src 的相对路径。所以, style 目标的作用是,将 <%= yeoman.app %>/styles 以及下级目录中的所有 css 文件以平板方式拷贝到 .tmp/styles/ 。
autoprefixer
nDmitry/grunt-autoprefixer 使用Can I Use数据库为 CSS 添加前缀。
options.browsers 用于指定浏览器的版本,如 ‘last 1 version’ 指定各浏览器的最后一个版本。
autoprefixer: {
options: {
browsers: [ 'last 1 version' ]
},
dist: {
files: [{
expand: true ,
cwd: '.tmp/styles/' ,
src: '{,*/}*.css' ,
dest: '.tmp/styles/'
}]
}
}
dist 目标表示为 .tmp/styles/ 目录以及下级目录中的所有 css 文件添加前缀,然后覆盖原文件。
connect:livereload
gruntjs/grunt-contrib-connect 用于启动一个静态 web 服务器。
connect: {
options: {
port: 9000 ,
// Change this to '0.0.0.0' to access the server from outside.
hostname: 'localhost' ,
livereload: 35729
},
livereload: {
options: {
open: true ,
middleware: function (connect) {
return [
connect.static( '.tmp' ),
connect().use(
'/bower_components' ,
connect.static( './bower_components' )
),
connect.static(appConfig.app)
];
}
}
}
options.hostname 和 options.port 用于指定服务器的主机名和端口。 options.hostname 默认为 0.0.0.0 ,可以设置为 ‘*’,让它可以从任何地方访问。
options.open 指定为 true 时表示打开默认的服务器 URL,指定为字符串时,则打开该字符串指定的地址。
options.livereload 设置为 true 或者端口号,表示使用 connect-livereload 在你的页面中注入 live reload 脚本。这不会执行 live reloading,它试图和 grunt-contrib-watch 或者其他根据文件的修改触发 live reload 的服务器结合使用。
options.keepalive 设置为 true 表示让服务器无限期的执行。而这个任务之后的其任务不再执行。默认情况下,grunt 任务执行完成,web 服务器会停止。
options.middleware 类型为 Function 或者数组,用来添加 Connect 中间件。如果是函数,该函数返回中间件数组。Default: Array of connect middlewares that use options.base for static files and directory browsing,如:
connect: {
server: {
options: {
middleware: [
function myMiddleware (req, res, next) {
res.end( 'Hello, world!' );
}
],
},
},
}
我们的例子中, connect.livereload 目标中使用的是函数方式,返回 3 个使用 connect.static 方法构建的中间件。分别把 .tmp , /bower_components , appConfig.app 作为 static 资源。
watch
gruntjs/grunt-contrib-watch 用于监视文件的变化,然后运行指定的任务。
watch: {
bower: {
files: [ 'bower.json' ],
tasks: [ 'wiredep' ]
},
js: {
files: [ '<%= yeoman.app %>/scripts/{,*/}*.js' ],
tasks: [ 'newer:jshint:all' ],
options: {
livereload: '<%= connect.options.livereload %>'
}
},
jsTest: {
files: [ 'test/spec/{,*/}*.js' ],
tasks: [ 'newer:jshint:test' , 'karma' ]
}
styles: {
files: [ '<%= yeoman.app %>/styles/{,*/}*.css' ],
tasks: [ 'newer:copy:styles' , 'autoprefixer' ]
},
gruntfile: {
files: [ 'Gruntfile.js' ]
},
livereload: {
options: {
livereload: '<%= connect.options.livereload %>'
},
files: [
'<%= yeoman.app %>/{,*/}*.html' ,
'.tmp/styles/{,*/}*.css' ,
'<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
]
}
}
options.livereload 目标中通过 options.livereload 启用实时加载,使用的是 connect 任务的 livereload 配置。当 livereload.files 中指定的文件发生变化时,live reloading 服务器就会被触发。See also how to enable livereload on your HTML . 在这里,我们是通过 connect.livereload 目标为页面注入 live reloading 脚本。
除了 livereload 之外,还有
bower 监视 bower.json 文件,一旦文件发生变化,运行 wiredep 任务。
js 监视 <%= yeoman.app %>/scripts/{,*/} 下的所有 js 文件,如果有变化,则运行 newer:jshint:all 任务,而且会触发 live reloading 服务器。
jsTest 监视 test/spec/{,*/} 下的 js 文件,如果有变化,则运行 ‘newer:jshint:test’ 和 ‘karma’ 两个任务。
styles 监视 <%= yeoman.app %>/styles/ 下的文件,如果有变化,运行 ‘newer:copy:styles’ 和 ‘autoprefixer’ 两个任务。
gruntfile 监视 Gruntfile 文件,没有任务。
newer
tschaub/grunt-newer 配置任务只对新的文件运行任务。 newer 任务不要求特殊的配置,你只需要在任务前加上 newer: 。
对于同时有 src 和 dest 的任务, src 中的文件的修改时间会和 dest 中修改时间比较,如果有一个多多个更新的文件,则任务会重新运行,如:
grunt.initConfig({
uglify: {
all: {
files: {
'dest/app.min.js' : [ 'src/**/*.js' ]
}
}
}
});
grunt.loadNpmTasks( 'grunt-contrib-uglify' );
grunt.loadNpmTasks( 'grunt-newer' );
grunt.registerTask( 'minify' , [ 'newer:uglify:all' ]);
minify 任务只有在 src/**/*.js 中的一个或多个文件比 dest/app.min.js 更新时, uglify:all 任务才会运行。
对于只有 src 没有 dest 的配置, newer 只会使用比上次成功运行的时间新的文件。如:
jshint: {
options: {
jshintrc: '.jshintrc'
},
all: {
src: 'src/**/*.js'
}
}
grunt.registerTask( 'lint' , [ 'newer:jshint:all' ]);
第一次运行 grunt lint 时, jshint:newer:all 会使用所有 src 下的文件,之后,只有你修改过的文件才会运行 jshint 。
结合 watch 使用时,当 src 中的文件变化时,会使用新的文件运行任务,如:
js: {
files: [ '<%= yeoman.app %>/scripts/{,*/}*.js' ],
tasks: [ 'newer:jshint:all' ],
options: {
livereload: '<%= connect.options.livereload %>'
}
}
当 files 中的文件有变化时,针对新的文件运行指定的任务,如这里的 jshint:all 。
build
build 任务由以下 14 个子任务组成。
grunt.registerTask( 'build' , [
'clean:dist' ,
'wiredep' ,
'useminPrepare' ,
'concurrent:dist' ,
'autoprefixer' ,
'concat' ,
'ngAnnotate' ,
'copy:dist' ,
'cdnify' ,
'cssmin' ,
'uglify' ,
'filerev' ,
'usemin' ,
'htmlmin'
]);
清除 .tmp 和 <%= yeoman.dist %>/{,*/}* ,但保留 <%= yeoman.dist %>/.git* 文件或者文件夹。
根据 bower 的依赖关系替换 index.html 中的占位符。
利用 useminPrepare 任务生成的配置合并文件,指定输出路径为 <%= yeoman.dist %> 。包括 bower_components 中的 js 和 css,以及 .temp/ 下用户自定义的 css 以及 .tmp/ 或者 app/ 下的 js。
并行运行 concurrent:dist 目标中的三个子任务:
将 <%= yeoman.app %>/styles 以及下级目录中的所有 css 文件拷贝到 .tmp/styles/ 。
将 <%= yeoman.app %>/images 中的图片压缩到 <%= yeoman.dist %>/images 中。
将 <%= yeoman.app %>/images/{,*/}/ 下的所有 svg 图片压缩到 <%= yeoman.dist %>/images 中。
.tmp/styles/ 目录以及下级目录中的所有 css 文件添加前缀,然后覆盖原文件。
使用的是 useminPrepare 任务生成的配置合并 css 和 js 文件。
将 index.html 中有 wiredep 插入的 css 合并为 .tmp/styles/vendor.css 文件中。
将 中的 css (路径为 .temp 目录下)合并到 .tmp/styles/main.css 中。
将 wiredep 插入的 js 文件合并到 .tmp/scripts/vendor.js 中。
将 中的 js 文件(存在于 .tmp 或者 app 目录下)合并到 .temp/scripts/scripts.js 中。
将 .tmp/concat/scripts 目录下除了 oldieshim.js 之外的所有 js 文件加上 angular 注解后覆盖原来的文件。
将 <%= yeoman.app %>/ 的所有 html、图片、字体文件、.htaccess 拷贝到 <%= yeoman.dist %> 下。
将 <%= yeoman.dist %>/*.html 中的本地路径修改为 CDN 路径。
使用 useminPrepare 生成的配置最小化 CSS。压缩后置于 '<%= yeoman.dist %>'/styles/ 中。
使用 useminPrepare 生成的配置混淆 JavaScript。混淆后置于 '<%= yeoman.dist %>'/scripts 中。
使用 filerev 重命名 <%= yeoman.dist %>/ 下所有 js、css、图片和字体文件。
将 <%= yeoman.dist %>/{,*/}*.html 和 <%= yeoman.dist %>/styles/{,*/}*.css 中的 usemin 块替换为 '<%= yeoman.dist %>','<%= yeoman.dist %>/images' 中优化后并且 revved 的版本。
压缩 <%= yeoman.dist %> 下的所有 html 以及视图文件夹所有的 html,将压缩后的文件置于 <%= yeoman.dist %>/ 下。
clean:dist
清除 .tmp 和 <%= yeoman.dist %>/{,*/}* ,但保留 <%= yeoman.dist %>/.git* 文件或者文件夹。
clean: {
dist: {
files: [{
dot: true ,
src: [
'.tmp' ,
'<%= yeoman.dist %>/{,*/}*' ,
'!<%= yeoman.dist %>/.git*'
]
}]
},
server: '.tmp'
}
wiredep
根据 bower 的依赖关系替换 index.html 中的占位符。
useminPrepare
根据 <%= yeoman.app %>/index.html 中的 usemin 块生成 JS 和 CSS 的压缩配置,并指定输出路径为 <%= yeoman.dist %> 。
useminPrepare: {
html: '<%= yeoman.app %>/index.html' ,
options: {
dest: '<%= yeoman.dist %>' ,
flow: {
html: {
steps: {
js: [ 'concat' , 'uglifyjs' ],
css: [ 'cssmin' ]
},
post: {}
}
}
}
}
index.html 中 usemin 块:
< link rel = "stylesheet" href = "styles/main.css" >
useminPrepare 为实现优化修改 Grunt 配置,定义 js 的优化流程为 concat —> uglifyjs ,css 的优化流程为 cssmin 。优化的 html 文件为 <%= yeoman.app %>/index.html ,优化后的文件路径为 <%= yeoman.dist %> 。
将 index.html 中有 wiredep 插入的 css 合并为 .tmp/styles/vendor.css 文件中。将 中的 css (路径为 .temp 目录下)合并到 .tmp/styles/main.css 中。压缩后放到 <%= yeoman.dist %>/
将 wiredep 插入的 js 文件合并到 .tmp/scripts/vendor.js 中,将进一步混淆后的文件生成到 <%= yeoman.dist %>/scripts/vendor.js 中。将 中的 js 文件(存在于 .tmp 或者 app 目录下)合并到 .temp/scripts/scripts.js 中,混淆后的文件生成到 <%= yeoman.dist %>/scripts/scripts.js 中。
concurrent:dist
并行运行 concurrent:dist 目标中的三个子任务。
concurrent: {
dist: [
'copy:styles' ,
'imagemin' ,
'svgmin'
]
}
copy:styles 将 <%= yeoman.app %>/styles 以及下级目录中的所有 css 文件拷贝到 .tmp/styles/ 。
imagemin 将 <%= yeoman.app %>/images 中的图片压缩到 <%= yeoman.dist %>/images 中。
svgmin 将 <%= yeoman.app %>/images/{,*/}/ 下的所有 svg 图片压缩到 <%= yeoman.dist %>/images 中。
imagemin
使用 gruntjs/grunt-contrib-imagemin 进行图片压缩。
imagemin: {
dist: {
files: [
{
expand: true ,
cwd: '<%= yeoman.app %>/images' ,
src: '{,*/}*.{png,jpg,jpeg,gif}' ,
dest: '<%= yeoman.dist %>/images'
}
]
}
}
压缩的图片存在于 <%= yeoman.app %>/images 压缩后的图片放置在 <%= yeoman.dist %>/images 。所以这个任务的目的是将 <%= yeoman.app %>/images 中的图片压缩到 <%= yeoman.dist %>/images 中。
svgmin
sindresorhus/grunt-svgmin 用来压缩 SVG。
svgmin: {
dist: {
files: [{
expand: true ,
cwd: '<%= yeoman.app %>/images' ,
src: '{,*/}*.svg' ,
dest: '<%= yeoman.dist %>/images'
}]
}
}
将 <%= yeoman.app %>/images/{,*/}/ 下的所有 svg 图片压缩到 <%= yeoman.dist %>/images 中。
autoprefixer
dist 目标表示为 .tmp/styles/ 目录以及下级目录中的所有 css 文件添加前缀,然后覆盖原文件。
concat
gruntjs/grunt-contrib-concat 用于拼接文件。
我们这里使用的是 useminPrepare 任务生成的配置,合并后的文件生成到临时目录( .tmp )目录下。
ngAnnotate
mzgol/grunt-ng-annotate 用来将 Angular 添加、删除或者重建 Angular 中的依赖注入。
有如下 Angular 模块,在未标注之前:
angular.module( "MyMod" ).controller( "MyCtrl" , function (
});
标注之后:
angular.module( "MyMod" ).controller( "MyCtrl" , [ "scope, timeout) {
}]);
添加标注后可以安全地对代码进行压缩。
我们的 ngAnnotate 的任务配置如下:
ngAnnotate: {
dist: {
files: [{
expand: true ,
cwd: '.tmp/concat/scripts' ,
src: [ '*.js' , '!oldieshim.js' ],
dest: '.tmp/concat/scripts'
}]
}
}
将 .tmp/concat/scripts 目录下除了 oldieshim.js 之外的所有 js 文件加上注解后覆盖原来的文件。
copy:dist
copy: {
dist: {
files: [{
expand: true ,
dot: true ,
cwd: '<%= yeoman.app %>' ,
dest: '<%= yeoman.dist %>' ,
src: [
'*.{ico,png,txt}' ,
'.htaccess' ,
'*.html' ,
'views/{,*/}*.html' ,
'images/{,*/}*.{webp}' ,
'fonts/*'
]
}, {
expand: true ,
cwd: '.tmp/images' ,
dest: '<%= yeoman.dist %>/images' ,
src: [ 'generated/*' ]
}]
}
}
将 <%= yeoman.app %>/ 的所有 html、图片、字体文件、.htaccess 拷贝到 <%= yeoman.dist %> 下。
cdnify
callumlocke/grunt-cdnify 将指定文件中的本地路径修改为 CDN 路径,CDN 可以通过 options.base 配置,默认为 Google 提供的 CDN。
cdnify: {
dist: {
html: [ '<%= yeoman.dist %>/*.html' ]
}
}
将 <%= yeoman.dist %>/*.html 中的本地路径修改为 CDN 路径。
cssmin
使用 useminPrepare 生成的配置,你也可以自定义:
cssmin: {
dist: {
files: {
'<%= yeoman.dist %>/styles/main.css' : [
'.tmp/styles/{,*/}*.css'
]
}
}
}
uglify
使用 useminPrepare 生成的配置,混淆后的文件默认生成到通过 useminPrepare 中指定的目标目录。
你也可以自定义:
uglify: {
dist: {
files: {
'<%= yeoman.dist %>/scripts/scripts.js' : [
'<%= yeoman.dist %>/scripts/scripts.js'
]
}
}
}
filerev
yeoman/grunt-filerev 通过文件内容的哈希重命名文件名。
// Renames files for browser caching purposes
filerev: {
dist: {
src: [
'<%= yeoman.dist %>/scripts/{,*/}*.js' ,
'<%= yeoman.dist %>/styles/{,*/}*.css' ,
'<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}' ,
'<%= yeoman.dist %>/styles/fonts/*'
]}
}
使用 filerev 重命名 <%= yeoman.dist %>/ 下所有 js、css、图片和字体文件。
usemin
usemin: {
html: [ '<%= yeoman.dist %>/{,*/}*.html' ],
css: [ '<%= yeoman.dist %>/styles/{,*/}*.css' ],
options: {
assetsDirs: [ '<%= yeoman.dist %>' , '<%= yeoman.dist %>/images' ]}
}
usemin 使用 useminPrepare 阶段准备的配置优化,将 <%= yeoman.dist %>/{,*/}*.html 和 <%= yeoman.dist %>/styles/{,*/}*.css 中的 usemin 块替换为 '<%= yeoman.dist %>','<%= yeoman.dist %>/images' 中优化后并且 revved 的版本。 options.assetsDirs 重定义了 revved 版本的查找路径。
htmlmin
gruntjs/grunt-contrib-htmlmin 压缩 html。
htmlmin: {
dist: {
options: {
collapseWhitespace: true ,
conservativeCollapse: true ,
collapseBooleanAttributes: true ,
removeCommentsFromCDATA: true ,
removeOptionalTags: true},
files: [{
expand: true ,
cwd: '<%= yeoman.dist %>' ,
src: [ '*.html' , 'views/{,*/}*.html' ],
dest: '<%= yeoman.dist %>'
}]
}
}
压缩 <%= yeoman.dist %> 下的所有 html 以及视图文件夹所有的 html,将压缩后的文件置于 <%= yeoman.dist %>/ 下。