恩。。今天趁着有空,把项目里的前端相关的东西弄个自动化好了
工具grunt
不多介绍了,大家都懂得。。。
安装也不多说了,google上都有的
项目需要的功能
- css最小化
- js压缩
- coffee编译
这些都是比较常见的功能了,当然css的预编译还有less、sass,在此处的原理和coffee->js的逻辑一样,也不赘述了
package.json
json
{ "name": "project_name", "version": "0.0.1", "devDependencies": { "grunt": "~0.4.5", "grunt-contrib-cssmin": "~0.5.0", "grunt-contrib-uglify": "~0.8.0", "grunt-contrib-coffee": "~0.13.0", "grunt-contrib-copy": "~0.8.0", "grunt-contrib-watch": "~0.6.1", }, "main": "Gruntfile.js", "author": "zhulegequ", "license": "MIT" }
依赖的包,前面四个不解释了,顾名思义。grunt-contrib-copy
:拷贝文件、文件数,此处是因为项目里有一些图片啊字体文件啊模板文件等其他的静态文件,这些文件和css、js放在一个文件夹里,但css、js会随着压缩而移动,所以需要这个包grunt-contrib-watch
:监视文件修改,增量任务的关键
Gruntfile.coffee
grunt既支持javascript,也能支持coffeescript,为了统一逼格,所以此处用的是coffeescript,coffeescript编译到js,减少了很多代码量,可以说再也不用担心大括号了。我也是今天才写,还是挺好使得。。
文件我分为三部分,此处的分部主要是功能的分开,但代码不一定是分开的,注意缩进:
第一部分:基本任务定义
coffeescript
module.exports = (grunt)-> #第一部分开始,这一部分是初始化配置, grunt.initConfig pkg: grunt.file.readJSON('package.json') cssmin: comparecss: files:[ expand:true cwd:'themes/src/'#目录下 src:'**/*.css'#所有css文件 dest: 'themes/dist/'#输出到此目录下 ] uglify: #压缩js comparejs: files:[ expand:true cwd:'themes/src/'#目录下 src:'**/*.js'#所有js文件 dest: 'themes/dist/'#输出到此目录下 ] copy: #移动 main: files:[ expand:true cwd:'themes/src/'#目录下 src:['*.template']#所有模板文件 dest: 'themes/dist/'#输出到此目录下 ] coffee: compile: options: bare:true files:[ expand:true flatten: true cwd:'themes/src/coffee'#目录下 src:'*.coffee'#所有coffee文件 ext: '.js' dest: 'themes/src/js'#输出到此目录下 ] #第一部分结束
第一部分的功能是初始化配置,在这里基本定义了几个任务:cssmin、uglify、copy、coffee,相关的标志位文档可以查看这些包的github主页,一个比较常见的标志位expand
,表示会递归的输送,比如在src='folder/src',dest='folder/dest/'
的情况下,folder/src/a/b/c.js
,会输送到folder/dest/a/b/c.js
第二部分:监视任务定义
恩,这一部分其实算坑吧也不知道算不算,在watch的一个示例文档中,其实也提到了基础的用法:
coffeescript
grunt.initConfig({ watch: { #忽略一部分代码 src: { files: ['lib/*.js', 'css/**/*.scss', '!lib/dontwatch.js'], tasks: ['default'], }, }, });
表示定义了一个src的监视任务,监视到files变量里的文件修改时,执行default任务。
还提到了一个增量的例子:
coffeescript
grunt.initConfig({ jshint: {#定义一个jshint任务 all: { src: ['lib/*.js'], }, }, watch: {#定义监视任务 scripts: { files: ['lib/*.js'], tasks: ['jshint'], options: { spawn: false, }, }, }, }); grunt.event.on('watch', function(action, filepath,target) {#此处回调函数应当是3个参数,target是监视任务名,此处是scripts,在有多个监视任务的时候有用 grunt.config('jshint.all.src', filepath); });
监视任务还是一样的,spawn
标志位的作用挺魔怔的。。默认是true,大家可以试一试效果,这个标志位特别重要!特别重要!特别重要!千万别漏了,漏了就是起的全量更新作用了。
此处起到监视增量作用的,就是注册的这个事件回调
实际上这句等同于grunt.config.set('jshint.all.src', filepath),我都要哭了。。
所以下面是我的监视任务定义
coffeescript
#第二部分开始 watch: coffeescript: files:['themes/src/coffee/*.coffee'] tasks:'coffee' # options: #这个很关键 # spawn: false jsuglify: files:['themes/src/**/*.js'] tasks:'uglify' options: spawn: false cssminify: files:['themes/src/**/*.css'] tasks:'cssmin' options: spawn: false #这段的缩进和grunt.initConfig平级 grunt.loadNpmTasks('grunt-contrib-cssmin') grunt.loadNpmTasks('grunt-contrib-uglify') grunt.loadNpmTasks('grunt-contrib-coffee') grunt.loadNpmTasks('grunt-contrib-watch')
注意到我这里的coffeescript的spawn被我注释掉了,这里坑了我一小下,这样意味着coffeescript的更新,是全量更新,一个文件修改了,全部文件都会重新编译一次,为啥这样做呢,因为有coffeescript->javascript->javascript.min这个过程,如果此处spawn置为false,那么后边那半段就没了。。。技术所限,只能走这样的流水线,如果你们知道coffeescript->javascript.min的过程,请务必告诉我。。
第三部分:同步删除
作用:删除js源文件的时候,把生成的js.min删掉,css同理
coffeescript
deleteDist=(srcfilepath,distpath,sourcepath)-> distfile = distpath+srcfilepath.substring(sourcepath.length) if grunt.file.exists(distfile) grunt.file.delete(distfile) grunt.event.on 'watch', (action, filepath,target) -> console.log action, filepath,target if target=="jsuglify" if action == "deleted" or action=="renamed" deleteDist(filepath,'themes/dist/','themes/src/') if action=="deleted" return grunt.config 'uglify.comparejs.files', [ expand:true cwd:'themes/src/'#目录下 src:filepath.substring(11)#去掉 themes/src/ 这11个字符 dest: 'themes/dist/'#输出到此目录下 ] console.log 'uglify detach',filepath,action return else if target=="coffeescript" grunt.config "coffee.compile.files",#覆盖配置,只对修改文件进行编译 [ expand:true flatten: true cwd:'themes/src/coffee'#目录下 src:filepath.substring(18)# ext: '.js' dest: 'themes/src/js'#输出到此目录下 ] console.log 'coffeescript detach',filepath,action return else if target=="cssminify" if action == "deleted" or action=="renamed" deleteDist(filepath,'themes/dist/','themes/src/') if action=="deleted" return console.log 'cssminify detach',filepath,action grunt.config.set "cssmin.comparecss.files",#覆盖配置,只对修改文件进行编译 [ expand:true cwd:'themes/src/'#目录下 src:filepath.substring(11)#所有css文件 dest: 'themes/dist/'#输出到此目录下 ] return grunt.registerTask 'default', ['watch'] return
这个第二部分讲过了,大致原理就是检测到修改的时候,把任务的配置改成被修改的文件的路径,然后结束,grunt在之后会事件冒泡,执行任务,大家看代码领悟就行了哈哈,
删除检测就是检测文件名,没用sync库,当进行重命名的时候,实际上会发生两次事件,一次deleted
旧文件,一次renamed
新文件,所以重命名的时候,需要删掉旧文件编译的结果、冒泡上去,继续生产新文件。而删除的时候就直接删掉旧文件编译的结果就行
比较丑陋的是src:filepath.substring(18)
这样的句子,因为filepath的参数是全路径,包含cwd,所以要去掉和cwd变量等长+一个'/'的长度。。。恩deleteDist这个函数也没用正则,=w=就酱。。。
其他的坑
就是注意coffee的函数调用,函数+空格+以逗号分隔的参数们,逗号没了意味着函数参数结束,所以console.log "hi","123"
和console.log "hi" "123"
显然不等同,空格换成回车也类似