使用 Gulp 構建 AngularJS / Jade 項目
我所經歷的大部分項目,并不是純粹的前端項目,相關的前端文件,都是使用 Express 來處理,除了 Jade 文件之外的,全部放在 Express 靜態文件目錄public中,Bower 也配置為將依賴包直接安裝到public/lib目錄,然后直接使用原路徑在 HTML 中引用對應的 JavaScript / CSS 文件,經常是一個頁面加載幾十個靜態資源。
上周在做一個 App 項目的商戶端,對應的 API 已經完成,只是用 AngularJS 來實現一個前端 Web 頁面,其中的 HTML 使用了 Jade 來完成。由于是一個完全的前端項目,終于決定嘗試用 Gulp 來進行構建。整體的需求如下:
- 使用 Bower 管理前端依賴
- 需要將 Jade 文件編碼成 HTML,并按照 Angular New Router 中的 Components 來組織目錄結構
- 將 CSS / JavaScript 文件組裝為單個文件
- 圖片和字體等靜態資源存放到對應目錄
- 使用 Gulp 創建一個調試用的服務器,并能夠修改文件后自動重載瀏覽器頁面 </ul>
項目的目錄與文件結構
整體文件夾結構如下:
├── app │ ├── controllers // AngularJS 控制器 │ │ └── home.js │ ├── modules // AngularJS 模塊 │ │ └── app.js │ ├── services // AngularJS 服務 │ │ └── city.client.service.js │ └── views // Jade 文件 │ ├── index.jade │ └── _partial │ └── home.jade ├── bower.json ├── config.js ├── gulpfile.js ├── package.json ├── README.md └── static ├── css │ └── style.css └── images ├── avatar.png └── logo.gif
另外還有個public目錄,作為發布目錄,提供給 Web 服務器對外發布。所有需要在瀏覽器使用的文件,最后使用都要生成或者復制到public中。
使用 Bower 管理前端依賴
悲劇,在我定這篇文章的大綱的時候, Bower 還在更新,結果沒兩天就宣布不再開發了。
Bower 之所以在以前統治著前端包管理領域,原因在于它的扁平化包管理, NPM 中每個模塊都有獨立的屬于各自的目錄,來存儲對應的依賴包,雖然會占用比較多的磁盤,但卻可以防止模塊版本不同而造成的依賴問題。 Bower 本身并不直接決定應用的包依賴,它將模塊的依賴同模塊本身一樣安裝。
自從 NPM 成立專門的公司來運營以后,已經致力于將自己從Node Package Manager提升為JavaScript Package Manager。所以也開始像 Bower 來組織模塊的依賴—— Bower 存在的理由又少了一個。
這兩天,網上正在嘲笑bower --save并不會把當前已經安裝的依賴存儲到bower.json,不過我懷疑他們沒有看到過 NPM 3.3.x 是怎么處理依賴的,npm --save后的package.json估計會相當的不堪入目吧。
Jade 模板文件轉換為 HTML 文件
以前 Jade 文件是使用 Express 的view engine來轉譯,在專門的路由文件中,一一按照 Angular New Router 的 Components 標準來進行解析。使用 Gulp 轉換也是類似,借助gulp-jade模塊,設置gulp.src為 jade 文件路徑,gulp.dest為轉換后的 HTML 文件路徑,為了方便,將需要轉換的 jade 文件和對應的路徑組成一個數組,再在gulp.task中對數據進行遍歷,并執行轉換。
var gulp = require('gulp'); var jade = require('gulp-jade');var jadeFiles = [ {src: './app/views/index.jade', dest: './public/'}, {src: './app/views/_partial/home.jade', dest: './public/components/home/'} ];
gulp.task('jade', function(){ jadeFiles.forEach(function(jf){ if(!jf.src || !jf.dest) return; gulp.src(jf.src) .pipe(jade({petty: true})) .pipe(gulp.dest(jf.dest)); }); });</pre>
將 JavaScript / CSS 文件組裝為單個文件
對于遵循 AngularJS 模塊化設計的前端應用, JavaScript 文件那必然是相當多,再加上使用一些擴展,就算是中小型項目,超過 70 個以上那也是相當常見,看 Chrome 開發工具中的 Network 頁那可以部是相當精彩,
如果只是將多個 JavaScript 和 CSS 文件合為一個,使用gulp-concat模塊即可,壓縮 JavaScript 文件,可以再加一個gulp-uglify模塊,壓縮 CSS 文件,可以使用gulp-minify-css模塊。gulp.src支持使用數組的方式來指定要處理的文件列表。
var gulp = require('gulp'); var concat = require('gulp-concat'); var uglify = require('gulp-uglify'); var minifyCss = require('gulp-minify-css'); var jsFiles = [ './bower_components/jquery/dist/jquery.js', './bower_components/bootstrap/dist/js/bootstrap.min.js', ',/bower_components/PACE/pace.min.js', './bower_components/angular/angular.js', './bower_components/angular-new-router/dist/router.es5.js', './bower_components/at-table/dist/angular-table.js', './app/modules/businessApp.js', './app/controllers/home.js' ]; var cssFiles = [ './bower_components/bootstrap/dist/css/bootstrap.min.css', './bower_components/font-awesome/css/font-awesome.min.css', './bower_components/PACE/themes/blue/pace-theme-loading-bar.css', './static/css/start.css' ]; // 在這兩個 `min` 任務之外,還有兩個不帶 `min` 的任務,區別在于不對文件壓縮 gulp.task('scripts_min', function(){ return gulp.src(jsFiles) .pipe(concat('all.js')) // 合并 JavaScript ,并設置合并后的文件名 .pipe(uglify()) // 執行 JavaScript 壓縮 .pipe(gulp.dest('./public/js')); }); gulp.task('stylesheets_min', function(){ return gulp.src(cssFiles) .pipe(concat('all.css')) // 合并 CSS ,并設置合并后的文件名 .pipe(minifyCss()) // 執行 CSS 壓縮 .pipe(gulp.dest('./public/stylesheet')); });
管理圖片字體等靜態資源
對于圖片、字體等文件,只是需要使用 Gulp 自帶的gulp.src和gulp.dest來復制到 Web 目錄即可。除了這些靜態文件,有些文件可能也需要單獨處理,比如 JavaScript 中的一些配置項文件,另外,如果使用加載狀態提示模塊,這個也是需要優秀加載的。順便安利一下 PACE ,它是個使用相當方便的加載提示模塊。
gulp.task('pace', function(){ // copy pace.js to js folder return gulp.src('./bower_components/PACE/pace.min.js') .pipe(gulp.dest('./public/js')); });
其它需要直接復制的文件,也都是類似方法處理。
使用 Gulp 來創建文件修改后瀏覽器自動刷新的 Web 服務器
如果想要文件修改后,瀏覽器自動刷新,需要做兩方面的工作:
- 監控 JavaScript / Jade / CSS 文件,修改后重新轉換或者壓縮
- 監控 JavaScript / Jade / CSS 文件,修改后刷新瀏覽器 </ul>
對于第一個,使用gulp-watch模塊來監視文件,并執行對應的 Task ,對于第二個,可以使用gulp-webserver模塊,它可以創建一個 Web 服務器,并且在瀏覽器和服務器之間創建 Socket.IO 長鏈接,一旦有文件修改,便通過長鏈接通知瀏覽器刷新頁面。
var gulp = require('gulp'); var webserver = require('gulp-webserver');gulp.task('watch', function(){ // 不同的文件個性,需要執行不同的任務來處理 gulp.watch(['app/views/', 'app/views/_partial/'], ['jade']); gulp.watch(['bower_components/'], ['scripts', 'stylesheets']); gulp.watch(['static/css/'], ['stylesheets']); gulp.watch(['app/controllers/', 'app/modules/', 'app/services/*'], ['scripts']); });
gulp.task('webserver', function(){ gulp.src('./public/') .pipe(webserver({ host: '0.0.0.0', livereload: true, fallback: 'index.html' })); });</pre>