grunt之filerev、usemin


  竊以為這兩個插件是比較有用的,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地址)

  1. argorithm: 編碼方式,接受參數'md5'、'sha1'、'sha256'、'sha512'
  2. 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最簡單的實現,但直接拋出所有代碼肯定會造成一定的困擾

  1. html中那些注釋是什么意思
  2. gruntfile中dist和.tmp兩個文件夾是什么
  3. gruntfile中useminPrepare是什么,跟usemin是什么關系
  4. gruntfile中useminPrepare指定的html和usemin中指定的html為什么不一樣,能一樣么
  5. gruntfile中usemin的target的名字為什么是html,而其他的都是build
  6. 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
  1. clean,刪除dist和.tmp文件夾,涉及第二個問題
  2. copy,復制index.html到dist,涉及第四個問題
  3. useminPrepare,涉及第三、六個問題
  4. concat,合並文件,涉及.tmp文件夾,第二、六個問題
  5. cssmin、uglify,壓縮文件,涉及dist文件夾,第二、六個問題
  6. filerev,給dist文件夾下的文件重命名,涉及第二個問題
  7. 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

  1. dest: 默認是'dist',定義目標文件夾
  2. staging: 默認是'.tmp',定義臨時文件夾
  3. 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,是不是又正常了

  4. 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

  1. assetsDirs: 類似useminPrepare中的root,指定html文件中資源根目錄
  2. 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的引用了

  3. revmap: JSON文件的路徑,文件對象寫法參考grunt.filerev.summary

  這一節內容告一段落了,日后想到再行補充,下節介紹更簡單、更有意思的connect和watch。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM