?
Dokumen ini menggunakan Manual laman web PHP Cina Lepaskan
安裝
命令腳本文件Gruntfile.js
Gruntfile.js實(shí)例:grunt-contrib-cssmin模塊
常用模塊設(shè)置
grunt-contrib-jshint
grunt-contrib-concat
grunt-contrib-uglify
grunt-contrib-copy
grunt-contrib-watch
其他模塊
參考鏈接
在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
模塊安裝完以后,下一步在項(xiàng)目的根目錄下,新建腳本文件Gruntfile.js。它是grunt的配置文件,就好像package.json是npm的配置文件一樣。Gruntfile.js就是一般的Node.js模塊的寫法。
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ù)。
下面通過(guò)cssmin模塊,演示如何編寫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è)模塊,這里就要寫幾條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
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ù)的寫法,也就是說(shuō)如何在grunt.initConfig方法中配置各個(gè)模塊。
jshint用來(lái)檢查語(yǔ)法錯(cuò)誤,比如分號(hào)的使用是否正確、有沒(méi)有忘記寫括號(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文件。
concat用來(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)。
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)指定輸入和輸出文件。
copy模塊用于復(fù)制文件與目錄。
copy: { main: { src: 'src/*', dest: 'dest/', }, },
上面代碼將src子目錄(只包含它下面的第一層文件和子目錄),拷貝到dest子目錄下面(即dest/src目錄)。如果要更準(zhǔn)確控制拷貝行為,比如只拷貝文件、不拷貝目錄、不保持目錄結(jié)構(gòu),可以寫成下面這樣:
copy: { main: { expand: true, cwd: 'src/', src: '**', dest: 'dest/', flatten: true, filter: 'isFile', }, },
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)記名和屬性名是否小寫、屬性值是否包括在雙引號(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>
Frederic Hemberger, A build tool for front-end projects
Mária Jur?ovi?ová, Building a JavaScript Library with Grunt.js
Swapnil Mishra, Simplifying Chores with Grunt
AJ ONeal, Moving to GruntJS
Grunt Documentation, Configuring tasks
Landon Schropp, Writing an Awesome Build Script with Grunt
Mike Cunsolo, Get Up And Running With Grunt
Matt Bailey, A Beginner’s Guide to Using Grunt With Magento
Paul Bakaus, Supercharging your Gruntfile