Grunt 新手一日入門
2014.06.20
TOC
當(dāng)時(shí)學(xué)習(xí) Grunt 的時(shí)候,真是很頭疼。分了兩個(gè)時(shí)間段,學(xué)習(xí)了兩次才硬啃下來,之后才能用在項(xiàng)目中。主要原因我認(rèn)為是學(xué)習(xí)資料和文檔上面寫的太高端了。這類的文檔或者資料有個(gè)顯著特點(diǎn),上來先簡單介紹一下這個(gè)玩意(Grunt 是一個(gè) JavaScript 任務(wù)運(yùn)行器),然后就是如何安裝,直接給你配置文件的語法,如何使用插件,新手往往看完還不知所以然。 就像我第一次學(xué)習(xí)的時(shí)候,只是大體知道 Grunt 很火,大家都在用,但耐著心看文檔和一些別人的學(xué)習(xí)總結(jié),還是困惑,這到底是個(gè)什么東西?究竟干什么用?為什么要這么麻煩配置這些東西? 到現(xiàn)在應(yīng)用在項(xiàng)目中也有一小段時(shí)間了,稍微有一點(diǎn)點(diǎn)經(jīng)驗(yàn),好吧,我決定寫一篇即便是新手也能馬上看懂的文章。 用途和使用場景先不要管 Grunt,我們先來看下微硬公司小明和小紅的日常前端開發(fā)工作: 小明在開發(fā)一個(gè) JS 插件,寫了好多代碼,終于寫完了,放在 HTML 文件里調(diào)用一看,console 里面好多 error,于是就挨個(gè)調(diào)整修改。為了排除深層潛在問題,他還打開了 http://www./ 這個(gè)網(wǎng)站,把自己的代碼復(fù)制進(jìn)去用 jshint 檢測了,結(jié)果發(fā)現(xiàn)了好多細(xì)節(jié)問題和不規(guī)范的地方,依次修改。最后要發(fā)布了,他又打開了 http://tool./ 網(wǎng)站,把自己的代碼復(fù)制進(jìn)去,使用 Uglify 來壓縮了一下,提供一個(gè)壓縮版本。然后上傳到了 Github 上托管。 這時(shí)候,Github 突然有一個(gè) Issue,他看了一下,原來有個(gè)疏忽的地方,又進(jìn)行了修改,然后打開 jsHint 和 Uglify 在線壓縮網(wǎng)站進(jìn)行檢查和壓縮,再次發(fā)布上去。 于此同時(shí),小紅在做一個(gè)活動(dòng)頁面的前端重構(gòu)工作。她打開了正在做的 HTML 頁面,由于使用了 Sass,所以她打開了 Koala 在后臺(tái)幫她自動(dòng)編譯成 CSS,但是每次保存一下,想要看到效果,還是需要切換到瀏覽器,刷新一下。做交互處理的時(shí)候,她寫了一些 JS,為了規(guī)范,也使用 jshint 檢查了一下。終于做完了,這時(shí)候做了一下最后的優(yōu)化處理準(zhǔn)備上線,她把 icons 在 Photoshop 中合并成了一張圖片,并在 CSS 的對(duì)應(yīng)位置修改了一下,然后用在線壓縮工具把 CSS 和 JS 都?jí)嚎s了一遍,提交測試去了。 這就是他倆的工作,日復(fù)一日年復(fù)一年,有一天,小明終于受夠了,朝小紅發(fā)牢騷:哥要是再來回復(fù)制粘貼到網(wǎng)站上排錯(cuò)壓縮,哥就是逗逼!小紅滿眼淚花的說,你想好辦法了,幫我解決一下自動(dòng)刷新問題,我也受夠了。 于是小明工作都不做了,開始苦思冥想怎么開發(fā)一個(gè)可以自動(dòng)把寫的代碼發(fā)送到遠(yuǎn)程網(wǎng)站,讓他們檢查一下錯(cuò)誤并且壓縮好,再反回來生成一個(gè)文件。 開發(fā)一個(gè)任務(wù)自動(dòng)處理器當(dāng)然,小明很快就不那么想了,因?yàn)橐蕾囃獠烤W(wǎng)站有很多意外因素,而且受限于網(wǎng)絡(luò)和網(wǎng)速,其次他們也沒提供有關(guān)接口來調(diào)用。但是他們都提供了一些算法之類的工具等。這樣可以在本地調(diào)用這些工具,來完成這些操作,甚至連網(wǎng)絡(luò)也不需要。 小明愉快的開始在筆記本上構(gòu)思這個(gè)工具的開發(fā)方法和需要的功能: 首先我需要先開發(fā)一個(gè)工具,可以調(diào)用這個(gè)工具對(duì)我的某個(gè)項(xiàng)目目錄里面的項(xiàng)目文件做一些操作,比如壓縮、查錯(cuò)、合并等。 如果要做成一個(gè)工具,可能不太好,或許別人還需要更多功能,但是我沒法開發(fā)這么多功能啊。要不我就做個(gè)框架把,然后每個(gè)功能做成一個(gè)插件,比如壓縮插件、合并插件。如果有人需要在他的項(xiàng)目里壓縮某個(gè)文件,他安裝一下我這個(gè)工具然后再安裝壓縮插件就好了。這樣有更多需求的人,可以自己編寫功能插件,然后配合我的工具使用。 慢著,他安裝完了工具和插件之后,要怎么來調(diào)用這個(gè)插件來處理項(xiàng)目文件?在程序界面上選擇文件,然后勾選選項(xiàng)?我的天,我就會(huì)寫點(diǎn) JS,哪里可能開發(fā)帶有界面的程序?慢著,用 JS ?他可以在項(xiàng)目文件夾中編寫一個(gè) JS 來設(shè)置任務(wù)??!然后我的工具會(huì)讀取這個(gè) JS,解析之后獲得他要執(zhí)行的任務(wù)(比如壓縮某某文件并改名成某某),然后調(diào)用插件完成任務(wù)。 太棒了。但是插件這么多,放在項(xiàng)目里肯定很大,而且又是不相關(guān)代碼,要不等他發(fā)布的時(shí)候自動(dòng)刪除這些插件文件把?不行,如果他要發(fā)給別人,別人要繼續(xù)開發(fā),還得重新安裝依次安裝這些插件,然后執(zhí)行任務(wù)。那怎么辦?要不我再用個(gè)文件記錄一下當(dāng)前項(xiàng)目中安裝或者需要的插件把!這樣只需要把這個(gè)文件和 JS 任務(wù)文件放在項(xiàng)目目錄里面,有需要的人,直接輸入一條命令安裝一下,然后立刻就可以執(zhí)行了。 我太聰明了! 于是小明興奮的跑過去給小紅講了一下他的工具的開發(fā)思路,然后告訴她,他可以幫忙寫一個(gè)自動(dòng)刷新的插件。小紅反而淡定的說:等等,我好像見過這種東西,這不是 Grunt 嘛? 小明抓緊搜了一下 Grunt,看了一下文檔之后,對(duì)著小紅:尼瑪,你為什么不早說! 開始學(xué)習(xí) GruntGrunt 就是小明想的這樣一種自動(dòng)化任務(wù)處理工具,它就是一個(gè)工具框架,有很多插件擴(kuò)展它的功能。 Grunt 基于 Node.js ,用 JS 開發(fā),這樣就可以借助 Node.js 實(shí)現(xiàn)跨系統(tǒng)跨平臺(tái)的桌面端的操作,例如文件操作等等。此外,Grunt 以及它的插件們,都作為一個(gè) 包 ,可以用 NPM 安裝進(jìn)行管理。 所以 NPM 生成的 package.json 項(xiàng)目文件,里面可以記錄當(dāng)前項(xiàng)目中用到的 Grunt 插件,而 Grunt 會(huì)調(diào)用 Gruntfile.js 這個(gè)文件,解析里面的任務(wù)(task)并執(zhí)行相應(yīng)操作。 如果你對(duì) Node.js、NPM 這些名詞不太熟悉,建議先去搜索了解一下,因?yàn)橄旅娴拿顣?huì)涉及到它們,但是本文不會(huì)過多介紹。 安裝 GruntGrunt 依賴 Node.js 所以在安裝之前確保你安裝了 Node.js。然后開始安裝 Grunt。 實(shí)際上,安裝的并不是 Grunt,而是 Grunt-cli,也就是命令行的 Grunt,這樣你就可以使用 grunt 命令來執(zhí)行某個(gè)項(xiàng)目中的 Gruntfile.js 中定義的 task 。但是要注意,Grunt-cli 只是一個(gè)命令行工具,用來執(zhí)行,而不是 Grunt 這個(gè)工具本身。 安裝 Grunt-cli 需要使用 NPM,使用下面一行即可在全局范圍安裝 Grunt-cli ,換句話說,就是你可以在任何地方執(zhí)行 grunt 命令: npm install -g grunt-cli
需要注意,因?yàn)槭褂?-g 命令會(huì)安裝到全局,可能會(huì)涉及到系統(tǒng)敏感目錄,如果用 Windows 的話,可能需要你用管理員權(quán)限,如果用 OS X / Linux 的話,你可能需要加上 sudo 命令。 下面我們新建一個(gè)項(xiàng)目目錄,并新建一些文件,這里我準(zhǔn)備了一份很簡單的項(xiàng)目,放在了 Github 上,下面操作將以此來操作,你可以下載或者 clone 下來: https://github.com/yujiangshui/gruntxx 生成 package.json 文件這個(gè) package.json 文件其實(shí)是 Node.js 來描述一個(gè)項(xiàng)目的文件,JSON 格式。生成這個(gè)文件超級(jí)簡單,推薦用命令行交互式的生成一下: 打開命令行, 填寫好了之后,查看目錄就會(huì)發(fā)現(xiàn)生成 package.json 文件了,這樣就算生成好了。 其實(shí)就是一個(gè)文件而已,你覺得這種方式麻煩,完全可以新建一個(gè)文件,然后將類似下面的代碼復(fù)制進(jìn)去,改一下對(duì)應(yīng)選項(xiàng),保存成 package.json 文件就可以: {
'name': 'my-project-name',
'version': '0.1.0',
'devDependencies': {
}
}
最后我生成的代碼如下: {
'name': 'gruntxx',
'version': '0.0.1',
'description': '學(xué)習(xí) grunt',
'repository': {
'type': 'git',
'url': 'https://github.com/yujiangshui/gruntxx.git'
},
'author': 'Jiangshui',
'license': 'MIT',
'bugs': {
'url': 'https://github.com/yujiangshui/gruntxx/issues'
},
'homepage': 'https://github.com/yujiangshui/gruntxx'
}
但這時(shí)我們還沒有在項(xiàng)目文件中安裝 Grunt 以及相關(guān)任務(wù)插件。 安裝 Grunt 和所需要的插件就現(xiàn)在的這個(gè)示例項(xiàng)目而言,我打算讓 Grunt 幫忙實(shí)現(xiàn)下面幾個(gè)功能:檢查每個(gè) JS 文件語法、合并兩個(gè) JS 文件、將合并后的 JS 文件壓縮、將 SCSS 文件編譯、新建一個(gè)本地服務(wù)器監(jiān)聽文件變動(dòng)自動(dòng)刷新 HTML 文件。 差不多就是這些,根據(jù)這些任務(wù)需求,需要用到:
它們的命名和文檔都很規(guī)范,因?yàn)檫@些是官方提供的比較常用的插件。這些插件同時(shí)都是 NPM 管理的包,比如 grunt-contrib-concat - npm 你也可以在這上面看到用法等。 下面我們就要在這個(gè)項(xiàng)目中安裝這些插件,執(zhí)行命令: npm install grunt --save-dev
表示通過 npm 安裝了 grunt 到當(dāng)前項(xiàng)目,同時(shí)加上了 –save-dev 參數(shù),表示會(huì)把剛安裝的東西添加到 package.json 文件中。不信你打開 package.json 文件看下,是不是多了 'devDependencies': {
'grunt': '^0.4.5'
}
沒錯(cuò),這個(gè)的意思就是當(dāng)前項(xiàng)目依賴 grunt,后面是它的版本,咱們不用管。如果安裝的時(shí)候沒有添加 –save-dev 參數(shù),這里就不會(huì)出現(xiàn)了,你需要自行添加上去。 下面我們來安裝 Grunt 的插件,當(dāng)然,不需要一個(gè)個(gè)的安裝,太麻煩了,我們可以: npm install --save-dev grunt-contrib-concat grunt-contrib-jshint grunt-contrib-sass grunt-contrib-uglify grunt-contrib-watch grunt-contrib-connect
等待一大串亂七八糟的下載狀態(tài),我們把 grunt 和相關(guān)插件都安裝好了,不信看下是不是多了一個(gè) node_modules 文件夾?打開看下,里面就是咱們剛安裝的插件。 配置 Gruntfile.js 的語法插件也裝好了,開始寫任務(wù)吧!既然是要程序來讀取執(zhí)行,必要要有一定的語法規(guī)范,下面來簡單的說一下: 首先要明白,這是一個(gè) JS 文件,你可以寫任意的 JS 代碼,比如聲明一個(gè) 對(duì)象 來存儲(chǔ)一會(huì)要寫任務(wù)的參數(shù),或者是一個(gè)變量當(dāng)作開關(guān)等等。 然后,所有的代碼要包裹在 module.exports = function(grunt) {
...
};
里面。沒有為什么。 在這里面的代碼,除去你自己寫的亂七八糟的 JS,與 Grunt 有關(guān)的主要有三塊代碼:任務(wù)配置代碼、插件加載代碼、任務(wù)注冊(cè)代碼。 顧名思義,這三塊代碼,任務(wù)配置代碼就是調(diào)用插件配置一下要執(zhí)行的任務(wù)和實(shí)現(xiàn)的功能,插件加載代碼就是把需要用到的插件加載進(jìn)來,任務(wù)注冊(cè)代碼就是注冊(cè)一個(gè) task,里面包含剛在前面編寫的任務(wù)配置代碼。 這樣,就可以用 grunt 來執(zhí)行注冊(cè)的一個(gè) task 從而根據(jù)任務(wù)配置代碼調(diào)用需要的插件來執(zhí)行相應(yīng)的操作。 下面來分別看一下這三塊代碼的寫法。 任務(wù)配置代碼例如下面代碼: grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today('yyyy-mm-dd') %> */\n'
},
build: {
src: 'src/<%= pkg.name %>.js',
dest: 'build/<%= pkg.name %>.min.js'
}
}
});
可以看出,具體的任務(wù)配置代碼以對(duì)象格式放在 也就是說,在 Uglify 插件下面,有一個(gè) build 任務(wù),內(nèi)容是把 XX.js 壓縮輸出到 xx.min.js 里面。如果你需要更多壓縮任務(wù),也可以參照 build 多寫幾個(gè)任務(wù)。 至于怎么寫出來 options 里面的參數(shù)和 build 里面的參數(shù)內(nèi)容,這才是 grunt 學(xué)習(xí)的難點(diǎn),你需要查看每個(gè)插件的用法,根據(jù)用法來編寫任務(wù),可以看下 grunt-contrib-uglify 的官方文檔,往下面拉你就可以看到參數(shù)和使用方法了。 這樣,我們就新建了一個(gè)基于 uglify 的任務(wù) build,功能是把 插件加載代碼這個(gè)就超級(jí)簡單了,由于上面任務(wù)需要用到 grunt-contrib-uglify,當(dāng) grunt-contrib-uglify 安裝到我們的項(xiàng)目之后,寫下下面代碼即可加載: grunt.loadNpmTasks('grunt-contrib-uglify');
任務(wù)注冊(cè)代碼插件也加載了,任務(wù)也布置了,下面我們得注冊(cè)一下任務(wù),使用 grunt.registerTask('default', ['uglify']);
來注冊(cè)一個(gè)任務(wù)。上面代碼意思是,你在 default 上面注冊(cè)了一個(gè) Uglify 任務(wù),default 就是別名,它是默認(rèn)的 task,當(dāng)你在項(xiàng)目目錄執(zhí)行 grunt 的時(shí)候,它會(huì)執(zhí)行注冊(cè)到 default 上面的任務(wù)。 也就是說,當(dāng)我們執(zhí)行 grunt 命令的時(shí)候,uglify 的所有代碼將會(huì)執(zhí)行。我們也可以注冊(cè)別的 task,例如: grunt.registerTask('compress', ['uglify:build']);
如果想要執(zhí)行這個(gè) task,我們就不能只輸入 grunt 命令了,我們需要輸入 這里需要注意的是,task 的命名不能與后面的任務(wù)配置同名,也就是說這里的 compress 不能命名成 uglify,這樣會(huì)報(bào)錯(cuò)或者產(chǎn)生意外情況 OK,加上這三塊代碼,我們的示例 Gruntfile.js 就是這樣子的: module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today('yyyy-mm-dd') %> */\n'
},
build: {
src: 'src/<%= pkg.name %>.js',
dest: 'build/<%= pkg.name %>.min.js'
}
}
});
// Load the plugin that provides the 'uglify' task.
grunt.loadNpmTasks('grunt-contrib-uglify');
// Default task(s).
grunt.registerTask('default', ['uglify']);
};
這就是官方那個(gè)坑爹示例,貌似 uglify 的參數(shù)好像不對(duì),反正我之前學(xué)習(xí)的時(shí)候,沒法運(yùn)行這個(gè)配置,下面我們來根據(jù)示例項(xiàng)目和我們的需求配置一下。 配置 Gruntfile.js先從簡單的入手,我們先來配置一下編譯 Scss 文件的 task。先新建一個(gè) Gruntfile.js 文件,把大體的配置結(jié)構(gòu)復(fù)制進(jìn)去: module.exports = function(grunt) {
var sassStyle = 'expanded';
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
sass: {
}
});
grunt.loadNpmTasks('grunt-contrib-sass');
grunt.registerTask('outputcss',['sass']);
grunt.registerTask('default');
};
應(yīng)該可以看懂把?這里不再贅述了,我們來根據(jù) Sass 文檔,編寫一個(gè) Sass 任務(wù) output : module.exports = function(grunt) {
var sassStyle = 'expanded';
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
sass: {
output : {
options: {
style: sassStyle
},
files: {
'./style.css': './scss/style.scss'
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-sass');
grunt.registerTask('outputcss',['sass']);
grunt.registerTask('default');
};
意思就是將 下面拿起命令行,cd 到當(dāng)前文檔目錄,執(zhí)行一下 grunt 命令,結(jié)果報(bào)錯(cuò) undefined,沒錯(cuò),因?yàn)槲覀兊?default task 里面沒有定義任何任務(wù),然后執(zhí)行 下面我們打算先把 src 目錄下面的兩個(gè) JS 文件合并起來,然后再用 jshint 檢測一下是否有語法問題,如果正確,再用 uglify 對(duì)合并起來的文件進(jìn)行壓縮。 參照 grunt-contrib-concat 的官方文檔,我們可以寫出下面的任務(wù): module.exports = function(grunt) {
var sassStyle = 'expanded';
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
sass: {
output : {
options: {
style: sassStyle
},
files: {
'./style.css': './scss/style.scss'
}
}
},
concat: {
options: {
separator: ';',
},
dist: {
src: ['./src/plugin.js', './src/plugin2.js'],
dest: './global.js',
},
}
});
grunt.loadNpmTasks('grunt-contrib-sass');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.registerTask('outputcss',['sass']);
grunt.registerTask('concatjs',['concat']);
grunt.registerTask('default');
};
執(zhí)行 module.exports = function(grunt) {
var sassStyle = 'expanded';
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
sass: {
output : {
options: {
style: sassStyle
},
files: {
'./style.css': './scss/style.scss'
}
}
},
concat: {
options: {
separator: ';',
},
dist: {
src: ['./src/plugin.js', './src/plugin2.js'],
dest: './global.js',
},
},
uglify: {
compressjs: {
files: {
'./global.min.js': ['./global.js']
}
}
},
jshint: {
all: ['./global.js']
}
});
grunt.loadNpmTasks('grunt-contrib-sass');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.registerTask('outputcss',['sass']);
grunt.registerTask('concatjs',['concat']);
grunt.registerTask('compressjs',['concat','jshint','uglify']);
grunt.registerTask('default');
};
其中注冊(cè)了一個(gè) compressjs 任務(wù) grunt 遇到錯(cuò)誤就退出了,就沒法繼續(xù)執(zhí)行下面的任務(wù)。通過錯(cuò)誤提示可以發(fā)現(xiàn),是因?yàn)?concat 里面設(shè)置的參數(shù)——在兩個(gè)文件合并間插入一個(gè)“;”——這本來是為了防止兩個(gè)文件之間相互干擾設(shè)置的,結(jié)果無法被 jshint 驗(yàn)證通過,我們可以刪掉這個(gè)參數(shù),或者設(shè)置 jshint 驗(yàn)證這兩個(gè)文件,然后再進(jìn)行合并。 為了方便,我刪掉了這個(gè)參數(shù)。再執(zhí)行一下,成功了,項(xiàng)目目錄里面多了 global.js 和 global.min.js 文件。 小明看到這里,痛哭流淚,自己每次打開好幾個(gè)網(wǎng)站,辛苦挨個(gè)粘貼復(fù)制新建,沒想到輸入一條命令就可以了。不過讓他更傷心的還在后面,連這些命令都不用重復(fù)輸入。 我們可以通過 watch 來監(jiān)聽文件變動(dòng),當(dāng)文件變化了(我們編寫保存了),自動(dòng)執(zhí)行某些任務(wù)。此處為了節(jié)約版面,我連自動(dòng)刷新的任務(wù)一塊寫上去。根據(jù) grunt-contrib-watch 和 grunt-contrib-connect 這倆文檔,我們可以寫出下面的任務(wù): module.exports = function(grunt) {
var sassStyle = 'expanded';
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
sass: {
output : {
options: {
style: sassStyle
},
files: {
'./style.css': './scss/style.scss'
}
}
},
concat: {
dist: {
src: ['./src/plugin.js', './src/plugin2.js'],
dest: './global.js',
},
},
uglify: {
compressjs: {
files: {
'./global.min.js': ['./global.js']
}
}
},
jshint: {
all: ['./global.js']
},
watch: {
scripts: {
files: ['./src/plugin.js','./src/plugin2.js'],
tasks: ['concat','jshint','uglify']
},
sass: {
files: ['./scss/style.scss'],
tasks: ['sass']
},
livereload: {
options: {
livereload: '<%= connect.options.livereload %>'
},
files: [
'index.html',
'style.css',
'js/global.min.js'
]
}
},
connect: {
options: {
port: 9000,
open: true,
livereload: 35729,
// Change this to '0.0.0.0' to access the server from outside
hostname: 'localhost'
},
server: {
options: {
port: 9001,
base: './'
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-sass');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.registerTask('outputcss',['sass']);
grunt.registerTask('concatjs',['concat']);
grunt.registerTask('compressjs',['concat','jshint','uglify']);
grunt.registerTask('watchit',['sass','concat','jshint','uglify','connect','watch']);
grunt.registerTask('default');
};
添加了 connect 任務(wù),用來新建一個(gè)本地服務(wù)器,以當(dāng)前目錄作為服務(wù)器根目錄,然后添加 watch 任務(wù),監(jiān)聽 Scss 文件變動(dòng),如果變了,執(zhí)行一下 sass 任務(wù),監(jiān)聽那倆 JS,如果變了,執(zhí)行 合并、檢查、壓縮 任務(wù),監(jiān)聽 html、css、js 文件,如果變動(dòng),livereload 自動(dòng)刷新打開的頁面。 而注冊(cè)的 watchit task 就是我們的終極 task,第一次執(zhí)行,先編譯 sass、再合并、檢查、壓縮、開啟服務(wù)器、監(jiān)聽文件變動(dòng)。我們執(zhí)行一下 看到這里,小明和小紅相擁而泣。。。。 項(xiàng)目文件傳輸與協(xié)作項(xiàng)目開發(fā)完成之后,往往需要 push 到 Github 或者上傳 FTP 等?;蛟S其他人會(huì)接手你的項(xiàng)目繼續(xù)開發(fā),或者你會(huì)換臺(tái)電腦進(jìn)行開發(fā)。 當(dāng)小明用 git 上傳 Github 的時(shí)候,傻了眼,項(xiàng)目里 node_modules 文件夾下面的東西要十幾M呢,這比我項(xiàng)目本身還大,上傳下載都不方便。 其實(shí)這些插件和 grunt 不需要上傳,因?yàn)橛?package.json 這個(gè)文件記錄了你這個(gè)項(xiàng)目中依賴的 grunt 插件,你只需要上傳這個(gè)文件即可。下載下來之后,只需要在這個(gè)項(xiàng)目文件夾下面,輸入命令 也不需要在本地上傳的時(shí)候刪除,用 git 的話,可以使用 .gitignore 文件來過濾掉這個(gè)文件夾,禁止 git 追蹤。 總結(jié)與擴(kuò)展閱讀Grunt 就是這樣一種任務(wù)自動(dòng)運(yùn)行器,應(yīng)用好它可以減輕很多不必要的人工操作,只需要專注 coding 就可以。甚至還有Grunt 插件幫你自動(dòng)完成 CSS Sprite,更多功能還需要你自己去摸索。 新手看完本文,再看一下 Grunt 官方文檔 應(yīng)該沒有太多疑問了,那就再看一遍把。當(dāng)然也有中文版。 除了 Grunt 之外,同類型比較火的還有 Gulp 這個(gè)工具。其實(shí)兩個(gè)東西的功能是一樣的,只不過是任務(wù)配置 JS 的語法不同,Gulp 的 Gulpfile.js 的寫法更加通俗易懂,上手更快。但是 Gulp 的插件等感覺不如 Grunt,Grunt 官方提供了一些常見的插件,滿足大部分日常工作,而且可靠值得信賴,而 Gulp 好像沒有太多官方出品,各種插件不太規(guī)范。簡單的說,Grunt 和 Gulp 就像 iPhone 與 Android 一樣,一個(gè)質(zhì)量高學(xué)習(xí)難一點(diǎn),一個(gè)學(xué)起來簡單但是有點(diǎn)那個(gè),你懂得。 此外,可以看一些高手的項(xiàng)目,你會(huì)發(fā)現(xiàn)更好的 Grunt 用法,比如 Yeoman 生成的項(xiàng)目,就有很完善的 Grunt 任務(wù)和插件,此外,jQuery 等也用 Grunt 進(jìn)行打包,這些 Grunt 文件你都可以查看研究一下他們的寫法和用法,受益匪淺。 最后,如果你懶得跟著文章一點(diǎn)點(diǎn)的配置示例項(xiàng)目,你也可以跳轉(zhuǎn)到示例項(xiàng)目的 grunt 分支,這里面是我配置好的,你需要先 |
|