前端工程师:我用gulp4.0搭建一个前端脚手架

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

[[358414]]

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

前言

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

 

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

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

  • gulp-jshint ——js语法检测
  • gulp-util ——终端控制台打印自定义错误信息
  • http-proxy-middleware ——设置代理,配合gulp-connect使用
  • gulp-less ——将less编译成css
  • gulp-file-include ——用于文件模块化导入,如用include的方式导入公共部分
  • gulp-connect ——用于启动本地服务器
  • gulp-clean ——清理目录
  • gulp-uglify --压缩js
  • gulp-minify-css ——压缩css
  • gulp-autoprefixer ——自动添加浏览器前缀
  • imagemin-pngquant ——png图片压缩
  • gulp-imagemin ——图压缩
  • gulp-cache ——设置gulp打包的缓存,一般用于img
  • 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.
  • 2.
  • 3.

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')) //当前对应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.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.

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 发版本的时候为了避免浏览器读取了旧的缓存文件,需要为其添加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({ 
        cascadetrue, //是否美化属性值 默认: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.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.

4 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.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.

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": {} 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.

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

最后 

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

责任编辑:姜华 来源: 趣谈前端
相关推荐

2021-09-22 08:26:31

前端脚手架开源项目

2016-08-10 14:59:41

前端Javascript工具

2022-04-18 17:28:14

React前端

2019-12-25 15:20:48

前端脚手架命令

2015-08-24 09:02:49

前端工程师

2015-08-17 10:32:06

前端工程师优秀

2021-01-31 17:36:07

前端工程师职位

2016-09-08 15:20:04

JavascriptNodeGulp

2015-08-26 14:18:25

Web前端工程师价值

2012-12-27 09:43:42

前端工程师前端开发

2013-01-14 09:51:56

架构设计前端重构工程师

2019-06-24 09:40:17

前端前端工程师开发工具

2015-09-30 10:25:03

前端工程师

2022-07-18 07:58:46

Spring工具工具类

2016-09-22 16:14:45

前端设计Photoshop

2010-01-13 10:10:07

Web前端工程师

2015-03-16 16:01:40

Web前端前端工程师Web

2023-11-21 17:36:04

OpenFeignSentinel

2022-04-24 11:33:47

代码管理工程

2021-10-08 06:10:43

前端技术Vue
点赞
收藏

51CTO技术栈公众号