文章還可在我的github上找到,排版更友好一點:grunt從入門到自定義項目模板
一.Grunt入門介紹
1. Grunt是神馬
基於任務的命令行構建工具(針對JavaScript項目)
2. 使用Grunt的理由
前端的工具算得上是五花八門,在介紹如何Grunt之前,首先我們得反問自己:
- Grunt能夠幫我們解決什么問題?
- 是否有其他更合適的替代方案?
3. Grunt能夠幫我們解決什么問題?
作為一名開發人員,我們見過了不少功能胡里花哨但並不實用的工具。但是,我們很少會因為一個工具功能很強大而去使用它。更多地,是因為在工作中我們遇到了一些問題,而某個工具剛好幫我們解決了這些問題。
假設我們有個叫IMWEB_PROJ
的項目,該項目主要包含兩個功能模塊,分別是moduleA
、moduleB
。回想一下,作為一名前端開發人員,從功能開發到產品正式上線,我們的工作流程是什么樣的:
正式進入編碼工作前,得做些准備工作:
- 新建目錄
IMWEB_PROJ
,index.html為主入口;根目錄下面再另外新建三個目錄js、css、img,分別用來存放js文件、css文件、圖片 - IMWEB_PROJ/js下新建個main.js作為項目主邏輯的入口,添加moduleA.js、modueB.js,對了,不能把我們的基礎組件Badjs.js、simple.js、nohost.js給忘了
- IMWEB_PROJ/css下新建個reset.css,添加moduleA.css、moduleB.css
熱火朝天地編碼,產品終於即將上線,上線前的准備工作同樣不能馬虎
JSHint
——檢查下JS代碼規范性,避免進行類似隱式全局變量這樣的坑里concat
——JS文件合並,合理減少請求數,提升加載速度cssmin
——CSS文件合並,合理減少請求數,提升加載速度Uglyfy
——壓縮文件,減少文件尺寸,提升用戶側加載速度QUnit
——單元測試,提高項目可維護性,結合遞歸測試可盡早發現潛在問題- …
上面的場景是不是很眼熟?重復而枯燥的工作占據了我們太多的時間,忘了誰說過,當重復做一件事超過三次,就應該考慮將它自動化。
Grunt正是為了解決上述問題而誕生,它將上面提到的項目結構生成、JSHint檢查、文件合並、壓縮、單元測試等繁瑣的工作變成一個個可自動化完成的任務,一鍵搞定。
4. 其他使用Grunt的理由
- 文檔豐富:詳細的使用說明,從入門使用,到高級定制,非常詳盡
- 插件豐富:基本能夠想到的常用的任務,都可以找到
- 社區活躍:Grunt的開發團隊還是挺勤勞的,社區活躍度也挺高
5. 是否有其他更合適的替代方案?
當然有,而且不少,Ant、Yeoman、Mod、Fiddler+willow+qzmin等,先不展開
二. 從零開始使用Grunt
參考鏈接:http://gruntjs.com/getting-started
Grunt使用場景通常分兩種:
- 維護現有的Grunt項目——已經配置好的項目,下面以jQuery Plugin項目為例進行講解,簡單了解下一個Grunt項目的基本結構;
- 新創建的Grunt項目——包括項目目錄結構的創建,到Grunt任務的配置等;這里可以采用現有的Grunt模板,也可以采用自定義的模板;下文會采用自定義模板的形式,逐步講解如何創建一個**IMWEB團隊的前端基礎項目結構**
1. 環境以及依賴
Grunt以及Grunt的插件,都是通過npm進行安裝和管理,所以首先得安裝node環境,不贅述,見 http://nodejs.org/
2. 關於版本
注意:為了解決多版本並存的問題,從0.4.x
版本開始,每個項目需獨立安裝Grunt及對應插件,版本分別如下:
- Grunt
0.4.x
- Nodejs
>=0.8.0
3. 卸載老版本Grunt(版本<0.4.0)
grunt從版本0.3.X到0.4.x,變化比較大,主要是為了解決Grunt多版本共存的問題,有興趣的童鞋可以了解下。如果之前安裝了0.3.x版本,需先進行卸載
npm uninstall -g grunt
4. 安裝grunt-cli
grunt-cli的主要作用是讓我們可以運行Grunt命令,加上-g,則可以在任意目錄下運行,不展開
npm install -g grunt-cli
5. 安裝grunt-init
grunt-init是個腳手架工具,它可以幫你完成項目的自動化創建,包括項目的目錄結構,每個目錄里的文件等。具體情況要看你運行grunt-init指定的模板,以及創建過程中你對問題的回答,下文會簡單講到這個功能。
先運行下面命令安裝grunt-init,
npm install -g grunt-init
下面我們先通過安裝jQuery Plugin模板,來展示Gurnt模板的安裝,項目的創建,以及一個Grunt項目的目錄結構
三、jQuery Plugin示例:如何通過現有模板創建項目、運行Grunt任務
參考連接:http://gruntjs.com/project-scaffolding
1. 安裝jQuery Plugin模板
下面命令可以查看官方維護的Grunt模板
grunt-init --help
運行下面命令安裝jQuery模板
git clone git@github.com:gruntjs/grunt-init-jquery.git ~/.grunt-init/jquery
2. 根據jQuery Plugin模板創建項目
在上一步中我們已經安裝好了jQuery模板,接着運行下面命令,安裝jQuery項目
grunt-init jquery
按照引導回答下面問題,完成項目的創建
Please answer the following:
[?] Project name (test) DemoJQuery
[?] Project title (DemojQuery)
[?] Description (The best jQuery plugin ever.) just for test
[?] Version (0.1.0) 1.0.0
[?] Project git repository (git://github.com/root/test.git)
[?] Project homepage (https://github.com/root/test)
[?] Project issues tracker (https://github.com/root/test/issues)
[?] Licenses (MIT)
[?] Author name (none) 程序 猿 小卡
[?] Author email (none)
[?] Author url (none) http://chyingp.cnblogs.com
[?] Required jQuery version (*) 1.9.0
[?] Do you need to make any changes to the above before continuing? (y/N) N
項目目錄結構如下:
//項目目錄結構
-rw-r--r-- 1 root staff 1670 5 9 15:13 CONTRIBUTING.md
-rw-r--r-- 1 root staff 559 5 9 15:13 DemoJQuery.jquery.json
-rw-r--r-- 1 root staff 2184 5 9 15:13 Gruntfile.js
-rw-r--r-- 1 root staff 1053 5 9 15:13 LICENSE-MIT
-rw-r--r-- 1 root staff 543 5 9 15:13 README.md
drwxr-xr-x 5 root staff 170 5 9 15:13 libs
-rw-r--r-- 1 root staff 423 5 9 15:13 package.json
drwxr-xr-x 4 root staff 136 5 9 15:13 src
drwxr-xr-x 5 root staff 170 5 9 15:13 test
從上面的目錄結構,大致可以看出各個目錄、文件的作用,其中我們需要注意的是兩個文件Gruntfile.js、package.json,這兩個文件都需要放在項目跟目錄下。下面會稍微詳細介紹到:
- Gruntfile.js 項目的Grunt配置信息,包括模塊依賴、任務定義
- package.json 項目node模塊的依賴信息,主要根據Gruntfile生成
其他其他文件非Grunt項目必須的,可以暫時不去看它
3. 運行Grunt任務
首先運行下面命令,安裝所需node模塊,耐心等候安裝完即可
npm install
輸入下面命令,運行Grunt任務
grunt
輸出如下,done
Running "jshint:gruntfile" (jshint) task
>> 1 file lint free.
Running "jshint:src" (jshint) task
>> 1 file lint free.
...
4. 如何創建package.json
方式一:運行下面命令,通過逐步回答問題的方式創建基礎的package.json文件2```
npm install
方式二:創建空的package.json文件,拷貝下面內容,根據需要進行修改
{ "name": "HelloProj",
"version": "0.1.0",
"devDependencies": {
"grunt": "~0.4.1",
"grunt-contrib-jshint": "~0.1.1",
"grunt-contrib-nodeunit": "~0.1.2"
}
}
創建完package.json,運行如下命令,安裝所需插件
npm install
5. 如何安裝Grunt
運行如下命令,安裝最新版
的Grunt
npm install grunt --save-dev
6. 創建Gruntfile.js
Gruntfile.js的配置文件格式並不復雜,不過剛開始看的時候會有些雲里霧里,直接拿官方范例進行修改即可。參考鏈接:http://gruntjs.com/sample-gruntfile
module.exports = function(grunt) {
// 項目配置信息,這里只是演示用,內容隨便填的
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: { //壓縮文件
build: {
src: 'src/<%= pkg.name %>.js',
dest: 'build/<%= pkg.name %>.min.js'
}
},
concat: { //合並文件
js:{
src: ['js/moduleA.js', 'js/moduleB.js'],
dest: 'dist/js/moduleA-moduleB.js'
},
css:{
src:['dist/css/moduleA.css', 'dist/css/moduleB.css'],
dest: 'dist/css/moduleB.css'
}
}
});
// 加載uglify插件,完成壓縮任務
grunt.loadNpmTasks('grunt-contrib-uglify');
// 加載concat插件,完成文件合並任務
grunt.loadNpmTasks('grunt-contrib-concat');
// 默認任務,如果運行grunt命令,且后面沒有指定任務,或為defalut時,運行這個
grunt.registerTask('default', ['concat', 'uglify']);
};
其實這種方式還是有點麻煩,Grunt團隊還是比較人性化的,針對Gruntfile,還提供了一個單獨的plugin,讓我們免去重復勞動之苦,后面再講
四. imweb_template:自定義模板,創建IMWEB團隊專屬的前端項目骨架
1. 下載Grunt官方示例模板
下載鏈接:https://github.com/gruntjs/grunt-init-jquery
打開下載下來的示例目錄,可以看到如下內容:
-rwxr-xr-x@ 1 casperchen staff 877 2 18 09:00 README.md
-rwxr-xr-x@ 1 casperchen staff 138 2 18 09:00 rename.json
drwxr-xr-x@ 10 casperchen staff 340 2 18 09:00 root
-rwxr-xr-x@ 1 casperchen staff 3521 2 18 09:00 template.js
簡單介紹下里面內容:
template.js
主模板文件,非常重要!里面主要內容有:項目創建時需要回答的問題,項目依賴的Grunt模塊(根據這個生成package.json)rename.json
針對當前模板的目錄/文 件重命名規則,不贅述root/
重要!在這個目錄里的文件,通過該模板生成項目結構時,會將root目錄下的文件都拷貝到項目中去
2. 創建自定義項目之前
將之前下載的grunt-init-jquery-master
重命名為imweb_template
,然后就開始我們的模板自定義之旅了!鑒於這塊的內容實在太多,就不詳細講解,直接貼上修改后的文件,可以更為直觀,如需深入了解,可查看相關鏈接:http://gruntjs.com/project-scaffolding
3. 修改imweb_template/template.js
下面是template.js最常包含的一些內容,主要包括:
exports.description
模板簡單介紹信息exports.notes
開始回答項目相關問題前,控制台打印的相關信息exports.after
開始回答項目相關問題前,控制台打印的相關信息init.process
項目創建的時候,需要回答的問題init.writePackageJSON
生成package.json,供Grunt、npm使用
/*
* 模板名字
* https://gruntjs.com/
*
* 版權信息
* Licensed under the MIT license.
*/
'use strict';
// 模板簡單介紹信息
exports.description = '創建IMWEB專屬模板,帶文件合並壓縮哦!';
// 開始回答項目相關問題前,控制台打印的相關信息
exports.notes = '這段信息出現位置:回答各種項目相關的信息之前 ' +
'\n\n'+
'逐個填寫就行,如果不想填的會可以直接enter跳過';
// 結束回答項目相關問題后,控制台打印出來的信息
exports.after = '項目主框架已經搭建好了,現在可以運行 ' +
'\n\n' +
'1、npm install 安裝項目依賴的node模塊\n'+
'2、grunt 運行任務,包括文件壓縮、合並、校驗等\n\n';
// 如果運行grunt-init運行的那個目錄下,有目錄或文件符合warOn指定的模式
// 則會跑出警告,防止用戶不小心把當前目錄下的文件覆蓋了,一般都為*,如果要強制運行,可加上--force
// 例:grunt-init --force imweb_template
exports.warnOn = '*';
// The actual init template.
exports.template = function(grunt, init, done) {
init.process({type: 'IMWEB'}, [
// 項目創建的時候,需要回答的問題
init.prompt('name'),
init.prompt('title'),
init.prompt('description', 'IMWEB項目骨架'),
init.prompt('version', '1.0.0'),
init.prompt('author_name'),
init.prompt('author_email'),
], function(err, props) {
props.keywords = [];
// 需要拷貝處理的文件,這句一般不用改它
var files = init.filesToCopy(props);
// 實際修改跟處理的文件,noProcess表示不進行處理
init.copyAndProcess(files, props, {noProcess: 'libs/**'});
// 生成package.json,供Grunt、npm使用
init.writePackageJSON('package.json', {
name: 'IMWEB-PROJ',
version: '0.0.0-ignored',
npm_test: 'grunt qunit',
node_version: '>= 0.8.0',
devDependencies: {
'grunt-contrib-jshint': '~0.1.1',
'grunt-contrib-qunit': '~0.1.1',
'grunt-contrib-concat': '~0.1.2',
'grunt-contrib-uglify': '~0.1.1',
'grunt-contrib-cssmin': '~0.6.0',
'grunt-contrib-watch': '~0.2.0',
'grunt-contrib-clean': '~0.4.0',
},
});
// All done!
done();
});
};
4. 修改imweb_template/rename.json
reame.json的作用比較簡單,定義了從root目錄將文件拷貝到實際項目下時的路徑映射關系,以sourcepath: destpath
的形式聲明。sourcepath是相對於root的路徑,而destpath則是相對於實際項目的路徑。
ps:當destpath為false時,sourcepath對應的文件不會被拷貝到項目中去
{
"src/*.js": "js/*.js",
"test/test.html": "test/test.html"
}
5. imweb_template/root 目錄
進入root目錄,可以看到很多文件,其中我們需要關注的有Gruntfile.js、README.md
- Gruntfile.js 項目的任務配置信息,把基礎任務,如jshint、concat、uglify等配置好即可,其他的各個任務可自行擴充
- README.md 項目的readme信息,一個調理清晰的readme很重要
-rwxr-xr-x@ 1 casperchen staff 2408 5 10 09:34 Gruntfile.js
-rwxr-xr-x@ 1 casperchen staff 605 2 18 09:00 README.md
drwxr-xr-x 4 casperchen staff 136 5 9 20:31 css
drwxr-xr-x@ 8 casperchen staff 272 5 9 20:44 js
drwxr-xr-x@ 5 casperchen staff 170 2 18 09:00 libs
drwxr-xr-x@ 5 casperchen staff 170 2 18 09:00 test
6. 修改Gruntfile.js
對Gruntfile.js文件進行修改,如下,熟悉qzmin配置文件的童鞋應該很容易看懂
'use strict';
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
// Metadata.
pkg: grunt.file.readJSON('package.json'),
banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
'<%= grunt.template.today("yyyy-mm-dd") %>\n' +
'* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +
' */\n',
// 任務配置信息
clean: { // Grunt任務開始前的清理工作
files: ['dist']
},
concat: { //文件壓縮
js_and_css: {
files: {
// js文件合並
'dist/js/base.js': ['js/simple.js', 'js/badjs.js', 'js/nohost.js'],
'dist/js/main.js': ['js/moduleA.js', 'js/moduleB.js' 'js/main.js'],
// css文件合並
'dist/css/style.css': ['css/reset.css', 'css/moduleA.css', 'css/moduleB.css']
}
}
},
uglify: { //js文件壓縮
js: {
files: {
'dist/js/base.min.js': ['dist/js/base.js'],
'dist/js/main.min.js': ['dist/js/main.js']
}
}
},
cssmin:{ //CSS文件壓縮
css: {
files: {
'dist/css/style.min.css': ['dist/css/style.css']
}
}
},
qunit: { //單元測試,范例中未啟用
files: ['test/**/*.html']
},
jshint: { //文件校驗,范例中未啟用
gruntfile: {
options: {
jshintrc: '.jshintrc'
},
src: 'Gruntfile.js'
},
src: {
options: {
jshintrc: 'js/.jshintrc'
},
src: ['js/**/*.js']
},
test: {
options: {
jshintrc: 'test/.jshintrc'
},
src: ['test/**/*.js']
}
},
watch: { //watch任務,實時監聽文件的變化,並進行編譯
gruntfile: {
files: '<%= jshint.gruntfile.src %>',
tasks: ['jshint:gruntfile']
},
src: {
files: '<%= jshint.src.src %>',
tasks: ['jshint:src', 'qunit']
},
test: {
files: '<%= jshint.test.src %>',
tasks: ['jshint:test', 'qunit']
}
},
});
// 加載各種grunt插件完成任務
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-contrib-qunit');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
// 默認任務
grunt.registerTask('default', ['clean', 'concat', 'uglify', 'cssmin']);
//grunt.registerTask('default', ['jshint', 'qunit', 'clean', 'concat', 'uglify']);
};
7. 進入實戰
花了一點時間把IMWEB_PROJ配置好,現在終於到了實際運作階段了。假設我們當前在目錄IMWEB_PROJ下,imweb_template為IMWEB_PROJ目錄當前僅有的內容
drwxr-xr-x@ 8 casperchen staff 272 5 10 00:59 imweb_template
操作步驟可參照**jQuery Plugin示例:如何通過現有模板創建項目、運行Grunt任務**,下面直接上命令
grunt-init --force imweb_template/
npm install
grunt
下面為運行grunt命令后控制台輸出的信息
Running "clean:files" (clean) task
Cleaning "dist"...OK
Running "concat:js_and_css" (concat) task
File "dist/js/base.js" created.
File "dist/js/main.js" created.
File "dist/css/style.css" created.
Running "uglify:js" (uglify) task
File "dist/js/base.min.js" created.
Uncompressed size: 96927 bytes.
Compressed size: 7609 bytes gzipped (34814 bytes minified).
File "dist/js/main.min.js" created.
Uncompressed size: 926 bytes.
Compressed size: 93 bytes gzipped (305 bytes minified).
Running "cssmin:css" (cssmin) task
File dist/css/style.min.css created.
Done, without errors.
可以看到HelloProj目錄下的內容發生了改變,enjoy yourself!
-rw-r--r-- 1 root staff 2398 5 10 14:39 Gruntfile.js
-rw-r--r-- 1 root staff 605 5 10 14:37 README.md
drwxr-xr-x 6 root staff 204 5 10 14:37 css
drwxr-xr-x 4 root staff 136 5 10 14:39 dist
drwxr-xr-x@ 8 casperchen staff 272 5 10 00:59 imweb_template
drwxr-xr-x 10 root staff 340 5 10 14:37 js
drwxr-xr-x 5 root staff 170 5 9 20:17 libs
drwxr-xr-x 10 casperchen staff 340 5 10 09:28 node_modules
-rw-r--r-- 1 root staff 458 5 10 14:37 package.json
drwxr-xr-x 4 root staff 136 5 9 20:17 src
drwxr-xr-x 5 root staff 170 5 9 20:17 test
五. 關於Grunt、Ant、Mod的對比
上面對Grunt進行了入門介紹,下面簡單說下Ant、aven
- Ant:做過java開發的童鞋一般都不會陌生,功能很強大,相對Grunt來說更容易入門,配置文件更加友好,據說yahoo前端團隊用的Ant,推薦個不錯的教程:http://www.book.36ria.com/ant/index.html#index
- Mod:元彥童鞋開發維護,功能很強大,grunt能完成的,Mod都能完成,而且使用更加貼近我們的項目實踐,入門更簡單(有部分原因是因為mod集成了很多常用戶任務,而Grunt早期也是這么做的,不過因為多版本的問題放棄了這種做法),之前聽過元彥的分享,挺不錯的,打算在項目中試用下。github地址:https://github.com/modulejs/modjs
六. 寫在最后
由於時間問題,這里沒有對Grunt、Ant、Mod進行詳細的對比,來個todo吧~~