窃以为这两个插件是比较有用的,filerev是给js、css进行编码重命名,usemin修改html中被重命名的js、css文件的引用。另外说明下之前将concat、cssmin、uglify放在一篇中的原因,因为usemin会自动对引用中的css文件进行concat和cssmin,对js文件进行concat和uglify。也就是说,如果不是定制化需求,只是简单的合并和压缩,使用了usemin就不用配置concat、cssmin、uglify这些task了。
filerev相对比较简单,更完filerev可能要过段时间来更usemin。
filerev(V2.1.2)的options(github地址)
- argorithm: 编码方式,接受参数'md5'、'sha1'、'sha256'、'sha512'
- length: 截取用来重命名的字符串长度
注意1:重命名后会生成map对象grunt.filerev.summary,key为源文件名,value为目的文件名,filerev任务后可使用此对象获取数据
注意2:很多人采用grunt-rev这个插件,但由于grunt-filerev和grunt-usemin在github上为同一个账号下的项目(大名鼎鼎的yoeman),所以这里采用grunt-filerev
usemin(V3.0.0)(github地址)
这部分不采用列options的方式了,因为是核心,也是难点,一步一步跟着代码走起来。
代码结构是源文件夹src下面包含base.css、main.css、helloworld.js、hellogrunt.js、index.html五个文件
Gruntfile.js
module.exports = function (grunt) { require('load-grunt-tasks')(grunt); var path = { src : 'src', dest : 'dist', tmp : '.tmp' } grunt.initConfig({ path : path, clean : { beforebuild : { files : [{ src : ['<%= path.dest %>/', '<%= path.tmp %>/'] } ] } }, filerev : { build : { files : [{ src : ['<%= path.dest %>/**', '!<%= path.dest %>/index.html'] } ] } }, useminPrepare : { build : { files : [{ src : '<%= path.src %>/index.html' } ] } }, usemin : { html : { files : [{ src : '<%= path.dest %>/index.html' } ] }, css : { files : [{ src : '<%= path.dest %>/*.css' } ] } }, copy : { build : { files : [{ expand : true, cwd : '<%= path.src %>/', src : ['index.html'], dest : '<%= path.dest %>/' } ] } }, imagemin : { build : { files : [{ expand : true, cwd : '<%= path.src %>/', src : ['*.{jpg, png, jpeg, gif}'], dest : '<%= path.dest %>/' } ] } } }); grunt.registerTask('default', ['clean:beforebuild', 'copy', 'imagemin', 'useminPrepare', 'concat', 'cssmin', 'uglify', 'filerev', 'usemin']); };
index.html
<html> <head> <!-- build:css base.css --> <link rel="stylesheet" href="base.css"/> <!-- endbuild --> <!-- build:css main.css --> <link rel="stylesheet" href="main.css"/> <!-- endbuild --> </head> <body> <p>index</p> <!-- build:js hello.js --> <script type="text/javascript" src="helloworld.js"></script> <script type="text/javascript" src="hellogrunt.js"></script> <!-- endbuild --> </body> </html>
在命令行输入grunt,输出目录下的文件base.33fa5f7d.css、main.d58400c3.css、hello.c2959b6b.js、index.html为,其中index.html如下
<html> <head> <link rel="stylesheet" href="base.33fa5f7d.css"> <link rel="stylesheet" href="main.d58400c3.css"> </head> <body> <p>index</p> <script src="hello.c2959b6b.js"></script> </body> </html>
如上是usemin最简单的实现,但直接抛出所有代码肯定会造成一定的困扰
- html中那些注释是什么意思
- gruntfile中dist和.tmp两个文件夹是什么
- gruntfile中useminPrepare是什么,跟usemin是什么关系
- gruntfile中useminPrepare指定的html和usemin中指定的html为什么不一样,能一样么
- gruntfile中usemin的target的名字为什么是html,而其他的都是build
- gruntfile中并未指定concat、cssmin、uglify等task,为什么最后执行了
首先,html中的注释是有意义的,是usemin定义的一种语法结构,规则如下
<!-- build:<type> <output path> --> ... HTML Markup, list of script / link tags. <!-- endbuild -->
以上为一个block,其中声明的tags会被合并和压缩为一个文件output path,type定义不同的规则,原生支持js和css,也支持自定义,那是后话。
接下来,后面的问题要先捋顺整个流程,执行grunt后打印的log如下
Running "clean:beforebuild" (clean) task >> 2 paths cleaned. Running "copy:build" (copy) task Copied 1 file Running "useminPrepare:build" (useminPrepare) task Configuration changed for concat, uglify, cssmin Running "concat:generated" (concat) task File .tmp\concat\base.css created. File .tmp\concat\main.css created. File .tmp\concat\hello.js created. Running "cssmin:generated" (cssmin) task File dist\base.css created: 39 B → 21 B File dist\main.css created: 28 B → 17 B Running "uglify:generated" (uglify) task >> 1 file created. Running "filerev:build" (filerev) task Revved 4 files Running "usemin:html" (usemin) task Replaced 1 reference to assets
- clean,删除dist和.tmp文件夹,涉及第二个问题
- copy,复制index.html到dist,涉及第四个问题
- useminPrepare,涉及第三、六个问题
- concat,合并文件,涉及.tmp文件夹,第二、六个问题
- cssmin、uglify,压缩文件,涉及dist文件夹,第二、六个问题
- filerev,给dist文件夹下的文件重命名,涉及第二个问题
- usemin,给html文件进行引用修改,涉及第四、五个问题
useminPrepare,grunt-usemin包含两个task:一个是useminPrepare,一个是usemin。useminPrepare的功能是进行一些前期工作(第三个问题),主要是声明concat、cssmin、uglify等task,仅仅是声明,依赖、load、register还是要有的(第六个问题)。而这些task生成的临时文件即concat文件会存放到.tmp中,目标文件即压缩后的文件会存放到dist中(第二个问题)。那怎么找需要处理的资源文件呢,则是通过源html的引用(第四个问题)。经过这三个task和filerev后,usemin正式修改目标html中的引用(第四个问题),而且usemin不仅可以修改html中的引用,甚至可以修改css中的引用,前者为html target,后者为css target(第六个问题)。
以上就是那几个问题行诸于文字的解释了,要行诸于代码,还要从options说起。
useminPrepare的options
- dest: 默认是'dist',定义目标文件夹
- staging: 默认是'.tmp',定义临时文件夹
- root: 默认是源html所在的文件夹,定义处理的资源文件的相对路径
举个栗子,我们把src中的目录结构标准化,如下:我这里添加了一张图片,后话解释
结构变了,但是引用没变,也就是照之前的情况是找不到引用的源资源文件的,输入grunt,得到的结果如下
Running "clean:beforebuild" (clean) task >> 2 paths cleaned. Running "copy:build" (copy) task Copied 1 file Running "useminPrepare:build" (useminPrepare) task Configuration changed for concat, uglify, cssmin Running "concat:generated" (concat) task File .tmp\concat\base.css created. File .tmp\concat\main.css created. File .tmp\concat\hello.js created. Running "cssmin:generated" (cssmin) task >> Destination not written because minified CSS was empty. >> Destination not written because minified CSS was empty. Running "uglify:generated" (uglify) task >> 1 file created. Running "filerev:build" (filerev) task Revved 2 files Running "usemin:html" (usemin) task Replaced 1 reference to assets
虽然在临时文件夹中生成了css和js,但因为没有源文件,生成的都是空文件,所以cssmin的时候报错了,uglify不过也是生成了空文件而已。
这时候我们修改下useminPrepare,如下
options: { root: [ '<%= path.src %>/css', '<%= path.src %>/js' ] }
然后grunt,是不是又正常了
- flow: 重点中的重点,核心中的核心,虽然用到的不多,但是对理解整个流程是有帮助的,默认值是这个样子的
{ steps: { js: ['concat', 'uglifyjs'], css: ['concat', 'cssmin'] }, post: {} }
这个object中有两个属性,steps定义block中针对不同type的处理方式,post定义不同的处理过程中额外的行为,还是举个栗子
比如concat中的separator是换行符,你如果想修改为;,那么可以这样做flow: { steps: { js: ['concat', 'uglifyjs'], //注意不是uglify,是uglifyjs,下面的post中对应的name为uglify css: ['concat', 'cssmin'] }, post: { js: [{ name: 'concat', createConfig: function (context, block) { var generated = context.options.generated; //context.options即concat task generated.options = { separator: ';' }; } }] } }
上面的createConfig中的block对象为html中的block对象化
<!-- build:js scripts/site.js -->', <script src="foo.js"></script>', <script src="bar.js"></script>', <script src="baz.js"></script>', <!-- endbuild -->'
此段block对应的block对象为
var block = { type: 'js', dest: 'scripts/site.js', src: [ 'foo.js', 'bar.js', 'baz.js' ], raw: [ ' <!-- build:js scripts/site.js -->', ' <script src="foo.js"></script>', ' <script src="bar.js"></script>', ' <script src="baz.js"></script>', ' <!-- endbuild -->' ] };
usemin的options
- assetsDirs: 类似useminPrepare中的root,指定html文件中资源根目录
- blockReplacements: 类似flow在useminPrepare的地位,流程的核心,默认的值如下
{ css: function (block) { return ...; }, js: function (block) { return ...; } }
属性对应block中的type,return的值为最后输出的script / css tag,如果资源文件配置在CDN或静态服务器上,就需要修改原来的方法了
js: function(block) { var sFile = path.dest + require('path').sep + block.dest.replace(/\//g, '\\'), //require('path').sep是用来获取文件分隔符的,windows下面为\,linux下面为/ dFile = grunt.filerev.summary[sFile].replace(/\\/g, '/'); //记得filerev提到的那个对象么 return '<script type="text/javascript" src="' + 'http://static.*.*/' + dFile + '"></script>'; }
同时也可以处理自定义type的函数
比如我们开发过程中引用了less.js,但release版本肯定是不能上的,就在源html中自定义一个block
<!-- build:remove --> <script type="text/javascript" src="less.js"></script> <!-- endbuild -->
type为remove,然后在Gruntfile.js中添加处理函数
blockReplacements: { remove: function(block) { return ''; } }
目标html中就不包含对less.js的引用了
- revmap: JSON文件的路径,文件对象写法参考grunt.filerev.summary
这一节内容告一段落了,日后想到再行补充,下节介绍更简单、更有意思的connect和watch。