构建gulp自动化流程的思路
gulp
gulp本身只是一个平台,并不具备对项目进行构建的能力。主要是由开发人员使用各种插件和业务逻辑实现对应的需求和功能;如es6语法的降级,css预编译代码转化为浏览器可以识别的css代码,html模板引擎等操作
实现思路
这里我按照自己学习到的知识来简单说一下构建一个gulp的流程和思路
- 开发准备
- HTML、Css、JavaScript的构建任务
- 图片、字体图标的压缩和特殊操作
- 开发阶段的web server及热更新
- 完整的构建任务,代码清除、代码和文件的压缩处理
对于gulp任务执行的细节的基本知识就不做过多的解释,这里主要是介绍我开发gulp的思路
项目准备
首先,我们需要一个初始的项目结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s4pAdPn6-1614691055030)(README.assets/image-20210302154921630.png)]
开发准备
gulp自动化构建
首先我们使用的是gulp来对项目进行自动化构建,所以我们需要再项目中安装gulp(这里安装的模块都是开发依赖)
yarn add gulp --dev
gulpfile.js
然后需要在项目根目录下新建一个gulpfile.js文件,将来的自动化构建任务都写在这个文件中。
因为我们在执行gulp的时候,他会默认去执行一个gulpfile.js的文件,如果它找不到该文件则无法执行构建任务
对HTML、Css、JavaScript进行自动化构建
css任务的构建
这个任务主要是将css预编译代码(我用的是sass)转为css代码
const { src, dest } = require('gulp')
const sass = require('gulp-sass')
const styleHandle = () => {
return src('路径统配符', { base: '基础路径' }) // 读取操作
.pipe(sass()) // 转化操作
.pipe(dest('输出路径')) // 转化后的文件先存储到临时文件夹内
}
每一个gulp任务实际就是对文件进行读写的过程,src 和 dest 都是 gulp 的内置方法,src可以读取文件,通过pipe方法将读取的文件流向下一层级进行加工
- 先通过src读取路径统配符对应的文件
- 通过pipe流向sass方法,sass方法可以将sass代码转化为css代码
- 在通过pipe流向指定路径的目录,dest()可以创建一个写入流,可以将pipe流入的内容写入指定的路径
javascript任务的构建
这个任务主要是将高级的es6语法转为低阶的语法
const babel = require('gulp-babel')
const scriptHandle = () => {
return src('路径统配符', { base: '基础路径' })
.pipe(babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('输出路径'))
}
对JavaScript的转化这里主要使用的是babel,这里需要安装的模块是gulp-babel、@babel/core、@babel/preset-env。这是因为gulp-babel实际也只是封装了babel的使用,真正的转化还是需要由babel插件来进行。所以我们需要安装对应的babel插件。
html页面的构建
通常我们会使用模板引擎语法进行html的动态修改,所以在进行自动化构建的时候就需要将模板引擎语法转化为html代码,这里我们主要使用swig模块转化我们的模板引擎代码
const swig = require('gulp-swig')
const pageHandle = () => {
return src('路径统配符', { base: '基础路径' })
.pipe(swig('配置信息'))
.pipe(dest('输出路径'))
}
合并任务
由于上述三个任务都是同类型的,因此,我们可以使用parallel方法来合并上面的三个方法,parallel是gulp内置的方法,可以并行执行传递的任务
const transfer = parallel(styleHandle, scriptHandle, pageHandle)
图片、子图图标的压缩与额外操作
图片的压缩我们用到了gulp-imagemin模块
// 图片和字体图标的压缩
// 图片操作
const imageHandle = () => {
return src('路径统配符', { base: '基础路径' })
.pipe(imagemin())
.pipe(dest('输出路径')) // 直接输出到目标目录中
}
// 字体图标操作
const fontHandle = () => {
return src('路径统配符', { base: '基础路径' })
.pipe(imagemin())
.pipe(dest('输出路径')) // 直接输出到目标目录中
}
// 特殊操作
const extraHandle = () => {
return src('路径统配符', { base: '基础路径' })
.pipe(dest('输出路径'))
}
构建web server服务以及热更新
在开发阶段,需要通过web server服务来对项目进行调试,以及监听内部文件变化并即时更新(热更新),这里使用的是browser-sync模块
const browserSync = require('browser-sync')
// 通过create创建一个web服务
const bs = browserSync.create()
// 启动web服务任务
const serve = () => {
// 开启watch
watch('统配符路径', { cwd: '工作路径' }, '监听变化时执行的任务')
...
// 这里省略,只需要将我们需要监听的文件一类一类的进行监听即可
bs.init({
open: '是否自动打开页面',
port: '端口',
files: '监听的路径统配符', // files会监听指定路径下的文件变化并重启服务
server: {
baseDir: ['查询的基础路径'], // 编译的目录
routes: {
'/node_modules': 'node_modules'
}, // 在编译前会先将routes配置的路径进行替换
}
})
}
构建dev开发模式任务
在开发阶段的时候,我们只关注源代码的编译情况也就是html、css、JavaScript的执行情况,因此我们应该将上面transfer任务和serve任务合并成新的任务,这个任务也是未来我们开发时使用的任务-dev
任务的合并需要使用串行series来合并,这是因为我们必须先编译完html、css、JavaScript代码,才能启动web server服务。
const dev = series(transfer, serve)
完整的构建任务,代码清除、代码和文件的压缩处理
上面我们构建了开发时使用的任务,那么接下来我们就需要一个项目完整的自动化构建任务
html、css、javascript代码的压缩
上面我们曾对HTML、Css、JavaScript进行自动化构建,但这个阶段只是对HTML、Css、JavaScript进行加工处理,我们还需要对其进行压缩处理。
这里我们需要使用到gulp-useref模块和对于的压缩模块插件,这里简单介绍一下gulp-useref的作用
useref实际是通过一个入口文件来读取该文件及其依赖的其他文件的内容。因此这里我们可以将html作为入口文件,就可以读取html以及html依赖的css和js文件内容了
但这又有另外一个问题,因为内容是掺杂在一起的,所以我们需要通过一个插件来判断内容的类型,gulp-if就可以帮我们进行判断
const useref = require('gulp-useref')
const gif = require('gulp-if')
const htmlmin = require('gulp-htmlmin')
const uglify = require('gulp-uglify')
const cleanCss = require('gulp-clean-css')
// 压缩html css js 文件写入输出目录下
const compress = () => {
return src('路径统配符', {options})
.pipe(useref({ searchPath: [conf.temp, '.'] })) // 查询依赖的路径
.pipe(gif(/\.js$/, uglify())) // 通过gulp-if插件来判断当前文件的格式,从而来判断用什么插件进行压缩
.pipe(gif(/\.css$/, cleanCss()))
.pipe(gif(/\.html$/, htmlmin({
collapseWhitespace: true,
minifyJS: true,
minifyCSS: true
}))) // html的压缩需要进行一些配置选项
.pipe(dest('输出路径')) // 同一文件夹的读写有可能会导致压缩的失败,所以将压缩的文件放到临时文件夹内
}
这里需要注意的是html、css、JavaScript的编译任务的文件输出路径和html、css、JavaScript的压缩任务的输出路径不要相同,因为这会导致文件读写冲突,导致压缩失败,我们可以将编译任务的内容写入到临时文件夹temp内,再将temp的内容压缩到dist下会更好
构建clean任务
在进行压缩编译的时候最好将以前生成的文件夹清空,防止无用文件的占用和意外错误
const del = require('del')
const clean = () => {
return del(['需要清空的路径'])
}
构建build任务
最后我们需要将对应的任务合并成一个完整的自动化构建流程
- 清空以前生成的目录(dist和temp)
- 编译html、css、JavaScript
- 压缩html、css、JavaScript
- 图片、字体图标压缩和额外文件
部分任务有先后顺序
// 完整构建任务
const build = series(
clean, // 先清空dist和temp目录
parallel(
series(transfer, compress), // 先将文件编译到temp,再将temp内的文件压缩到dist
extraHandle,
imageHandle,
fontHandle
)
)
示例
const { src, dest, series, parallel, watch } = require('gulp')
const sass = require('gulp-sass')
const babel = require('gulp-babel') // gulp只是一个插件,我们还需要对应的模块@babel/core @babel/preset-env
const swig = require('gulp-swig')
const imagemin = require('gulp-imagemin')
const useref = require('gulp-useref')
const gif = require('gulp-if')
const htmlmin = require('gulp-htmlmin')
const uglify = require('gulp-uglify')
const cleanCss = require('gulp-clean-css')
const del = require('del')
const browserSync = require('browser-sync')
let conf = {
src: 'src',
dist: 'dist',
public: 'public',
temp: '.temp',
paths: {
styles: 'assets/styles/*.scss',
scripts: 'assets/scripts/*.js',
pages: '*.html',
images: 'assets/images/**',
fonts: 'assets/fonts/**',
extra: 'public/**'
},
tmplEngiData: {
menus: [],
pkg: require('./package.json'),
date: new Date()
},
devServe: {
notify: false,
port: '8080',
open: true
}
}
// 变量定义
const srcOptions = { base: conf.src, cwd: conf.src }
const compressSrcOptions = { base: conf.temp, cwd: conf.temp }
const babelOptions = { presets: ['@babel/preset-env'] }
const swigOptions = { defaults: { cache: false }, data: conf.tmplEngiData }
// 通过create创建一个web服务
const bs = browserSync.create()
// 文件清除
const clean = () => {
return del([conf.temp, conf.dist])
}
// 对源文件的加工动作
// 编译处理
// 样式处理任务
const styleHandle = () => {
return src(conf.paths.styles, srcOptions)
.pipe(sass()) // 转化操作
.pipe(dest(conf.temp)) // 转化后的文件先存储到临时文件夹内,为了压缩这些文件至真正的项目中
.pipe(bs.reload({ stream: true }))
}
// 脚本处理任务
const scriptHandle = () => {
return src(conf.paths.scripts, srcOptions)
.pipe(babel(babelOptions))
.pipe(dest(conf.temp))
.pipe(bs.reload({ stream: true }))
}
// 页面布局处理任务
const pageHandle = () => {
return src(conf.paths.pages, srcOptions)
.pipe(swig(swigOptions))
.pipe(dest(conf.temp))
.pipe(bs.reload({ stream: true }))
}
// 将这类编译处理任务合并成并列任务
const transfer = parallel(styleHandle, scriptHandle, pageHandle)
// 图片和字体图标的压缩
// 图片操作
const imageHandle = () => {
return src(conf.paths.images, srcOptions)
.pipe(imagemin())
.pipe(dest(conf.dist)) // 直接输出到目标目录中
}
// 字体图标操作
const fontHandle = () => {
return src(conf.paths.fonts, srcOptions)
.pipe(imagemin())
.pipe(dest(conf.dist)) // 直接输出到目标目录中
}
// 特殊操作
const extraHandle = () => {
return src(conf.paths.extra, srcOptions)
.pipe(dest(conf.dist))
}
// 启动web服务任务
const serve = () => {
// 开启watch
watch(conf.paths.styles, { cwd: conf.src }, styleHandle)
watch(conf.paths.scripts, { cwd: conf.src }, scriptHandle)
watch(conf.paths.pages, { cwd: conf.src }, pageHandle)
watch([
conf.paths.images,
conf.paths.fonts,
], { cwd: conf.src }, bs.reload)
watch('**', { cwd: conf.public }, bs.reload)
const { devServe } = conf
bs.init({
notify: devServe.notify,
open: devServe.open,
port: devServe.port, // 为何不起作用,位置定义错误
// files: 'temp/**', // files会监听指定路径下的文件变化并重启服务,那么files能不能指定src呢?不行,因为我们不仅需要重启还需要重编译
server: {
baseDir: [conf.temp, conf.src, conf.public], // 编译的目录
routes: {
'/node_modules': 'node_modules'
}, // 在编译前会先将routes配置的路径进行替换
}
})
}
// 压缩html css js 文件写入输出目录下
const compress = () => {
return src(conf.paths.pages, compressSrcOptions)
.pipe(useref({ searchPath: [conf.temp, '.'] })) // searchPath的用处?
.pipe(gif(/\.js$/, uglify())) // 通过gulp-if插件来判断当前文件的格式,从而来判断用什么插件进行压缩
.pipe(gif(/\.css$/, cleanCss()))
.pipe(gif(/\.html$/, htmlmin({
collapseWhitespace: true,
minifyJS: true,
minifyCSS: true
}))) // html的压缩需要进行一些配置选项
.pipe(dest(conf.dist)) // 同一文件夹的读写有可能会导致压缩的失败,所以将压缩的文件放到临时文件夹内
}
// 将开发模式的任务合并起来
const dev = series(transfer, serve)
// 完整构建任务
const build = series(
clean, // 先清空dist和temp目录
parallel(
series(transfer, compress), // 先将文件编译到temp,再将temp内的文件压缩到dist
extraHandle,
imageHandle,
fontHandle))
// 导出任务
module.exports = {
clean,
dev,
build
}
以上内容仅供参考和学习总结