本文将会介绍如何使用gulp4来搭建项目脚手架,如果您还在使用gulp3或更老的版本,您也以通过本文的一些思想将之前的项目进行完善,更新。如果gulp不是你们团队的重点,也可以移步我的另一篇文章:

用 webpack 4.0 撸单页/多页脚手架 (jquery, react, vue, typescript)

前言

由于本文重点是介绍gulp4.0搭建脚手架的思路,相关插件的用法以及项目结构的设计,由于gulp的基本用法很简单,如果不熟悉可以移步官网自行研究学习。该脚手架的设计思路和功能如下:

同时为了提高开发环境的效率,这里我们参考webpack的配置,区分开发环境和生产环境,在接下来将会具体介绍。

脚手架用到的第三方插件介绍

  1. gulp-jshint ——js语法检测

  2. gulp-util ——终端控制台打印自定义错误信息

  3. http-proxy-middleware ——设置代理,配合gulp-connect使用

  4. gulp-less ——将less编译成css

  5. gulp-file-include ——用于文件模块化导入,如用include的方式导入公共部分

  6. gulp-connect ——用于启动本地服务器

  7. gulp-clean ——清理目录

  8. gulp-uglify --压缩js

  9. gulp-minify-css ——压缩css

  10. gulp-autoprefixer ——自动添加浏览器前缀

  11. imagemin-pngquant ——png图片压缩

  12. gulp-imagemin ——图压缩

  13. gulp-cache ——设置gulp打包的缓存,一般用于img

  14. gulp-md5-plus ——将文件名进行md5处理便于打包更新

当然gulp还有很多常用的插件可以更好的为我们的项目服务,大家也可以整合自己的插件让项目更加完善。

项目目录设计

1.src目录,即我们开发项目时的源目录,具体结构如下:

我们定义views是我们视图层,即页面文件的目录,js目录为业务逻辑的脚本文件,lib存放第三方框架,include目录为公共部分的存放目录,我们可以用gulp-file-include来导入到html中,images和css大家都比较清楚,分别时存放image和css文件的目录。

2. dist目录,即输出的目录,具体结构如下:

可以看到我们会看到src打包后的目录对应static目录,为什么我们会加一层static呢?我的设计是如果项目使用node等服务层框架,我们可以用gulp一并打包放入dist下,这样dist就是一个完整的包括前后端服务的项目目录了,当然大家也可以直接将src打包后的文件和文件夹直接放到dist下,根具业务需求灵活设计吧。

在这里我要说一点,由于笔者亲测gulp-md5-plus有时候打包不稳定,可能不会给html自动添加对应的md5后缀,所以笔者在这块做了特殊的处理,如果大家在工作中有更好的方案,可以及时和笔者沟通交流。

3. gulpfile文件配置

由于我们要区分开发环境和生产环境,所以这里我们使用两个不同的配置文件,根据NODE_ENV来区分用哪个文件。

我们将配置文件统一放到build目录下,config为公共配置文件,gulp.dev.js和gulp.prod.js分别为开发和生产环境配置文件。我们整体的目录结构如下:

脚手架完整源码(部分插件和配置会给出详细注释)

  1. config.js

module.exports = {	
    dist: './dist/static',  // 配置构建目录	
}	
复制代码
  1. 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')) //当前对应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+可以单独配置	
    // 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() {	
    // 不设置allowEmpty: true会报File not found with singular glob	
    return await gulp.src(dist, {allowEmpty: true}).pipe(Clean());	
}	

	
// 服务器函数	
async function server() {	
    Connect.server({	
        root:dist, //根目录	
        // ip:'192.168.11.62',//默认localhost:8080	
        livereload:true, //自动更新	
        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	
}	
复制代码
  1. 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 发版本的时候为了避免浏览器读取了旧的缓存文件,需要为其添加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, //是否美化属性值 默认:true 像这样:	
        //-webkit-transform: rotate(45deg);	
        //        transform: rotate(45deg);	
        remove: true //是否去掉不必要的前缀 默认:true	
    }))	
    .pipe(Minifycss({   // 压缩css	
        //类型:Boolean 默认:true [是否开启高级优化(合并选择器等)]	
        advanced: true,	
        //保留ie7及以下兼容写法 类型:String 默认:''or'*' [启用兼容模式; 'ie7':IE7兼容模式,'ie8':IE8兼容模式,'*':IE9+兼容模式]	
        compatibility: '',	
        //类型:Boolean 默认:false [是否保留换行]	
        keepBreaks: false,	
        //保留所有特殊前缀 当你用autoprefixer生成的浏览器前缀,如果不加这个参数,有可能将会删除你的部分前缀        	
        keepSpecialComments: '*'	
    }))	
    .pipe(gulp.dest(dist + '/css'))	
    .pipe(md5(10, dist + '/*.html', {	
        mappingFile: 'manifest.json',	
        connector: '.' // 文件名和hash的连接符	
    }))	
    .pipe(gulp.dest(dist + '/css')) //当前对应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  默认:3  取值范围:0-7(优化等级)	
        progressive: true, //类型:Boolean 默认:false 无损压缩jpg图片	
        interlaced: true, //类型:Boolean 默认:false 隔行扫描gif进行渲染	
        multipass: true, //类型:Boolean 默认:false 多次优化svg直到完全优化	
        svgoPlugins: [{removeViewBox: false}],//不要移除svg的viewbox属性	
        use: [Pngquant()] //使用pngquant深度压缩png图片的imagemin插件	
    })))	
    .pipe(gulp.dest(dist + '/images'));	
}	

	

	
// clean dir	
async function clean() {	
    // 不设置allowEmpty: true会报File not found with singular glob	
    return await gulp.src(dist, {allowEmpty: true}).pipe(Clean());	
}	

	

	

	
module.exports = {	
    html,	
    css,	
    js,	
    image,	
    clean	
}	
复制代码
  1. gulpfile.js

const gulp = require('gulp');	

	
// 根据环境引入不同的配置文件	
let buildConfig;	
if(process.env.NODE_ENV === 'dev') {	
    buildConfig = require('./build/gulp.dev');	
    gulp.task('server', buildConfig.server);  // 本地服务	
    	
} 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);        // 打包css	
gulp.task('images', buildConfig.image);   // 打包image	
gulp.task('sources', gulp.series('html', gulp.parallel('js', 'css', 'images')));	

	

	
// 监听文件变化	
gulp.task('watch', async () => {	
    gulp.watch('src/views/*', gulp.series('html')); // 监听HTML变化	
    gulp.watch('src/js/**', gulp.series('js')); // 监听js变化	
    gulp.watch('src/css/*', gulp.series('css')); // 监听css变化	
    gulp.watch('src/images/*', gulp.series('images')); // 监听image变化	
});	

	
// build	
if(process.env.NODE_ENV === 'dev') {	
    gulp.task('dev', gulp.series('sources', 'server', 'watch'));	
} else {	
    gulp.task('build', gulp.series('sources'));	
}	

	

	
复制代码
  1. 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": {}	
}

复制代码

要想获取项目完整源码和demo,请移步gulp4_multi_pages。

(https://github.com/MrXujiang/gulp4_multi_pages)

最后

该脚手架任然有需要完善的地方,比如如何兼容uglify和babel,md5需要使用两次的情况,如果更好的解决方案,欢迎随时交流。在脚手架选型上,也不一定非要用gulp,webpack,一般的经验是传统型的静态网站适合用gulp,由于不需要编译es6,所以有更小的体积,当然也可以用webpack,本文主要是给大家提供一使用gulp4搭建个脚手架的思路,希望能有所收获。

更多推荐

如何用不到200行代码写一款属于自己的js类库)

让你瞬间提高工作效率的常用js函数汇总(持续更新)

一张图教你快速玩转vue-cli3

3分钟教你用原生js实现具有进度监听的文件上传预览组件

使用Angular8和百度地图api开发《旅游清单》

js基本搜索算法实现与170万条数据下的性能测试

《前端算法系列》如何让前端代码速度提高60倍

vue高级进阶系列——用typescript玩转vue和vuex

欢迎关注下方公众号,获取更多前端知识精粹和学习社群:

在公众号点击进群,可以加入vue学习小组,一起学习前端技术;

回复 学习路径,将获取笔者多年从业经验的前端学习路径的思维导图

趣谈前端

Vue、React、小程序、Node 

前端 算法|性能|架构|安全