受益于grunt这么久,继续分享关于grunt的一些技巧。grunt确实是前端项目中不可或缺的提升效率的工具。第一次接触grunt是在去年7月份,开始有接触LESS、Coffee Script的等需要编译的模板才能使用的,所以grunt就有了很大的用处。当然除了编译,还有一部分的工作就是压缩,grunt常用的任务就是压缩JS、CSS,检查语法错误,同时也可以保证质量压缩图片(删除图片多余信息)。
使用起来也很简单,基于node,所以我们就可以通过js来控制这些文件。唯一需要做的是编写配置文件,做一套适合我们项目的编译系统。除此之外,另一个很方便的功能就是grunt能够通过监听文件变化(创建、删除、保存)来执行相应的任务。在初次学习grunt的配置的时候,踩过许多坑,之前也写过两篇关于grunt的文章,所以这里就写下Grunt的配置写法,具体为何那么写以及需要注意的点已经在代码里面表明清楚。
示范
写法仅供参考,具体请结合自己的项目编写配置。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
|
module.exports = function(grunt){
// 构建配置任务
grunt.initConfig({
//读取package.json的内容,形成个json数据
pkg: grunt.file.readJSON('package.json'),
// 复制
copy: {
// 指定子任务,调用可以是grunt copy(执行copy里面的全部任务),grunt copy:build(执行copy里面的build任务)
build: {
cwd: 'js', //指向的目录是相对的,全称Change Working Directory更改工作目录
src: ['**'], //指向源文件,**是一个通配符,用来匹配Grunt任何文件
dest: 'images', //用来输出结果任务
expand: true //expand参数为true来启用动态扩展,涉及到多个文件处理需要开启
},
// 注:如果src: [ '**', '!**/*.styl' ],表示除去.styl文件,!在文件路径的开始处可以防止Grunt的匹配模式
},
// 清除
clean: {
build: {
src: ['css/**/*.*']
},
},
less: {
dynamic_mappings: {
files: [{
expand: true, // Enable dynamic expansion.
cwd: 'build/less', // Src matches are relative to this path.
src: ['**/*.less', '!**/header.less', '!**/sidebar.less', '!**/footer.less', '!**/reset.less', '!**/layout.less', '!**/nprogress.less', '!**/post.less', '!**/single.less'], // Actual pattern(s) to match.
dest: 'css', // Destination path prefix.
ext: '.css', // Dest filepaths will have this extension.
}],
},
},
// CSS压缩
cssmin: {
build: {
expand: true,
cwd: 'css/',
src: ['*.css', '!*.min.css'],
dest: 'css/',
ext: '.css'
}
},
// 压缩js
uglify: {
// 基本压缩(用于不常修改的文件)
build: {
files: [{
expand: true,
cwd: 'build/js',
src: ['*.js', '!**/component.js', '!**/jquery.js', '!**/html5.js'],
dest: 'js/'
}],
},
// public(常修改维护的文件)
publicJs: {
files: {
'js/public.js': ['build/js/public.js']
}
},
// 组件压缩(组件级别,一般仅压缩一次)
component: {
options: {
mangle: false // false表示关闭短命名方式压缩。如果文件要共享到另一个项目中,会带来问题,因为名称已改变
},
files: {
'js/component.js': [ 'build/js/component/piano_storage.js']
},
},
},
// JS语法检查
jshint: {
all: ['js/*.js'],
},
// 监听(监测到文件改变时执行对应任务)
watch: {
stylesheets: {
files: 'build/less/*.less',
tasks: [ 'stylesheets' ]
},
publicJs: {
files: 'build/js/public.js',
tasks: [ 'uglify:publicJs' ],
},
scripts: {
files: ['build/js/*.js', '!build/js/**/public.js' ],
tasks: [ 'uglify:build' ],
},
componentJS: {
files: ['build/js/component/*.js'],
tasks: [ 'uglify:component' ],
}
},
// initConfig结尾
});
// 加载任务-分开加载
grunt.loadNpmTasks("grunt-contrib-copy");
grunt.loadNpmTasks("grunt-contrib-less");
grunt.loadNpmTasks("grunt-contrib-jshint");
grunt.loadNpmTasks("grunt-contrib-uglify");
grunt.loadNpmTasks("grunt-contrib-watch");
grunt.loadNpmTasks("grunt-contrib-clean");
grunt.loadNpmTasks("grunt-contrib-cssmin");
// 把grunt-contrib插件全部一次性加载
// grunt.loadNpmTasks('grunt-contrib');
// grunt.event.on('watch', function(action, filepath) {
// grunt.config(['uglify', 'build'], filepath);
// });
grunt.event.on('watch', function(action, filepath) {
grunt.config(['jshint', 'all'], filepath);
});
// 自定义任务
// 作用:将以上众多子任务和在一起,便于手工运行或定义watch的任务
// 处理CSS
grunt.registerTask(
'stylesheets',
'Compiles the stylesheets.',
// [ 'less' ]
[ 'less', 'cssmin' ]
);
// 处理JS
grunt.registerTask(
'scripts',
'Compiles the JavaScript files.',
[ 'uglify:publicJs' ]
);
// 处理public
grunt.registerTask(
'publicJs',
'Compiles the JavaScript files.',
[ 'uglify:publicJs' ]
);
// componentJS
grunt.registerTask(
'componentJS',
'Compiles the JavaScript files.',
[ 'uglify:componentJS' ]
);
// 创建工程
grunt.registerTask(
'build', //任务名称
'Compiles all of the assets and copies the files to the build directory.', //任务描述
[ 'clean', 'copy', 'stylesheets', 'scripts', 'jade' ] //将要运行的任务数组,按顺序执行
);
// 默认工程
grunt.registerTask(
'default',
'Watches the project for changes, automatically builds them and runs a server.',
[ 'build', 'connect', 'watch' ]
);
// default任务运行build创建一个初始的build,然后它开始连接服务器,最后它会运行watch,监测文件变化和重新构建。
// 因为watch一直在运行,所以服务器一直在运行。在你的控制台上运行grunt,然后到http://localhost:4000查看你的项目。
//modules结尾
};
|
方案
本站的JS在第一版本的主题中,没有规范的整理,CSS也算是。第二版本的主题进行了优化,包括使用LESS,高度压缩JS、CSS,因为使用lESS,使得样式组件化,很多代码得到重用。而JS的组织更加趋于模块化管理(后续会继续改进,如使用seajs)。
在编写Grunt配置文件中,出现过一些问题,下面列一下
1. 各个插件的写法
因为插件众多,而各插件的写法也不一致,而且插件也都是英文的,所以读起来略繁琐。但是我们能做的就是在readme.md中查看每个用到的插件的语法,多试几个例子就好了。
2. 插件加载问题
因为Grunt是基于node的,所以加载插件的时候,我们不需要指定具体的路径,只需要放在node_modules文件夹下,嵌套多深也不要紧,只要是在node_modules里面就行。因为github上有这么一个项目grunt-contrib是收集了基本上常用到的Grunt插件,刚开始接触Grunt的时候,不知道选择什么插件,可以这样clone引用。但是会带来一个效率的问题,引用的时候:grunt.loadNpmTasks(‘grunt-contrib’);,因为涉及到的文件(文件夹)过多,会带来效率上的问题。所以仅需要当加载我们需要的插件就好。Grunt常用插件介绍:http://www.xuanfengge.com/grunt-commonly-used-plug-in-introduced.html
3. watch任务问题
watch给我们带来了很多方便,但是有时候也是个累赘。虽然说Grunt基于node,执行效率比Ant高很多,但是随着项目的庞大,文件的增多,简单的watch任务会使得每改动一次文件,都会编译所有同类型的文件,这相当的不需要。不过我们也不能watch单个文件,没有这样的写法,也不能写死(单个文件都写配置)。所以我们就需要根据文件改动编译次数,分成不同类型的任务。很少改动的直接不watch,转为手动编译就好
4. 涉及多文件时出错
提示错误:Source file “xxx.less” not found,普通写法对单个文件的编译没问题,但是涉及到多文件多目录的时候,就需要加上配置:expand: true
5. Grunt是不是最好的自动化工具
答案移至上一篇文章,Grunt自动化的前端项目构建工具:http://www.xuanfengge.com/grunt-front-end-project-build-automation-tools.html
另外,百度也有个前端集成解决方案:http://fis.baidu.com/