前端工程師:我用gulp4.0搭建一個(gè)前端腳手架
本文將會(huì)介紹如何使用gulp4來搭建項(xiàng)目腳手架,如果您還在使用gulp3或更老的版本,您也以通過本文的一些思想將之前的項(xiàng)目進(jìn)行完善,更新。如果gulp不是你們團(tuán)隊(duì)的重點(diǎn),也可以移步我的Webpack4.0搭建文章.
前言
由于本文重點(diǎn)是介紹gulp4.0搭建腳手架的思路,相關(guān)插件的用法以及項(xiàng)目結(jié)構(gòu)的設(shè)計(jì),由于gulp的基本用法很簡單,如果不熟悉可以移步官網(wǎng)自行研究學(xué)習(xí)。該腳手架的設(shè)計(jì)思路和功能如下:
 同時(shí)為了提高開發(fā)環(huán)境的效率,這里我們參考webpack的配置,區(qū)分開發(fā)環(huán)境和生產(chǎn)環(huán)境,在接下來將會(huì)具體介紹。
腳手架用到的第三方插件介紹
- gulp-jshint ——js語法檢測(cè)
 - gulp-util ——終端控制臺(tái)打印自定義錯(cuò)誤信息
 - http-proxy-middleware ——設(shè)置代理,配合gulp-connect使用
 - gulp-less ——將less編譯成css
 - gulp-file-include ——用于文件模塊化導(dǎo)入,如用include的方式導(dǎo)入公共部分
 - gulp-connect ——用于啟動(dòng)本地服務(wù)器
 - gulp-clean ——清理目錄
 - gulp-uglify --壓縮js
 - gulp-minify-css ——壓縮css
 - gulp-autoprefixer ——自動(dòng)添加瀏覽器前綴
 - imagemin-pngquant ——png圖片壓縮
 - gulp-imagemin ——圖壓縮
 - gulp-cache ——設(shè)置gulp打包的緩存,一般用于img
 - gulp-md5-plus ——將文件名進(jìn)行md5處理便于打包更新
 
當(dāng)然gulp還有很多常用的插件可以更好的為我們的項(xiàng)目服務(wù),大家也可以整合自己的插件讓項(xiàng)目更加完善。
項(xiàng)目目錄設(shè)計(jì)
1.src目錄,即我們開發(fā)項(xiàng)目時(shí)的源目錄,具體結(jié)構(gòu)如下:

我們定義views是我們視圖層,即頁面文件的目錄,js目錄為業(yè)務(wù)邏輯的腳本文件,lib存放第三方框架,include目錄為公共部分的存放目錄,我們可以用gulp-file-include來導(dǎo)入到html中,images和css大家都比較清楚,分別時(shí)存放image和css文件的目錄。
2. dist目錄,即輸出的目錄,具體結(jié)構(gòu)如下:

可以看到我們會(huì)看到src打包后的目錄對(duì)應(yīng)static目錄,為什么我們會(huì)加一層static呢?我的設(shè)計(jì)是如果項(xiàng)目使用node等服務(wù)層框架,我們可以用gulp一并打包放入dist下,這樣dist就是一個(gè)完整的包括前后端服務(wù)的項(xiàng)目目錄了,當(dāng)然大家也可以直接將src打包后的文件和文件夾直接放到dist下,根具業(yè)務(wù)需求靈活設(shè)計(jì)吧。
在這里我要說一點(diǎn),由于筆者親測(cè)gulp-md5-plus有時(shí)候打包不穩(wěn)定,可能不會(huì)給html自動(dòng)添加對(duì)應(yīng)的md5后綴,所以筆者在這塊做了特殊的處理,如果大家在工作中有更好的方案,可以及時(shí)和筆者溝通交流。
3. gulpfile文件配置
由于我們要區(qū)分開發(fā)環(huán)境和生產(chǎn)環(huán)境,所以這里我們使用兩個(gè)不同的配置文件,根據(jù)NODE_ENV來區(qū)分用哪個(gè)文件。

我們將配置文件統(tǒng)一放到build目錄下,config為公共配置文件,gulp.dev.js和gulp.prod.js分別為開發(fā)和生產(chǎn)環(huán)境配置文件。我們整體的目錄結(jié)構(gòu)如下:

腳手架完整源碼(部分插件和配置會(huì)給出詳細(xì)注釋)
1 config.js
- module.exports = {
 - dist: './dist/static', // 配置構(gòu)建目錄
 - }
 
2 gulp.dev.js
- const gulp = require('gulp');
 - // js
 - const Jshint = require("gulp-jshint"); //js檢查
 - const Gutil = require('gulp-util');
 - const Proxy = require('http-proxy-middleware');
 - // const Webpack = require('webpack');
 - // const WebpackConfig = require('./webpack.config.js');
 - // css
 - const Less = require('gulp-less'); // 編譯less
 - // html
 - const FileInclude = require('gulp-file-include'); // 文件模塊化
 - // server
 - const Connect = require('gulp-connect'); //引入gulp-connect模塊
 - const Clean = require('gulp-clean'); // 清理目錄
 - // 配置文件
 - const config = require('./config');
 - const { dist } = config;
 - // html
 - async function html() {
 - return gulp.src('src/views/*.html')
 - .pipe(FileInclude({ // HTML模板替換,具體用法見下文
 - prefix: '##',
 - basepath: '@file'
 - })).on('error', function(err) {
 - console.error('Task:copy-html,', err.message);
 - this.end();
 - })
 - .pipe(gulp.dest(dist)) // 拷貝
 - .pipe(Connect.reload())
 - }
 - // css
 - async function css() {
 - return await gulp.src('src/css/*.less')
 - .pipe(Less()) //編譯less
 - .pipe(gulp.dest(dist + '/css')) //當(dāng)前對(duì)應(yīng)css文件
 - .pipe(Connect.reload());//更新
 - }
 - // js
 - // const compilerJS = Webpack(WebpackConfig);
 - async function js() {
 - return await gulp.src('src/js/**')
 - .pipe(Jshint())//檢查代碼
 - // .pipe(Babel({
 - // presets: ['es2015']
 - // }))
 - .on('error', function(err) {
 - Gutil.log(Gutil.colors.red('[Error]'), err.toString());
 - })
 - .pipe(gulp.dest(dist + '/js')) // 拷貝
 - .pipe(Connect.reload()); //更新
 - // 使用es6+可以單獨(dú)配置
 - // compilerJS.run(function(err, stats) {
 - // if(err) throw new Gutil.PluginError("webpack:js", err);
 - // Gutil.log("[webpack]", stats.toString({
 - // colors: true
 - // }));
 - // cb()
 - // });
 - }
 - // image
 - async function image() {
 - return await gulp.src('src/images/*')
 - .pipe(gulp.dest(dist + '/images'));
 - }
 - // clean dir
 - async function clean() {
 - // 不設(shè)置allowEmpty: true會(huì)報(bào)File not found with singular glob
 - return await gulp.src(dist, {allowEmpty: true}).pipe(Clean());
 - }
 - // 服務(wù)器函數(shù)
 - async function server() {
 - Connect.server({
 - root:dist, //根目錄
 - // ip:'192.168.11.62',//默認(rèn)localhost:8080
 - livereload:true, //自動(dòng)更新
 - port:9909, //端口
 - middleware: function(connect, opt) {
 - return [
 - Proxy('/api', {
 - target: 'http://localhost:8080',
 - changeOrigin:true
 - }),
 - Proxy('/otherServer', {
 - target: 'http://IP:Port',
 - changeOrigin:true
 - })
 - ]
 - }
 - })
 - }
 - module.exports = {
 - html,
 - css,
 - js,
 - image,
 - clean,
 - server
 - }
 
3 gulp.prod.js
- const gulp = require('gulp');
 - // const Rename = require('gulp-rename'); // 重命名
 - // js
 - const Uglify = require('gulp-uglify'); // 壓縮js
 - // const Babel = require('gulp-babel');
 - // css
 - const Minifycss = require('gulp-minify-css'); // 壓縮css
 - const Less = require('gulp-less'); // 編譯less
 - const Autoprefixer = require('gulp-autoprefixer'); // 瀏覽器前綴
 - // html
 - const MinifyHtml = require("gulp-minify-html"); //壓縮html
 - const FileInclude = require('gulp-file-include'); // 文件模塊化
 - // image
 - const Imagemin = require('gulp-imagemin');
 - const Pngquant = require('imagemin-pngquant'); //png圖片壓縮插件
 - const Cache = require('gulp-cache');
 - const Clean = require('gulp-clean'); // 清理目錄
 - // md5 發(fā)版本的時(shí)候?yàn)榱吮苊鉃g覽器讀取了舊的緩存文件,需要為其添加md5戳
 - const md5 = require("gulp-md5-plus");
 - const config = require('./config');
 - const { dist } = config;
 - // html
 - async function html() {
 - return gulp.src('src/views/*.html')
 - .pipe(FileInclude({ // HTML模板替換,具體用法見下文
 - prefix: '##',
 - basepath: '@file'
 - }))
 - // .pipe(MinifyHtml())
 - .on('error', function(err) {
 - console.error('Task:copy-html,', err.message);
 - this.end();
 - })
 - .pipe(gulp.dest(dist)) // 拷貝
 - }
 - // css
 - async function css() {
 - return await gulp.src('src/css/**')
 - .pipe(Less()) //編譯less
 - .pipe(Autoprefixer({
 - cascade: true, //是否美化屬性值 默認(rèn):true 像這樣:
 - //-webkit-transform: rotate(45deg);
 - // transform: rotate(45deg);
 - remove: true //是否去掉不必要的前綴 默認(rèn):true
 - }))
 - .pipe(Minifycss({ // 壓縮css
 - //類型:Boolean 默認(rèn):true [是否開啟高級(jí)優(yōu)化(合并選擇器等)]
 - advanced: true,
 - //保留ie7及以下兼容寫法 類型:String 默認(rèn):''or'*' [啟用兼容模式; 'ie7':IE7兼容模式,'ie8':IE8兼容模式,'*':IE9+兼容模式]
 - compatibility: '',
 - //類型:Boolean 默認(rèn):false [是否保留換行]
 - keepBreaks: false,
 - //保留所有特殊前綴 當(dāng)你用autoprefixer生成的瀏覽器前綴,如果不加這個(gè)參數(shù),有可能將會(huì)刪除你的部分前綴
 - keepSpecialComments: '*'
 - }))
 - .pipe(gulp.dest(dist + '/css'))
 - .pipe(md5(10, dist + '/*.html', {
 - mappingFile: 'manifest.json',
 - connector: '.' // 文件名和hash的連接符
 - }))
 - .pipe(gulp.dest(dist + '/css')) //當(dāng)前對(duì)應(yīng)css文件
 - }
 - // js
 - async function js() {
 - return await gulp.src('src/js/**')
 - // .pipe(Babel({
 - // presets: ['es2015']
 - // }))
 - .pipe(Uglify()) // 壓縮js
 - .pipe(gulp.dest(dist + '/js'))
 - .pipe(md5(10, dist + '/*.html', {
 - mappingFile: 'manifest.json',
 - connector: '.'
 - }))
 - .pipe(gulp.dest(dist + '/js')) // 拷貝
 - }
 - // image
 - async function image() {
 - return await gulp.src('src/images/*')
 - .pipe(Cache(Imagemin({
 - optimizationLevel: 5, //類型:Number 默認(rèn):3 取值范圍:0-7(優(yōu)化等級(jí))
 - progressive: true, //類型:Boolean 默認(rèn):false 無損壓縮jpg圖片
 - interlaced: true, //類型:Boolean 默認(rèn):false 隔行掃描gif進(jìn)行渲染
 - multipass: true, //類型:Boolean 默認(rèn):false 多次優(yōu)化svg直到完全優(yōu)化
 - svgoPlugins: [{removeViewBox: false}],//不要移除svg的viewbox屬性
 - use: [Pngquant()] //使用pngquant深度壓縮png圖片的imagemin插件
 - })))
 - .pipe(gulp.dest(dist + '/images'));
 - }
 - // clean dir
 - async function clean() {
 - // 不設(shè)置allowEmpty: true會(huì)報(bào)File not found with singular glob
 - return await gulp.src(dist, {allowEmpty: true}).pipe(Clean());
 - }
 - module.exports = {
 - html,
 - css,
 - js,
 - image,
 - clean
 - }
 
4 gulpfile.js
- const gulp = require('gulp');
 - // 根據(jù)環(huán)境引入不同的配置文件
 - let buildConfig;
 - if(process.env.NODE_ENV === 'dev') {
 - buildConfig = require('./build/gulp.dev');
 - gulp.task('server', buildConfig.server); // 本地服務(wù)
 - } else {
 - buildConfig = require('./build/gulp.prod');
 - // gulp.task('md5', gulp.series(buildConfig.md5Css, buildConfig.md5Js));
 - gulp.task('clean', buildConfig.clean); // 清理目錄
 - }
 - gulp.task('html', buildConfig.html); // 打包html
 - gulp.task('js', buildConfig.js); // 打包js
 - gulp.task('css', buildConfig.css); // 打包c(diǎn)ss
 - gulp.task('images', buildConfig.image); // 打包image
 - gulp.task('sources', gulp.series('html', gulp.parallel('js', 'css', 'images')));
 - // 監(jiān)聽文件變化
 - gulp.task('watch', async () => {
 - gulp.watch('src/views/*', gulp.series('html')); // 監(jiān)聽HTML變化
 - gulp.watch('src/js/**', gulp.series('js')); // 監(jiān)聽js變化
 - gulp.watch('src/css/*', gulp.series('css')); // 監(jiān)聽css變化
 - gulp.watch('src/images/*', gulp.series('images')); // 監(jiān)聽image變化
 - });
 - // build
 - if(process.env.NODE_ENV === 'dev') {
 - gulp.task('dev', gulp.series('sources', 'server', 'watch'));
 - } else {
 - gulp.task('build', gulp.series('sources'));
 - }
 
5 package.json
- {
 - "dependencies": {
 - "@babel/core": "^7.4.5",
 - "babel-preset-es2015": "^6.24.1",
 - "gulp": "^4.0.2",
 - "gulp-autoprefixer": "^6.1.0",
 - "gulp-babel": "^8.0.0",
 - "gulp-cache": "^1.1.2",
 - "gulp-clean": "^0.4.0",
 - "gulp-connect": "^5.7.0",
 - "gulp-file-include": "^2.0.1",
 - "gulp-imagemin": "^6.0.0",
 - "gulp-jshint": "^2.1.0",
 - "gulp-less": "^4.0.1",
 - "gulp-md5-plus": "^1.0.3",
 - "gulp-minify-css": "^1.2.4",
 - "gulp-minify-html": "^1.0.6",
 - "gulp-rename": "^1.4.0",
 - "gulp-uglify": "^3.0.2",
 - "gulp-util": "^3.0.8",
 - "http-proxy-middleware": "^0.19.1",
 - "http-server": "^0.11.1",
 - "imagemin-pngquant": "^8.0.0",
 - "jshint": "^2.10.2",
 - "jsonfile": "^5.0.0",
 - "webpack": "^4.35.2"
 - },
 - "scripts": {
 - "start": "NODE_ENV=dev gulp dev",
 - "build": "NODE_ENV=prod gulp clean && gulp build",
 - "serve": "http-server dist/static -p 3000"
 - },
 - "devDependencies": {}
 - }
 
要想獲取項(xiàng)目完整源碼和demo,請(qǐng)移步gulp4_multi_pages。
最后
該腳手架任然有需要完善的地方,比如如何兼容uglify和babel,md5需要使用兩次的情況,如果更好的解決方案,歡迎隨時(shí)交流。在腳手架選型上,也不一定非要用gulp,webpack,一般的經(jīng)驗(yàn)是傳統(tǒng)型的靜態(tài)網(wǎng)站適合用gulp,由于不需要編譯es6,所以有更小的體積,當(dāng)然也可以用webpack,本文主要是給大家提供一使用gulp4搭建個(gè)腳手架的思路,希望能有所收獲。
















 
 
 








 
 
 
 