本文重點是詳細介紹Angular.js項目的構建。gulp是優秀的自動化項目構建工具,我們將用它完成 javascript/less/css/html/images/fonts 等文件的的測試、檢查、合併、壓縮、格式化、瀏覽器自動刷新、部署文件生成,並監聽文件在改動後重復指定的這些步驟(熱重載)。bower是項目依賴管理工具。
環境需求
gulp運行在nodejs環境,首先安裝nodejs。一般npm會隨nodejs一起安裝,npm(node package manager)是nodejs的包管理器,用於node插件管理(包括安裝、卸載、管理依賴等)。可以用node -v查看安裝的nodejs版本以及npm -v`查看npm的版本號。
安裝bower:$ npm install -g bower。
在windows環境中,git bsah 安裝bower會出現錯誤,可以在切換到cmd中安裝。
初始化項目
$ cd d:/develop #定位到develop目錄
$ mkdir angular-gulp-bower-seed #新建項目目錄
$ cd angular-gulp-bower-seed #進入項目目錄
$ npm init #初始化項目,並在項目項目中自動生成package.json文件
$ bower init #新建bower.json
此時項目初始化完畢,可以開始安裝項目依賴
#其他項目依賴也用此命令安裝,這些依賴將自動安裝到bower_component文件夾下,在代碼中引入制定的位置即可
$ bower install angular jquery bootstrap --save-dev
安裝時使用--save將自動保存配置信息至bower.json的dependencies數組中,使用--save-dev將自動保存配置信息至bower.json的devDependencies數組中。
保存至bower.json後,其他開發者對應下載即可(命令提示符執行bower install,則會根據bower.json下載所有依賴包,bower install --production只下載dependencies節點的包)。
安裝gulp工具包
$ npm install -g gulp #全局安裝gulp後可以在命令行中使用gulp命令,執行gulp任務
$ npm install --save-dev gulp $本地安裝gulp插件,以便在配置文件中調用gulp插件的功能
$ npm install autoprefixer-core babel-preset-es2015 browser-sync gulp-autoprefixer gulp-babel gulp-changed gulp-clean gulp-clean-css gulp-concat gulp-csso gulp-htmlmin gulp-jshint gulp-load-plugins gulp-ng-annotate gulp-ng-html2js gulp-rename gulp-rev gulp-rev-collector gulp-sequence gulp-size gulp-uglify gulp-useref http-server jasmine-core jshint jshint-stylish url --save-dev #安裝插件
構建項目目錄
項目目錄結構如下:
angular-gulp-bower-seed
│ .bowerrc //bower安裝包目錄配置
│ .gitignore
│ bower.json
│ gulpfile.js //gulp配置文件,打包處理,開啓服務等等
│ LICENSE
│ mockAPI.js //構造.json數據文件後,通過讀取文件方式模擬API請求,在前後端開發進度不一致時非常有用
│ package.json
│ README.md
│
├─app //項目開發目錄
│ │ angular.png.ico //ico圖標
│ │ app.js //angular項目公共配置,包括主模塊創建,路由配置,http攔截處理等
│ │ index-dist.html // 供gulp編譯,引入打包後的文件(script/styles/fonts)
│ │ index.html SPA項目入口
│ │
│ ├─bower_components //bower管理的依賴庫
│ │ ├─angular
│ │ ├─angular-bootstrap
│ │ ├─angular-cookies
│ │ ├─angular-local-storage
│ │ ├─angular-md5
│ │ ├─angular-resource
│ │ ├─angular-route
│ │ ├─angular-ui
│ │ ├─angular-ui-router
│ │ ├─bootstrap
│ │ └─jquery
│ ├─data //構建json格式的靜態數據(mock datas)
│ │ csrfToken.json
│ │ login.json
│ │
│ ├─public //項目公共資源,包括字體,圖片
│ │ ├─css
│ │ ├─fonts
│ │ └─images
│ │
│ └─src //項目源代碼
│ ├─controllers //控制器相關代碼,語義化命名
│ │ appControllers.js
│ │
│ ├─directives //自定義指令相關代碼
│ │ │ directives.js //指令腳本
│ │ │
│ │ └─tpls //指令模板文件夾
│ │ datepicker.html
│ │ modal.html
│ │ pagination.html
│ │
│ ├─filters //過濾器代碼
│ │ filters.js
│ │
│ ├─services //服務代碼
│ │ apiServices.js //與API請求相關服務
│ │ app.util.js //公共服務
│ │ services.js //其他
│ │
│ ├─styles //項目樣式文件夾
│ │ app.css
│ │ directives.css
│ │ page1.css
│ │ page2.css
│ │
│ └─templates //項目視圖文件夾
│ │ app.html
│ │ login.html
│ │ noRight.html
│ │
│ ├─modalViews //模態框視圖文件夾
│ │
│ ├─page1Views //按業務劃分視圖1文件夾
│ │
│ └─page2Views //按業務劃分視圖2文件夾
│
│
├─appDist //項目編譯打包後生成目錄
配置gulpfile.js
var gulp = require('gulp');
var gulpLoadPlugins = require('gulp-load-plugins');
var $ = plugins = gulpLoadPlugins();
var autoprefixer = require('gulp-autoprefixer');
var ngHtml2Js = require("gulp-ng-html2js");
var gulpif = require('gulp-if');
var uglify = require('gulp-uglify');//Minify JavaScript with UglifyJS2.
var cleanCSS = require('gulp-clean-css');
var htmlmin = require('gulp-htmlmin');
var jshint = require('gulp-jshint'); //jshint檢測javascript的語法錯誤
var useref = require('gulp-useref');
var csso = require('gulp-csso');
var concat = require('gulp-concat');
var rename = require("gulp-rename");
const babel = require('gulp-babel');
var browserSync = require('browser-sync');
var reload = browserSync.reload;
var url = require('url');
var mockApi = require('./mockApi');
var distFolderUrl = "appDist";
gulp.task('clean', function () {
return require('del')([distFolderUrl + '/**','tmp/**','dist/**']);
});
gulp.task('templatesTpls', function () {
return gulp.src([
'./app/src/directives/tpls/*.html',
])
.pipe(ngHtml2Js({
moduleName: "myApp",
prefix: "src/directives/tpls/"
}))
.pipe(concat("templatesTpls.min.js"))
.pipe(babel({
presets: ['es2015']
}))
.pipe(uglify())
.pipe(gulp.dest('./tmp/templates'))
});
gulp.task('templatesViews', function () {
return gulp.src([
'./app/src/templates/**/*.html'
])
.pipe(ngHtml2Js({
moduleName: "myApp",
rename:function (templateUrl, templateFile) {
var pathParts = templateFile.path.split('\\');
var file = pathParts[pathParts.length - 1];
var folder = pathParts[pathParts.length - 2];
if ("templates" === folder) {
return "./src/templates/" + file
} else {
return "./src/templates/" + folder + '/' + file
}
}
}))
.pipe(concat("templatesViews.min.js"))
.pipe(babel({
presets: ['es2015']
}))
.pipe(uglify())
.pipe(gulp.dest('./tmp/templates'))
});
gulp.task('copyTemplatesToDist', function () {
return gulp.src([
'./app/src/templates/**/*.html',
])
.pipe(gulp.dest(distFolderUrl + '/src/templates'));
});
gulp.task('copyTplsToDist', function () {
return gulp.src([
'./app/src/directives/tpls/**/*.html',
])
.pipe(gulp.dest(distFolderUrl + '/src/directives/tpls'));
});
// gulp.task('font', function() {
// return gulp.src(['./app/public/fonts/**/*'], {base: './app/'})
// .pipe(gulp.dest(distFolderUrl + ''))
// });
// gulp.task('images', function() {
// return gulp.src(['./app/public/images/**/*'], {base: './app/'})
// .pipe(gulp.dest(distFolderUrl + ''))
// });
gulp.task('public', function() {
return gulp.src(['./app/public/**/*','./app/*.ico'], {base: './app/'})
.pipe(gulp.dest(distFolderUrl))
});
gulp.task('vendorCss',function () {
return gulp.src(['./app/bower_components/**/*.css'])
.pipe(gulp.dest(distFolderUrl + '/vendor'))
})
gulp.task('vendorFont',function () {
return gulp.src([ './app/bower_components/bootstrap/dist/fonts/**'])
.pipe(gulp.dest(distFolderUrl + '/vendor/bootstrap/dist/fonts'))
})
gulp.task('vendorJs',function () {
return gulp.src('./app/bower_components/**/*.js')
.pipe(gulp.dest(distFolderUrl + '/vendor'))
})
// gulp.task('vendor', ['vendorCss', 'vendorJs', 'vendorFont']);
gulp.task('vendor', function () {
return gulp.src(['./app/bower_components/**/*'])
.pipe(gulp.dest(distFolderUrl + '/vendor'))
});
var cssList = [
'./app/src/styles/app.css',
'./app/src/styles/*.css'
];
gulp.task('css', function() {
return gulp.src(cssList)
.pipe(autoprefixer({
browsers: ['last 2 versions'],
cascade: false
}))
.pipe(concat('app.min.css'))
.pipe(cleanCSS())
.pipe(gulp.dest(distFolderUrl + '/static/css'))
})
var jsList = [
'./app/*.js',
'./app/src/directives/*.js',
'./app/src/controllers/*.js',
'./app/src/services/*.js',
'./app/src/filters/*.js',
'./tmp/templates/*.js',
];
gulp.task('jshint', function () {
return gulp.src(jsList)
.pipe(reload({stream: true, once: true}))
.pipe(jshint())
.pipe(jshint.reporter('jshint-stylish'))
});
// gulp.task('js', ['jshint'], function () {
gulp.task('js', ['templatesTpls','templatesViews'], function () {
return gulp.src(jsList)
.pipe(concat('app.min.js'))
.pipe(babel({
presets: ['es2015']
}))
.pipe(uglify({
mangle: false,//類型:Boolean 默認:true 是否修改變量名
compress: false,//類型:Boolean 默認:true 是否完全壓縮
preserveComments: 'all' //保留註釋
}).on('error', function(e){
console.log(e);
}))
.pipe(gulp.dest(distFolderUrl + '/static/js'))
});
gulp.task('htmlVendor', function () {
return gulp.src(['app/index-vendor.html'])
.pipe(useref({ searchPath: ['app'] }))
// .pipe(rename('index1.html'))
.pipe(gulpif('*.js', uglify({
mangle: false,
compress: false,
preserveComments: 'all'
})))
.pipe(gulpif('*.css', csso()))
// .pipe(gulpif('*.html', htmlMinify({conditionals: true, loose: false})))
.pipe(gulp.dest(distFolderUrl));
});
// gulp.task('html', ['copyTemplatesToDist', 'copyTplsToDist'], function () {
gulp.task('html', function () {
return gulp.src(['app/index-dist.html'])
.pipe(rename('index.html'))
.pipe(gulp.dest(distFolderUrl))
})
gulp.task('build', ['public','vendor','js','css','html'], function () {
return gulp.src(distFolderUrl + '/**/*').pipe($.size({title: 'build', gzip: true}));
});
gulp.task('default', ['clean'], function () {
gulp.start('build');
});
// var files = [
// 'app/**/*.html',
// 'app/**/*.css',
// 'app/**/*.js',
// 'app/public/**/*',
// 'app/data/**/*'
// ];
gulp.task('serve', function () {
browserSync({
notify: false, // Don't show any notifications in the browser.
port: 8081,
open: false,
server: {
baseDir: ['app'],
routes: {
// 'bower_components': 'bower_components',//if bower_components' path is up the tree of app
},
middleware:
function (req, res, next) {
var urlObj = url.parse(req.url, true),
method = req.method,
paramObj = urlObj.query;
mockApi(res, urlObj.pathname, paramObj, next);
}
}
});
// watch for changes
gulp.watch([
'app/**/*.html',
'app/**/*.css',
'app/**/*.js',
'app/public/**/*',
'app/data/**/*'
]).on('change', reload);
gulp.watch('app/src/**/*.less', ['styles', reload]);
gulp.watch('bower.json', ['fonts', reload]);
});
gulp.task('serve-release', function () {
browserSync({
notify: false,
port: 8081,
server: {
baseDir: [distFolderUrl]
}
});
});
運行gulp任務
gulp <taskName>
$ gulp clean #清空編譯目錄
$ gulp build #編譯打包
$ gulp serve #開啓開發環境服務器 http://localhost:8080
$ gulp serve-release #開啓生產環境服務器 http://localhost:8081
項目源碼:angular-gulp-bower-seed