Grunt:任務(wù)自動(dòng)管理工具

2021-09-15 16:13 更新

在Javascript的開(kāi)發(fā)過(guò)程中,經(jīng)常會(huì)遇到一些重復(fù)性的任務(wù),比如合并文件、壓縮代碼、檢查語(yǔ)法錯(cuò)誤、將Sass代碼轉(zhuǎn)成CSS代碼等等。通常,我們需要使用不同的工具,來(lái)完成不同的任務(wù),既重復(fù)勞動(dòng)又非常耗時(shí)。Grunt就是為了解決這個(gè)問(wèn)題而發(fā)明的工具,可以幫助我們自動(dòng)管理和運(yùn)行各種任務(wù)。

簡(jiǎn)單說(shuō),Grunt是一個(gè)自動(dòng)任務(wù)運(yùn)行器,會(huì)按照預(yù)先設(shè)定的順序自動(dòng)運(yùn)行一系列的任務(wù)。這可以簡(jiǎn)化工作流程,減輕重復(fù)性工作帶來(lái)的負(fù)擔(dān)。

安裝

Grunt基于Node.js,安裝之前要先安裝Node.js,然后運(yùn)行下面的命令。

sudo npm install grunt-cli -g

grunt-cli表示安裝的是grunt的命令行界面,參數(shù)g表示全局安裝。

Grunt使用模塊結(jié)構(gòu),除了安裝命令行界面以外,還要根據(jù)需要安裝相應(yīng)的模塊。這些模塊應(yīng)該采用局部安裝,因?yàn)椴煌?xiàng)目可能需要同一個(gè)模塊的不同版本。

首先,在項(xiàng)目的根目錄下,創(chuàng)建一個(gè)文本文件package.json,指定當(dāng)前項(xiàng)目所需的模塊。下面就是一個(gè)例子。

{
  "name": "my-project-name",
  "version": "0.1.0",
  "author": "Your Name",
  "devDependencies": {
    "grunt": "0.x.x",
    "grunt-contrib-jshint": "*",
    "grunt-contrib-concat": "~0.1.1",
    "grunt-contrib-uglify": "~0.1.0",
    "grunt-contrib-watch": "~0.1.4"
  }
}

上面這個(gè)package.json文件中,除了注明項(xiàng)目的名稱和版本以外,還在devDependencies屬性中指定了項(xiàng)目依賴的grunt模塊和版本:grunt核心模塊為最新的0.x.x版,jshint插件為最新版本,concat插件不低于0.1.1版,uglify插件不低于0.1.0版,watch插件不低于0.1.4版。

然后,在項(xiàng)目的根目錄下運(yùn)行下面的命令,這些插件就會(huì)被自動(dòng)安裝在node_modules子目錄。

npm install

上面這種方法是針對(duì)已有package.json的情況。如果想要自動(dòng)生成package.json文件,可以使用npm init命令,按照屏幕提示回答所需模塊的名稱和版本即可。

npm init

如果已有的package.json文件不包括Grunt模塊,可以在直接安裝Grunt模塊的時(shí)候,加上--save-dev參數(shù),該模塊就會(huì)自動(dòng)被加入package.json文件。

npm install <module> --save-dev

比如,對(duì)應(yīng)上面package.json文件指定的模塊,需要運(yùn)行以下npm命令。

npm install grunt --save-dev
npm install grunt-contrib-jshint --save-dev
npm install grunt-contrib-concat --save-dev
npm install grunt-contrib-uglify --save-dev
npm install grunt-contrib-watch --save-dev

命令腳本文件Gruntfile.js

模塊安裝完以后,下一步在項(xiàng)目的根目錄下,新建腳本文件Gruntfile.js。它是grunt的配置文件,就好像package.json是npm的配置文件一樣。Gruntfile.js就是一般的Node.js模塊的寫(xiě)法。

module.exports = function(grunt) {

  // 配置Grunt各種模塊的參數(shù)
  grunt.initConfig({
    jshint: { /* jshint的參數(shù) */ },
    concat: { /* concat的參數(shù) */ },
    uglify: { /* uglify的參數(shù) */ },
    watch:  { /* watch的參數(shù) */ }
  });

  // 從node_modules目錄加載模塊文件
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-watch');

  // 每行registerTask定義一個(gè)任務(wù)
  grunt.registerTask('default', ['jshint', 'concat', 'uglify']);
  grunt.registerTask('check', ['jshint']);

};

上面的代碼用到了grunt代碼的三個(gè)方法:

  • grunt.initConfig:定義各種模塊的參數(shù),每一個(gè)成員項(xiàng)對(duì)應(yīng)一個(gè)同名模塊。

  • grunt.loadNpmTasks:加載完成任務(wù)所需的模塊。

  • grunt.registerTask:定義具體的任務(wù)。第一個(gè)參數(shù)為任務(wù)名,第二個(gè)參數(shù)是一個(gè)數(shù)組,表示該任務(wù)需要依次使用的模塊。default任務(wù)名表示,如果直接輸入grunt命令,后面不跟任何參數(shù),這時(shí)所調(diào)用的模塊(該例為jshint,concat和uglify);該例的check任務(wù)則表示使用jshint插件對(duì)代碼進(jìn)行語(yǔ)法檢查。

上面的代碼一共加載了四個(gè)模塊:jshint(檢查語(yǔ)法錯(cuò)誤)、concat(合并文件)、uglify(壓縮代碼)和watch(自動(dòng)執(zhí)行)。接下來(lái),有兩種使用方法。

(1)命令行執(zhí)行某個(gè)模塊,比如

grunt jshint

上面代碼表示運(yùn)行jshint模塊。

(2)命令行執(zhí)行某個(gè)任務(wù)。比如

grunt check

上面代碼表示運(yùn)行check任務(wù)。如果運(yùn)行成功,就會(huì)顯示“Done, without errors.”。

如果沒(méi)有給出任務(wù)名,只鍵入grunt,就表示執(zhí)行默認(rèn)的default任務(wù)。

Gruntfile.js實(shí)例:grunt-contrib-cssmin模塊

下面通過(guò)cssmin模塊,演示如何編寫(xiě)Gruntfile.js文件。cssmin模塊的作用是最小化CSS文件。

首先,在項(xiàng)目的根目錄下安裝該模塊。

npm install grunt-contrib-cssmin --save-dev

然后,新建文件Gruntfile.js。

module.exports = function(grunt) {

  grunt.initConfig({
    cssmin: {
      minify: {
        expand: true,
        cwd: 'css/',
        src: ['*.css', '!*.min.css'],
        dest: 'css/',
        ext: '.min.css'
      },
      combine: {
        files: {
          'css/out.min.css': ['css/part1.min.css', 'css/part2.min.css']
        }
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-cssmin');

  grunt.registerTask('default', ['cssmin:minify','cssmin:combine']);

};

下面詳細(xì)解釋上面代碼中的三個(gè)方法,下面一個(gè)個(gè)來(lái)看。

(1)grunt.loadNpmTasks

grunt.loadNpmTasks方法載入模塊文件。

grunt.loadNpmTasks('grunt-contrib-cssmin');

你需要使用幾個(gè)模塊,這里就要寫(xiě)幾條grunt.loadNpmTasks語(yǔ)句,將各個(gè)模塊一一加載。

如果加載模塊很多,這部分會(huì)非常冗長(zhǎng)。而且,還存在一個(gè)問(wèn)題,就是凡是在這里加載的模塊,也同時(shí)出現(xiàn)在package.json文件中。如果使用npm命令卸載模塊以后,模塊會(huì)自動(dòng)從package.json文件中消失,但是必須手動(dòng)從Gruntfile.js文件中清除,這樣很不方便,一旦忘記,還會(huì)出現(xiàn)運(yùn)行錯(cuò)誤。這里有一個(gè)解決辦法,就是安裝load-grunt-tasks模塊,然后在Gruntfile.js文件中,用下面的語(yǔ)句替代所有的grunt.loadNpmTasks語(yǔ)句。

require('load-grunt-tasks')(grunt);

這條語(yǔ)句的作用是自動(dòng)分析package.json文件,自動(dòng)加載所找到的grunt模塊。

(2)grunt.initConfig

grunt.initConfig方法用于模塊配置,它接受一個(gè)對(duì)象作為參數(shù)。該對(duì)象的成員與使用的同名模塊一一對(duì)應(yīng)。由于我們要配置的是cssmin模塊,所以里面有一個(gè)cssmin成員(屬性)。

cssmin(屬性)指向一個(gè)對(duì)象,該對(duì)象又包含多個(gè)成員。除了一些系統(tǒng)設(shè)定的成員(比如options),其他自定義的成員稱為目標(biāo)(target)。一個(gè)模塊可以有多個(gè)目標(biāo)(target),上面代碼里面,cssmin模塊共有兩個(gè)目標(biāo),一個(gè)是“minify”,用于壓縮css文件;另一個(gè)是“combine”,用于將多個(gè)css文件合并一個(gè)文件。

每個(gè)目標(biāo)的具體設(shè)置,需要參考該模板的文檔。就cssmin來(lái)講,minify目標(biāo)的參數(shù)具體含義如下:

  • expand:如果設(shè)為true,就表示下面文件名的占位符(即*號(hào))都要擴(kuò)展成具體的文件名。

  • cwd:需要處理的文件(input)所在的目錄。

  • src:表示需要處理的文件。如果采用數(shù)組形式,數(shù)組的每一項(xiàng)就是一個(gè)文件名,可以使用通配符。

  • dest:表示處理后的文件名或所在目錄。

  • ext:表示處理后的文件后綴名。

除了上面這些參數(shù),還有一些參數(shù)也是grunt所有模塊通用的。

  • filter:一個(gè)返回布爾值的函數(shù),用于過(guò)濾文件名。只有返回值為true的文件,才會(huì)被grunt處理。

  • dot:是否匹配以點(diǎn)號(hào)(.)開(kāi)頭的系統(tǒng)文件。

  • makeBase:如果設(shè)置為true,就只匹配文件路徑的最后一部分。比如,a?b可以匹配/xyz/123/acb,而不匹配/xyz/acb/123。

關(guān)于通配符,含義如下:

  • *:匹配任意數(shù)量的字符,不包括/。
  • ?:匹配單個(gè)字符,不包括/。
  • **:匹配任意數(shù)量的字符,包括/。
  • {}:允許使用逗號(hào)分隔的列表,表示“or”(或)關(guān)系。
  • !:用于模式的開(kāi)頭,表示只返回不匹配的情況。

比如,foo/*.js匹配foo目錄下面的文件名以.js結(jié)尾的文件,foo/*/.js匹配foo目錄和它的所有子目錄下面的文件名以.js結(jié)尾的文件,!*.css表示匹配所有后綴名不為“.css”的文件。

使用通配符設(shè)置src屬性的更多例子:

{src: 'foo/th*.js'}grunt-contrib-uglify

{src: 'foo/{a,b}*.js'}

{src: ['foo/a*.js', 'foo/b*.js']}

至于combine目標(biāo),就只有一個(gè)files參數(shù),表示輸出文件是css子目錄下的out.min.css,輸入文件則是css子目錄下的part1.min.css和part2.min.css。

files參數(shù)的格式可以是一個(gè)對(duì)象,也可以是一個(gè)數(shù)組。

files: {
        'dest/b.js': ['src/bb.js', 'src/bbb.js'],
        'dest/b1.js': ['src/bb1.js', 'src/bbb1.js'],
},

// or

files: [
        {src: ['src/aa.js', 'src/aaa.js'], dest: 'dest/a.js'},
        {src: ['src/aa1.js', 'src/aaa1.js'], dest: 'dest/a1.js'},
],

如果minify目標(biāo)和combine目標(biāo)的屬性設(shè)置有重合的部分,可以另行定義一個(gè)與minify和combine平行的options屬性。

grunt.initConfig({
    cssmin: {
      options: { /* ... */ },
      minify: { /* ... */ },
      combine: { /* ... */ }
    }
  });

(3)grunt.registerTask

grunt.registerTask方法定義如何調(diào)用具體的任務(wù)?!癲efault”任務(wù)表示如果不提供參數(shù),直接輸入grunt命令,則先運(yùn)行“cssmin:minify”,后運(yùn)行“cssmin:combine”,即先壓縮再合并。如果只執(zhí)行壓縮,或者只執(zhí)行合并,則需要在grunt命令后面指明“模塊名:目標(biāo)名”。

grunt # 默認(rèn)情況下,先壓縮后合并

grunt cssmin:minify # 只壓縮不合并

grunt css:combine # 只合并不壓縮

如果不指明目標(biāo),只是指明模塊,就表示將所有目標(biāo)依次運(yùn)行一遍。

grunt cssmin

常用模塊設(shè)置

grunt的模塊已經(jīng)超過(guò)了2000個(gè),且還在快速增加。下面是一些常用的模塊(按字母排序)。

  • grunt-contrib-clean:刪除文件。
  • grunt-contrib-compass:使用compass編譯sass文件。
  • grunt-contrib-concat:合并文件。
  • grunt-contrib-copy:復(fù)制文件。
  • grunt-contrib-cssmin:壓縮以及合并CSS文件。
  • grunt-contrib-imagemin:圖像壓縮模塊。
  • grunt-contrib-jshint:檢查JavaScript語(yǔ)法。
  • grunt-contrib-uglify:壓縮以及合并JavaScript文件。
  • grunt-contrib-watch:監(jiān)視文件變動(dòng),做出相應(yīng)動(dòng)作。

模塊的前綴如果是grunt-contrib,就表示該模塊由grunt開(kāi)發(fā)團(tuán)隊(duì)維護(hù);如果前綴是grunt(比如grunt-pakmanager),就表示由第三方開(kāi)發(fā)者維護(hù)。

以下選幾個(gè)模塊,看看它們配置參數(shù)的寫(xiě)法,也就是說(shuō)如何在grunt.initConfig方法中配置各個(gè)模塊。

grunt-contrib-jshint

jshint用來(lái)檢查語(yǔ)法錯(cuò)誤,比如分號(hào)的使用是否正確、有沒(méi)有忘記寫(xiě)括號(hào)等等。它在grunt.initConfig方法里面的配置代碼如下。

jshint: {
    options: {
        eqeqeq: true,
        trailing: true
    },
    files: ['Gruntfile.js', 'lib/**/*.js']
},

上面代碼先指定jshint的檢查項(xiàng)目,eqeqeq表示要用嚴(yán)格相等運(yùn)算符取代相等運(yùn)算符,trailing表示行尾不得有多余的空格。然后,指定files屬性,表示檢查目標(biāo)是Gruntfile.js文件,以及l(fā)ib目錄的所有子目錄下面的JavaScript文件。

grunt-contrib-concat

concat用來(lái)合并同類(lèi)文件,它不僅可以合并JavaScript文件,還可以合并CSS文件。

concat: {
  js: {
    src: ['lib/module1.js', 'lib/module2.js', 'lib/plugin.js'],
    dest: 'dist/script.js'
  }
  css: {
    src: ['style/normalize.css', 'style/base.css', 'style/theme.css'],
    dest: 'dist/screen.css'
  }
},

js目標(biāo)用于合并JavaScript文件,css目標(biāo)用語(yǔ)合并CSS文件。兩者的src屬性指定需要合并的文件(input),dest屬性指定輸出的目標(biāo)文件(output)。

grunt-contrib-uglify

uglify模塊用來(lái)壓縮代碼,減小文件體積。

uglify: {
  options: {
    banner: bannerContent,
    sourceMapRoot: '../',
    sourceMap: 'distrib/'+name+'.min.js.map',
    sourceMapUrl: name+'.min.js.map'
  },
  target : {
    expand: true,
    cwd: 'js/origin',
    src : '*.js',
    dest : 'js/'
  }
},

上面代碼中的options屬性指定壓縮后文件的文件頭,以及sourceMap設(shè)置;target目標(biāo)指定輸入和輸出文件。

grunt-contrib-copy

copy模塊用于復(fù)制文件與目錄。

copy: {
  main: {
    src: 'src/*',
    dest: 'dest/',
  },
},

上面代碼將src子目錄(只包含它下面的第一層文件和子目錄),拷貝到dest子目錄下面(即dest/src目錄)。如果要更準(zhǔn)確控制拷貝行為,比如只拷貝文件、不拷貝目錄、不保持目錄結(jié)構(gòu),可以寫(xiě)成下面這樣:

copy: {
  main: {
    expand: true,
    cwd: 'src/',
    src: '**',
    dest: 'dest/',
    flatten: true,
    filter: 'isFile',
  },
},

grunt-contrib-watch

watch模塊用來(lái)在后臺(tái)運(yùn)行,監(jiān)聽(tīng)指定事件,然后自動(dòng)運(yùn)行指定的任務(wù)。

watch: {
   scripts: {
    files: '**/*.js',
    tasks: 'jshint',
    options: {
      livereload: true,
    },
   },
   css: {
    files: '**/*.sass',
    tasks: ['sass'],
    options: {
      livereload: true,
    },
   },
},

設(shè)置好上面的代碼,打開(kāi)另一個(gè)進(jìn)程,運(yùn)行g(shù)runt watch。此后,任何的js代碼變動(dòng),文件保存后就會(huì)自動(dòng)運(yùn)行jshint任務(wù);任何sass文件變動(dòng),文件保存后就會(huì)自動(dòng)運(yùn)行sass任務(wù)。

需要注意的是,這兩個(gè)任務(wù)的options參數(shù)之中,都設(shè)置了livereload,表示任務(wù)運(yùn)行結(jié)束后,自動(dòng)在瀏覽器中重載(reload)。這需要在瀏覽器中安裝livereload插件。安裝后,livereload的默認(rèn)端口為localhost:35729,但是也可以用livereload: 1337的形式重設(shè)端口(localhost:1337)。

其他模塊

下面是另外一些有用的模塊。

(1)grunt-contrib-clean

該模塊用于刪除文件或目錄。

clean: {
  build: {
    src: ["path/to/dir/one", "path/to/dir/two"]
  }
}

(2)grunt-autoprefixer

該模塊用于為CSS語(yǔ)句加上瀏覽器前綴。

autoprefixer: {
  build: {
    expand: true,
    cwd: 'build',
    src: [ '**/*.css' ],
    dest: 'build'
  }
},

(3)grunt-contrib-connect

該模塊用于在本機(jī)運(yùn)行一個(gè)Web Server。

connect: {
  server: {
    options: {
      port: 4000,
      base: 'build',
      hostname: '*'
    }
  }
}

connect模塊會(huì)隨著grunt運(yùn)行結(jié)束而結(jié)束,為了使它一直處于運(yùn)行狀態(tài),可以把它放在watch模塊之前運(yùn)行。因?yàn)閣atch模塊需要手動(dòng)中止,所以connect模塊也就會(huì)一直運(yùn)行。

(4)grunt-htmlhint

該模塊用于檢查HTML語(yǔ)法。

htmlhint: {
    build: {
        options: {
            'tag-pair': true,
            'tagname-lowercase': true,
            'attr-lowercase': true,
            'attr-value-double-quotes': true,
            'spec-char-escape': true,
            'id-unique': true,
            'head-script-disabled': true,
        },
        src: ['index.html']
    }
}

上面代碼用于檢查index.html文件:HTML標(biāo)記是否配對(duì)、標(biāo)記名和屬性名是否小寫(xiě)、屬性值是否包括在雙引號(hào)之中、特殊字符是否轉(zhuǎn)義、HTML元素的id屬性是否為唯一值、head部分是否沒(méi)有script標(biāo)記。

(5)grunt-contrib-sass模塊

該模塊用于將SASS文件轉(zhuǎn)為CSS文件。

sass: {
    build: {
        options: {
            style: 'compressed'
        },
        files: {
            'build/css/master.css': 'assets/sass/master.scss'
        }
    }
}

上面代碼指定輸出文件為build/css/master.css,輸入文件為assets/sass/master.scss。

(6)grunt-markdown

該模塊用于將markdown文檔轉(zhuǎn)為HTML文檔。

markdown: {
    all: {
      files: [
        {
          expand: true,
          src: '*.md',
          dest: 'docs/html/',
          ext: '.html'
        }
      ],
      options: {
        template: 'templates/index.html',
      }
    }
},

上面代碼指定將md后綴名的文件,轉(zhuǎn)為docs/html/目錄下的html文件。template屬性指定轉(zhuǎn)換時(shí)采用的模板,模板樣式如下。

<!DOCTYPE html>
<html>
<head>
    <title>Document</title>
</head>
<body>

    <div id="main" class="container">
        <%=content%>
    </div>

</body>
</html>

參考鏈接

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)