- 公司有个新项目要做官网,需要支持国际化,UI设计了很多页面,老板着急要于是我们就直接用 html + css + jquery分工开发了, ,做出来的项目结构是这样的(直接部署到服务器上):
- 等到项目维护迭代的时候就很麻烦,遇到了很多问题:
- 每个html页面都有导航、footer、head等公共页面,修改需要设计所有文件
- 没有使用css预处理器,用惯了sass,css嵌套写起来很别扭
- 用惯了ES6,总是想写let
- 资源文件没有加hash值
- 新加语言种类需要把所有html页面复制一份重新编写
- 正好最近看招聘信息,好多要求要会用webpack和gulp,就想着学学gulp,用gulp搭个脚手架升级一下官网项目。
- 下面来介绍一下这个脚手架的搭建过程
项目介绍
项目简介
- 项目基于gulp、babel7构建
- 使用ejs开发静态页面,支持国际化开发
- js支持commenjs规范以及esm规范
- 使用sass预处理css,使用postcss处理浏览器后缀
- 使用browser-sync构建开发环境,使用http-proxy-middleware处理请求代理
项目结构
├── README.md ├── dev //开发环境打包代码 ├── dist //生产环境打包代码 ├── favicon.ico ├── gulp //gulp配置 ├── gulpfile.babel.js //babel配置 ├── package.json ├── src │ ├── html //index入口文件 │ │ ├── ejs │ │ │ └── footer.ejs │ │ └── index.html │ ├── imgs //图片 │ │ └── 123.jpeg │ ├── js //js │ │ ├── index.js │ │ └── moduleA.js │ ├── lang //国际化语言文件 │ │ ├── en.json │ │ └── zh-cn.json │ ├── scss //sass文件 │ │ ├── common │ │ │ └── _reset.scss │ │ └── index.scss │ └── static //静态文件 │ └── jquery-3.2.1.min.js └── user.config.js.config //开发配置文件 复制代码
使用gulp打包
gulp简介
- gulp相关内容可以查看官网以及其它文章
- gulp配置文件中使用相关库可以查询github了解功能以及使用方法
gulp配置
- gulp主要任务配置都放在了gulp文件夹下
- 根目录新建了gulpfile.babel.js文件,并使用 require-dir 导入gulp文件夹下的任务
//gulpfile.babel.js文件 import requireDir from 'require-dir' requireDir('./gulp/task') requireDir('./gulp') 复制代码
-
项目分为开发环境和生产环境两个主要任务,/gulp/dev.js是开发环境任务,/gulp/prod.js是生产环境任务,/gulp/task/文件夹下是其他任务
-
npm 命令
"scripts": { "start": "npm run dev",//开发环境 "dev": "gulp dev", "build": "gulp prod"//生产环境 }, 复制代码
开发与生产
区别
- 首先看一下生产环境与开发环境区别:
- 生产环境需要资源加hash值,防止用户缓存问题
- 生产环境需要压缩代码
- 开发环境需要建立本地服务器,处理转发请求
- 开发环境文件更改需要同步更新,刷新浏览器
- 开发环境文件需要添加sourcemap配置,方便检查错误
开发环境
- gulp dev
gulp.task('dev', gulp.series( 'clean:dev', 'html:dev', gulp.parallel('scss:dev', 'js:dev', 'static:dev', 'favicon:dev'), 'img:dev', 'server' )) 复制代码
-
开发环境使用browser-sync 搭建服务器,使用 http-proxy-middleware 代理请求
-
同时创建user.config.js.config文件,供开发配置服务端口和请求代理配置使用,需要复制一份改为user.config.js,防止多人开发冲突。
-
开发环境不进行代码压缩等处理,打包后代码放在dev文件夹下
-
/gulp/task/server.js文件中创建server任务
gulp.task('server', function () { browserSync.init({ server: "./dev", port: userConfig.port, middleware: proxyMiddleware }); }); 复制代码
生产环境
- gulp prod
gulp.task('prod', gulp.series( 'clean', 'html:prod', gulp.parallel('scss:prod', 'js:prod', 'static:prod', 'favicon:dist'), 'img:prod' )) 复制代码
- 生产环境任务主要是在开发环境任务基础上,添加压缩、hash编码等任务,打包文件放在dist文件夹下
文件处理
清理文件夹
- 打包前使用 gulp-clean 清空dev|dist文件夹,/gulp/task/clean.js文件中的clean:prod、clean:dev、clean任务
gulp.task('clean:prod',function () { return gulp.src('./dist', {read: false,allowEmpty: true}) .pipe(gulpClean()); }) gulp.task('clean:dev',function () { return gulp.src('./dev', {read: false,allowEmpty: true}) .pipe(gulpClean()); }) gulp.task('clean',gulp.parallel('clean:prod','clean:dev')) 复制代码
处理js
- 由于官网项目不需要太多js操作,因此引入jquery足够了,jquery作为静态文件引入,之后会讲
- /gulp/task/js.js文件处理js任务,项目js入口代码在/src/js/文件夹下
- 首先分析一下需求,因为有多个html页面,每个页面需要处理不同的表单和页面逻辑,因此js也需要有多个入口。
- html中可以使用相对于服务器路径引用js文件
<script src="/js/index.js"></script> 复制代码
- 使用 browserify 打包js文件,可以支持commonjs模块化,同时也使用了 gulp-babel ,使项目支持SE6语法以及esm模块化开发。
- 入口文件配置
//如果需要多个入口文件,则继续配置 let entries = [ { name: 'index', entry: ['src/js/index.js'] } ] 复制代码
- 开发环境处理js,处理流程:任务js:dev->devArrFun循环入口文件->makeBundle打包js->重新命名->生成文件到dev文件夹->同时监听变化->重新打包刷新浏览器
- 生产环境处理js,处理流程:任务js:prod->prodArrFun循环入口文件->bundle打包js存储在dev文件夹中->任务js:dev2dist压缩js、添加hash、替换html文件中的路径
- 任务代码
//使用browserify和babel打包js文件 function makeBundle(name,entry){ if(!bundleArr[name]){ let b = browserify({ entries: entry, debug: devServer, extensions: ['es6'], }) .transform(html2js) .transform(babelify) .on('error', function (err) { console.error(err); }) bundleArr[name] = b } return bundleArr[name] } //直接打包不检测更新 function bundle(name,entry){ let b = makeBundle(name,entry) return b .bundle() .pipe(source(`${name}.js`)) .pipe(buffer()) .pipe(replace('@img', 'img')) .pipe(gulp.dest('dev/js')) .pipe(gulpif(devServer,global.browserSync.reload({stream: true})))//文件变化刷新浏览器 } //开发环境打包 let devArrFun = entries.map(i=>{//循环入口,每个文件都打包 return devFun.bind(null,i.name,i.entry) }) //打包js并检测更新 function devFun(name,entry) { devServer = true let b = makeBundle(name,entry) b.plugin(watchify); //文件变化重新打包js b.on('update',bundle.bind(null,name,entry)) return bundle(name,entry) } //生产环境打包 let prodArrFun = entries.map(i=>{ return bundle.bind(null,i.name,i.entry) }) gulp.task('js',gulp.parallel(prodArrFun)) //开发环境任务 gulp.task('js:dev',gulp.parallel(devArrFun)) //将dev中文件转入dist文件夹中 gulp.task('js:dev2dist',function () { return gulp.src('dev/js/*.js') .pipe(uglify()) .pipe(md5(6, './dist/*.html')) .pipe(gulp.dest('dist/js')) }) //生产环境任务 gulp.task('js:prod',gulp.series('js','js:dev2dist')) 复制代码
处理css
- 使用sass预处理css,使用postcss的autoprefixer添加浏览器前缀,/src/scss/文件夹放置sass入口文件
- 使用相对于服务器路径引用css文件
<link rel="stylesheet" href="/css/index.css"> 复制代码
- 处理流程依然是查找入口文件,打包scss文件,同时开发环境监听文件变化刷新浏览器,生产环境进一步处理开发环境打包的文件。
- 任务代码
function scss() { return gulp .src('./src/scss/*.scss')//查找入口文件 .pipe(gulpif(devServer,sourcemaps.init()))//开发环境添加sourcemap配置 .pipe(sass().on('error', sass.logError)) .pipe(postcss([autoprefixer()]))//添加浏览器前缀 .pipe(replace('../imgs', '../imgs'))//处理图片路径 .pipe(replace('../../imgs', '../imgs')) .pipe(gulpif(devServer,sourcemaps.write())) .pipe(gulp.dest('./dev/css'));//开发环境存放文件 } gulp.task('scss',scss) gulp.task('scss:dev', function () { devServer = true //开发环境监听文件变化重新打包并刷新浏览器 gulp.watch(['./src/scss/*.scss','./src/scss/*/*.*'], function (event) { return scss().pipe(global.browserSync.reload({stream: true})); }); return scss() }); gulp.task('scss:dev2dist',function () { return gulp.src('./dev/css/*.css') .pipe(webpcss())//处理webp文件 .pipe(cleanCSS())//压缩文件 .pipe(md5(6, './dist/*.html'))//添加hash,并替换html中的文件名称 .pipe(gulp.dest('./dist/css'));//生产环境保存文件 }) gulp.task('scss:prod', gulp.series('scss','scss:dev2dist')); 复制代码
处理图片
- css以及html中使用图片可以直接相对路径引用,在scss以及html打包中会替换img文件路径
- 图片存储在/src/imgs/文件夹中,目前只支持两级目录
- 开发环境只是图片复制,生产环境会压缩图片、添加hash、替换css\html中的文件
- 任务代码
const srcArr = ['./src/imgs/*.{png,gif,jpg,jpeg}','./src/imgs/*/*.{png,gif,jpg,jpeg}'] function img(){ return gulp .src(srcArr) .pipe(gulp.dest('./dev/imgs')) } gulp.task('img',img) gulp.task('img:dev',function () { gulp.watch(srcArr, function (event) { return img(event.path) .pipe(global.browserSync.reload({stream: true})) }); return img(); }) gulp.task('img:dev2dist',function () { return gulp .src(srcArr) .pipe(imagemin())//压缩图片 .pipe(md5(6, ['./dist/*.html', './dist/css/*.css', './dist/js/*.js']))//添加hash,替换文件名 .pipe(gulp.dest('./dist/imgs')) }) gulp.task('img:prod',gulp.series('img','img:dev2dist')) 复制代码
处理静态文件
- jquery等其他库可以放在/src/static/文件加下,在html中使用相对于服务器路径引用即可,/gulp/task/static.js中的任务负责,处理静态资源的复制。
<script src="/static/jquery-3.2.1.min.js"></script> 复制代码
ejs以及国际化配置
- 最后一项是对于html的处理,再来回顾一下我们的需求
- 导航、footer、head等可以使用公共模板
- 添加国际化配置文件可以生成不同语言页面
- 因此我使用了ejs 来编写html,同时添加语言配置的json文件,打包出不同的语言页面
处理公共html模块
- /src/html/文件夹存放html以及ejs文件,使用ejs加载公共模块
<%- include('ejs/footer',{type: common.type}) %> 复制代码
html入口
- 处理html的任务在/task/html.js中,每次新增页面,需要配置html入口文件
//该配置会打包出index.html(中文)以及index-en.html(英文)两个页面 //html入口文件 let htmlConfig = [ { entry: 'src/html/index.html', name: 'index.html',//打包后文件名 lang: 'zh-cn'//ejs语言配置文件,对应/src/lang下的json文件 }, { entry: 'src/html/index.html', name: 'index-en.html', lang: 'en' }, ] 复制代码
国际化处理
- /src/lang/文件夹中为不语言添加不同配置
- 举个例子:
- 比如en.json以及zh-cn.json的配置如下
//en.json { "footer": "footer" } //zh-cn.json { "footer": "福特儿" } 复制代码
- ejs中使用模板
<div><%= footer %></div> 复制代码
- 打包出html分别为
//index.html <div>福特儿</div> //index-en.html <div>footer</div> 复制代码
-
我们还可使用ejs其他语法编写比如:按条件渲染生成不同html处理页面差异、用变量处理dom元素属性、生成不同class处理语言显示等问题,这些就需要靠你的智慧了。
-
任务代码
//处理ejs模板 function html(config){ return gulp .src(config.entry) .pipe(data(function(file) {//加载语言配置 return JSON.parse(fs.readFileSync(`src/lang/${config.lang}.json`)) })) .pipe(replace('../imgs', './imgs'))//替换图片路径 .pipe(replace('../../imgs', './imgs')) .pipe(ejs().on('error', handleError))//错误处理 .pipe(rename(config.name))//替换文件名 .pipe(gulp.dest('dev')) } let htmlDevArr = htmlConfig.map(config=>{ return function (config) { //监听变化重新打包并且刷新浏览器 gulp.watch([config.entry,'src/html/*/*.*','src/lang/*'], function (event) { return html(config).pipe(global.browserSync.reload({stream: true})); }); return html(config) }.bind(null,config) }) //开发环境命令 gulp.task('html:dev',gulp.parallel(htmlDevArr)) gulp.task('html:dev2dist',function () { return gulp .src('dev/*.html') .pipe(htmlmin({ collapseWhitespace: true }))//压缩html .pipe(gulp.dest('dist')) }) let htmlProdArr = htmlConfig.map(config=>{ return function (config) { return html(config) }.bind(null,config) }) //生产环境命令 gulp.task('html:prod',gulp.series(gulp.parallel(htmlProdArr),'html:dev2dist')) 复制代码
错误处理
- 开发中还遇到了一个问题:ejs模板报错时会停止gulp任务,需要使用 gulp-notify 处理报错,防止任务终止
const notify = require("gulp-notify"); module.exports = function(){ var args = Array.prototype.slice.call(arguments) notify.onError({ title: 'compile error', message: '<%=error.message %>' }).apply(this, args) this.emit(); } 复制代码
总结
- 以上就是gulp官网项目脚手架搭建过程,由于是一边学习gulp一边搭的,很多处理方法都是一边搜索一边找合适的加上的,希望大家多提意见。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
PCI Express 体系结构导读
王齐 / 机械工业 / 2010-3 / 55.00元
《PCI Express 体系结构导读》讲述了与PCI及PCI Express总线相关的最为基础的内容,并介绍了一些必要的、与PCI总线相关的处理器体系结构知识,这也是《PCI Express 体系结构导读》的重点所在。深入理解处理器体系结构是理解PCI与PCI Express总线的重要基础。 读者通过对《PCI Express 体系结构导读》的学习,可超越PCI与PCI Express总线......一起来看看 《PCI Express 体系结构导读》 这本书的介绍吧!