构建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任务

最后我们需要将对应的任务合并成一个完整的自动化构建流程

  1. 清空以前生成的目录(dist和temp)
  2. 编译html、css、JavaScript
  3. 压缩html、css、JavaScript
  4. 图片、字体图标压缩和额外文件

部分任务有先后顺序

// 完整构建任务
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
}

以上内容仅供参考和学习总结

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值