当前位置: 首页 > 科技观察

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

时间:2023-03-15 00:56:55 科技观察

本文将介绍如何使用gulp4搭建项目脚手架。如果你还在使用gulp3或者更老的版本,也可以利用本文的一些思路对之前的项目进行改进和更新。如果gulp不是你团队的重心,也可以移步我的Webpack4.0搭建文章。前言由于本文的重点是介绍gulp4.0中搭建脚手架的思路,相关插件的使用以及项目结构的设计,因为gulp的基本用法非常简单,如果你是不熟悉的可以去官网自己研究学习。脚手架的设计思路和作用如下:同时,为了提高开发环境的效率,这里参考webpack的配置来区分开发环境和生产环境,将接下来详细介绍。脚手架使用的第三方插件介绍gulp-jshint——js语法检测gulp-util——终端控制台打印自定义错误信息http-proxy-middleware——设置代理,使用gulp-less和gulp-connect——将less编译成cssgulp-file-include-用于文件模块化导入,例如通过include导入公共部分gulp-connect-用于启动本地服务器gulp-clean-清理目录gulp-uglify-压缩jsgulp-minify-css——压缩cssgulp-autoprefixer——自动添加浏览器前缀imagemin-pngquant——png图片压缩gulp-imagemin——图片压缩gulp-cache——设置gulp打包的缓存,一般用于imggulp-md5-plus——对文件进行处理命名为md5,方便打包更新。当然gulp也有很多常用的插件可以更好的为我们的项目服务。您也可以集成自己的插件,使项目更加完美。项目目录设计1、src目录是我们开发项目时的源码目录。具体结构如下:我们定义views作为我们的视图层,也就是页面文件的目录,js目录是业务逻辑的脚本文件,lib存放第三方框架。include目录是public部分的存放目录。我们可以使用gulp-file-include将其导入到html中。每个人都知道图像和css。分别是存放图片和css文件的目录。2.dist目录,也就是输出目录,具体结构如下:可以看到我们会看到src打包目录对应的是static目录,为什么还要加一层static呢?我的设计是,如果项目使用了nodeframework这样的服务层,我们可以用gulp打包在一起放在dist下,这样dist就是一个完整的项目目录,包括前后端服务。当然你也可以直接把src打包好的文件和文件夹直接放到dist下面。符合业务需求的灵活设计。这里想说一下,因为笔者亲测gulp-md5-plus有时候打包不稳定,可能不会自动给html加上相应的md5后缀,所以笔者在这方面做了特殊处理,如果你是工作中如果有更好的方案,可以及时与作者沟通。3、gulpfile文件配置由于我们需要区分开发环境和生产环境,这里我们使用两个不同的配置文件,通过NODE_ENV来区分使用哪个文件。我们把配置文件放在build目录下,config是公共配置文件,gulp.dev.js和gulp.prod.js分别是开发和生产环境的配置文件。我们整体目录结构如下:脚手架完整源码(部分插件和配置会给出详细注释)1config.jsmodule.exports={dist:'./dist/static',//配置构建目录}2gulp.dev。jsconstgulp=require('gulp');//jsconstJshint=require("gulp-jshint");//js检查constGutil=require('gulp-util');constProxy=require('http-proxy-middleware');//constWebpack=require('webpack');//constWebpackConfig=require('./webpack.config.js');//cssconstLess=require('gulp-less');//少编译//htmlconstFileInclude=require('gulp-file-include');//文件模块化//serverconstConnect=require('gulp-connect');//引入gulp-connect模块constClean=require('gulp-clean');//清理目录//配置文件constconfig=require('./config');const{dist}=config;//htmlasyncfunctionhtml(){returngulp.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))//copy.pipe(Connect.reload())}//cssasyncfunctioncss(){returnawaitgulp.src('src/css/*.less').pipe(Less())//编译less.pipe(gulp.dest(dist+'/css'))//当前对应的cssfile.pipe(Connect.reload());//update}//js//constcompilerJS=We??bpack(WebpackConfig);asyncfunctionjs(){returnawaitgulp.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)thrownewGutil.PluginError("webpack:js",err);//Gutil.log([webpack]",stats.toString({//colors:true//}));//cb()//});}//imageasyncfunctionimage(){returnawaitgulp.src('src/images/*').pipe(gulp.dest(dist+'/images'));}//cleandirasyncfunctionclean(){//如果不设置allowEmpty:true,会报Filenotfoundwithsingularglobreturnawaitgulp.src(dist,{allowEmpty:true}).pipe(Clean());}//服务器函数asyncfunctionserver(){Connect.server({root:dist,//根目录//ip:'192.168.11.62',//默认本地人st:8080livereload:true,//自动更新port:9909,//portmiddleware: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}3gulp.prod.jsconstgulp=require('gulp');//constRename=require('gulp-重命名');//重命名//jsconstUglify=require('gulp-uglify');//压缩js//constBabel=require('gulp-babel');//cssconstMinifycss=require('gulp-minify-css');//压缩cssconstLess=require('gulp-less');//编译lessconstAutoprefixer=require('gulp-autoprefixer');//浏览器前缀//htmlconstMinifyHtml=require("gulp-minify-html");//压缩htmlconstFileInclude=require('gulp-file-include');//文件模块化//imageconstImagemin=require('gulp-imagemin');constPngquant=require('imagemin-pngquant');//png图片压缩插件-inconstCache=require('gulp-cache');constClean=require('gulp-clean');//清理目录//md5版本发布时,为了防止浏览器读取旧缓存文件,需要添加md5戳constmd5=require("gulp-md5-plus");constconfig=require('./config');const{dist}=config;//htmlasyncfunctionhtml(){returngulp.src('src/views/*.html').pipe(FileInclude({//HTML模板替换,具体用法见下文prefix:'##',basepath:'@file'}))//.管道(MinifyHtml()).on('error',function(err){console.error('Task:copy-html,',err.message);this.end();}).pipe(gulp.dest(dist))//copy}//cssasyncfunctioncss(){returnawaitgulp.src('src/css/**').pipe(Less())//编译less.pipe(Autoprefixer({cascade:true,//是否美化属性值default:true像这样://-webkit-transform:rotate(45deg);//transform:rotate(45deg);remove:true//是否去除不必要的前缀default:true})).pipe(minifycss({//压缩后的css//类型:BooleanDefault:true[是否启用高级优化(合并选择器等)]advanced:true,//保留ie7及以下兼容写法type:StringDefault:''or'*'[EnableCompatibilityMode;'ie7':IE7CompatibilityMode,'ie8':IE8CompatibilityMode,'*':IE9+CompatibilityMode]compatibility:'',//Type:BooleanDefault:false[是否保留换行符]保持休息:false,//保留所有特殊前缀当你使用autoprefixer生成的浏览器前缀时,如果你不加这个参数,你的某些前缀可能会被删除keepSpecialComments:'*'})).pipe(gulp.dest(dist+'/css')).pipe(md5(10,dist+'/*.html',{mappingFile:'manifest.json',connector:'.'//文件名和hash之间的连接器})).pipe(gulp.dest(dist+'/css'))//当前对应的css文件}//jsasyncfunctionjs(){returnawaitgulp.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'))//复制}//imageasyncfunctionimage(){returnawaitgulp.src('src/images/*').pipe(Cache(Imagemin({optimizationLevel:5,//类型:Number默认值:3取值范围:0-7(优化级别)progressive:true,//Type:BooleanDefault:falseLosslesscompressedjpgimageinterlaced:true,//Type:BooleanDefault:falseInterlacedscangifforrenderingmultipass:true,//Type:Boolean默认值:false多次优化svg直到完全优化svgoPlugins:[{removeViewBox:false}],//不去掉svg的viewbox属性use:[Pngquant()]//使用pngquant深度压缩png图片的imagemin插件}))).pipe(gulp.dest(dist+'/images'));}//cleandirasyncfunctionclean(){//如果不设置allowEmpty:true,Filenotfoundwithsingularglobreturnawaitgulp.src(dist,{allowEmpty:true}).pipe(Clean());}module.exports={html,css,js,image,clean}4gulpfile.jsconstgulp=require('gulp');//根据环境引入不同的配置文件letbuildConfig;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);//打包htmlgulp.task('js',buildConfig.js);//封装jsgulp.task('css',buildConfig.css);//封装cssgulp.task('images',buildConfig.image);//封装imagegulp.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'));//监听图片变化});//buildif(process.env.NODE_ENV==='dev'){gulp.task('dev',gulp.series('sources','server','watch'));}else{gulp.task('build',gulp.series('sources'));}5package.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”,“一饮而尽干净”:“^0.4.0”,“一口连接”:“^5.7.0”,“一口文件包含”:“^2.0.1”,“一口图像分钟”:"^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=devgulpdev","build":"NODE_ENV=prodgulpclean&&gulpbuild","serve":"http-serverdist/static-p3000"},"devDependencies":{}}如果想获取项目的完整源码和demo,请到gulp4_multi_pages。最后,脚手架还需要完善,比如如何兼容uglify和babel,md5需要二次使用。如果有更好的方案,欢迎交流在脚手架的选择上,不一定要用gulp和webpack,一般的经验是gulp适合传统的静态网站,由于不需要编译es6,所以体积更小,当然也可以用webpack,本文主要目的是给大家提供一个使用gulp4搭建脚手架的思路,希望有所收获。