实现vue-cli(一):gulp自动化构建

一、大概思路

一般自动化构建做的主要是以下几件事情:

(一)开发阶段的构建
  1. 将scss转成css,并在临时目录下生成对应的css文件(在开发阶段不需要压缩,所以将压缩放在单独的步骤里)。
  2. 将es6等浏览器不支持的新特性用babel转成浏览器支持的js语法,并生成文件。
  3. 根据提供的html模板(主要做的是数据填充)生成对应的html代码和文件。
  4. 启动一个服务器打开页面,并监听上面三步和图片等文件变化,若有变化则刷新页面。
(二)发布阶段的构建
  1. ...前三个步骤和开发阶段一样,都是处理html、js、css。
  2. 清空构建目录下的所有旧文件。
  3. 压缩字体文件和图片文件资源到构建目录下。
  4. 将public里不需要处理的资源,直接拷贝生成到构建目录下。
  5. 将前三步临时目录里的css、js、html文件做压缩混淆合并处理,并生成对应文件到构建目录下,将处理后的生成css和js文件插入到html里。

注意前三步的编译都是在临时目录下,最后压缩混淆合并才在构建目录下。之所以分两个目录,是因为在第7步对同个目录边读边写会产生冲突。

二、“开发阶段构建任务”具体实现

(一)准备工作
  1. 请确保自己本地已有npm或yarn等包管理工具,本文用yarn做演示。
  2. 下载该演示项目,或者自行vue-cli创建一个项目。
  3. 在该文件夹下空白处“按shift+鼠标右键”,选中“在此处打开命令行/powershell窗口”打开命令行窗口,或者自行通过命令窗口cd到该文件目录下。
  4. 命令行输入yarn init回车,自行填写信息一路回车,最后生成package.json配置文件。
  5. 命令行输入yarn add gulp --dev安装gulp自动构建工具。
  6. 在演示项目下的gulpfile.js输入下面代码(gulpfile.js是gulp的运行文件)。
const { src, dest } = require("gulp")

const extra = () => {
  // src是gulp中读取文件的方法,dest是gulp中写入文件到目标位置的方法
  // 将public目录下,以public为根目录的所有文件拷贝到dist目录下
  return src('public/**', {base: 'public'})
    .pipe(dest('dist'))
}

module.exports = {
  extra,
}
  1. 命令行输入yarn gulp extra(extra是gulpfile.js对应的函数任务名),如果能将public下文件拷贝到dist则证明成功。
(二)css处理
  1. 由于需要依赖到node-sass国外源可能会出现安装失败的问题,建议先设置npm的安装源为国内的淘宝镜像npm config set registry http://registry.npm.taobao.org
  2. 命令行输入yarn add gulp-sass sass --dev安装gulp处理sass的插件。
  3. gulpfile.js文件输入以下代码,再执行yarn gulp style成功生成temp对应css文件则成功。
const { src, dest } = require("gulp")
const sass = require('gulp-sass')

const style = () => {
  return src('src/assets/styles/*.scss', {base: 'src'})
    .pipe(sass({outputStyle: 'expanded'})) // _开头的sass文件被认为是依赖文件,不会被转换。expanded可以让右括号换行而不是跟在分号后面
    .pipe(dest('temp'))
}

module.exports = {
  style,
}

由于后续需要加载比较多的gulp插件,需要频繁写require,此处采用一个gulp-load-plugins来自动加载所需插件,命令行输入yarn add gulp-load-plugins --dev,代码改写为如下

const { src, dest } = require("gulp")
const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()

const style = () => {
  return src('src/assets/styles/*.scss', {base: 'src'})
    .pipe(plugins.sass({outputStyle: 'expanded'})) // _开头的sass文件被认为是依赖文件,不会被转换。expanded可以让右括号换行而不是跟在分号后面
    .pipe(dest('temp'))
}

module.exports = {
  style,
}
(三)js处理
  1. 命令行输入yarn add gulp-babel @babel/core @babel/preset-env --dev安装gulp转码js的插件。
  2. gulpfile.js文件增加以下代码,再执行yarn gulp script成功生成temp对应已转码好的js文件则成功。
const script = () => {
  return src('src/assets/scripts/*.js', {base: 'src'})
    .pipe(plugins.babel({presets: ['@babel/preset-env']}))
    .pipe(dest('temp'))
}

module.exports = {
  ...,
  script,
}
(四)html处理
  1. 命令行输入yarn add gulp-swig --dev安装gulp启动服务器并监听文件变化的插件。
  2. gulpfile.js文件增加以下代码,再执行yarn gulp page成功生成temp对应已插入好数据的html文件则成功。
const data = {
  menu: [],
  pkg: require('./package.json'),
  date: new Date(),
}

const page = () => {
  return src('src/*.html', {base: 'src'})
    .pipe(plugins.swig({data, defaults: {cache: false}})) // 指定页面中的插值data,根据模板生成html。记得忽略缓存,否则可能更新失败
    .pipe(dest('temp'))
    .pipe(bs.reload({stream: true}))
}

module.exports = {
  ...,
  page,
}
(五)服务器上启动页面并监听变化
  1. 命令行输入yarn add browser-sync --dev安装gulp对页面模板进行编译的插件。
  2. gulpfile.js文件增加以下代码,再执行yarn gulp serve,成功在浏览器启动页面,并且修改sass/js/html时会同步刷新页面变化则成功。
const { src, dest, watch } = require("gulp")
const browserSync = require('browser-sync') // 不是gulp插件所以要自己导入
const bs = browserSync.create() // 创建服务器

const serve = () => {
  // 由于bs.init只监听了temp下已经编译好文件的变化,但我们真正编辑是在src下的未处理文件,所以要先监听src文件变化触发编译到temp触发bs监听
  watch('src/assets/styles/*.scss', style)
  watch('src/assets/scripts/*.js', script)
  watch('src/*.html', page)
  // 图片等资源不在temp目录下,监听到变化了要手动调用bs.reload
  watch([
    'src/assets/images/**',
    'src/assets/fonts/**',
    'public/**',
  ], bs.reload)

  bs.init({
    notify: false, // 不提示刷新
    port: 2000,
    open: true, // 是否自动打开浏览器
    files: 'temp/**', // 哪些文件需要监听修改
    server: { // 先根据routes配置去指定目录找文件,找不到再根据baseDir配置去找
      routes: { // 遇到/node_modules路径时去node_modules目录下找文件
        '/node_modules': 'node_modules',
      },
      baseDir: ['temp', 'src', 'public'], // 需要寻找对应文件时,可以先在temp目录下找有没有,没有再找src再public
    },
  })
}

module.exports = {
  ...,
  serve,
}
(六)整合为开发阶段构建任务

此处通过gulp的两个方法parallelseries来整合上面四步任务。parallel方法可以同时开始执行任务,series方法是按顺序先后执行任务。整合代码如下,命令行yarn gulp develop即可运行该任务。

const compile = parallel(style, script, page) // 因为三个任务互不影响所以用parallel
const develop = series(compile, serve) // 要先编译才有temp目录,才能监听变化,所以用series

module.exports = {
  compile,
  develop,
}

三、“发布阶段构建任务”具体实现

(一)清空构建目录
  1. 命令行输入yarn add del --dev安装删除文件的插件。
  2. gulpfile.js文件增加以下代码,再执行yarn gulp clean,成功删除目录下的指定文件则成功。
const del = require('del')

const clean = () => {
  return del(['dist']) // del返回的是一个promise
}

module.exports = {
  clean,
}
(二)压缩图片和字体
  1. 命令行输入yarn add gulp-imagemin --dev安装压缩图片的插件。
  2. gulpfile.js文件增加以下代码,再执行yarn gulp image font,生成了对应图片并比原文件小则成功。
const image = () => {
  return src('src/assets/images/**', {base: 'src'}) // **任意文件
    .pipe(plugins.imagemin())
    .pipe(dest('dist'))
}

const font = () => { // 字体也有svg可以用同样压缩
  return src('src/assets/fonts/**', {base: 'src'})
     .pipe(plugins.imagemin())
     .pipe(dest('dist'))
 }

module.exports = {
  image,
  font,
}
(三)拷贝public资源

这段在开头准备工作中其实已经讲过,直接复制代码过来,不再赘述。

const extra = () => {
  // src是gulp中读取文件的方法,dest是gulp中写入文件到目标位置的方法
  // 将public目录下,以public为根目录的所有文件拷贝到dist目录下
  return src('public/**', {base: 'public'})
    .pipe(dest('dist'))
}
(四)压缩处理js/css/html,并将处理后的js/css插入html
  1. 命令行输入yarn add gulp-useref --dev安装处理引用关系的插件,可以将类似如下的特定注释内的引用文件打包合并到指定文件中。(这段代码表示把bootstrap.css和test.css两个文件打包合并成assets/styles/目录下一个vendor.css文件)




  1. 命令行输入yarn add gulp-htmlmin gulp-uglify gulp-clean-css --dev安装压缩处理html/js/css的gulp插件,yarn add gulp-if --dev安装用于判断当前文件类型并做对应处理的gulp插件。
  2. gulpfile.js文件增加以下代码,再执行yarn gulp useref,生成了对应各压缩文件并插入到html则成功。
const useref = () => {
   return src('temp/*.html', {base: 'temp'})
    .pipe(plugins.useref({searchPath: ['temp', '.']})) // 指定html里写的引入文件去哪里找
    // 找到的html、js、css文件分别做不同的处理
    .pipe(plugins.if(/\.js$/, plugins.uglify()))
    .pipe(plugins.if(/\.css$/, plugins.cleanCss()))
    .pipe(plugins.if(/\.html$/, plugins.htmlmin({
      collapseWhitespace: true,
      minifyCSS: true, // 对html里的css代码做压缩
      minifyJS: true,
    })))
    .pipe(dest('dist'))
 }

module.exports = {
  ...,
  useref,
}
(五)整合为发布阶段构建任务

由于useref执行完一次之后,temp里的html已经被压缩,特定注释被删除,所以再次运行useref任务无法达到想要的效果,所以必须先编译compile生成注释代码,再执行useref才有效。整合代码如下,命令行yarn gulp build即可运行该任务。

// 先删除旧目录,再执行构建任务生成对应文件
const build = series(clean, parallel(series(compile, useref), image, font, extra))

module.exports = {
  compile,
  develop,
  build,
}

四、最终gulpfile.js代码

const { src, dest, watch, parallel, series } = require("gulp")
const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()
const browserSync = require('browser-sync') // 不是gulp插件(gulp-开头)所以要自己导入
const bs = browserSync.create() // 创建服务器
const del = require('del')
const data = {
  menu: [],
  pkg: require('./package.json'),
  date: new Date(),
}

const style = () => {
  return src('src/assets/styles/*.scss', {base: 'src'})
    .pipe(plugins.sass({outputStyle: 'expanded'})) // _开头的sass文件被认为是依赖文件,不会被转换。expanded可以让右括号换行而不是跟在分号后面
    .pipe(dest('temp'))
    // .pipe(bs.reload({stream: true})) // 如果bs初始化files项没配置,也可以在watch到之后在任务里reload
}

const script = () => {
  return src('src/assets/scripts/*.js', {base: 'src'})
    .pipe(plugins.babel({presets: ['@babel/preset-env']}))
    .pipe(dest('temp'))
    // .pipe(bs.reload({stream: true}))
}

const page = () => {
  return src('src/*.html', {base: 'src'})
    .pipe(plugins.swig({data, defaults: {cache: false}})) // 指定页面中的插值data,根据模板生成html。记得忽略缓存,否则可能更新失败
    .pipe(dest('temp'))
    // .pipe(bs.reload({stream: true}))
}

const serve = () => {
  // 由于bs.init只监听了temp下已经编译好文件的变化,但我们真正编辑是在src下的未处理文件,所以要先监听src文件变化触发编译到temp触发bs监听
  watch('src/assets/styles/*.scss', style)
  watch('src/assets/scripts/*.js', script)
  watch('src/*.html', page)
  // 图片等资源不在temp目录下,监听到变化了要手动调用bs.reload
  watch([
    'src/assets/images/**',
    'src/assets/fonts/**',
    'public/**',
  ], bs.reload)

  bs.init({
    notify: false, // 不提示刷新
    port: 2000,
    open: true, // 是否自动打开浏览器
    files: 'temp/**', // 哪些文件需要监听修改
    server: { // 先根据routes配置去指定目录找文件,找不到再根据baseDir配置去找
      routes: { // 遇到/node_modules路径时去node_modules目录下找文件
        '/node_modules': 'node_modules',
      },
      baseDir: ['temp', 'src', 'public'], // 需要寻找对应文件时,可以先在temp目录下找有没有,没有再找src再public
    },
  })
}

const clean = () => {
  return del(['dist']) // del返回的是一个promise
}

const image = () => {
  return src('src/assets/images/**', {base: 'src'}) // **任意文件
    .pipe(plugins.imagemin())
    .pipe(dest('dist'))
}

const font = () => { // 字体也有svg可以用同样压缩
  return src('src/assets/fonts/**', {base: 'src'})
    .pipe(plugins.imagemin())
    .pipe(dest('dist'))
 }

 const extra = () => {
   // src是gulp中读取文件的方法,dest是gulp中写入文件到目标位置的方法
   // 将public目录下,以public为根目录的所有文件拷贝到dist目录下
   return src('public/**', {base: 'public'})
     .pipe(dest('dist'))
 }

 const useref = () => {
   return src('temp/*.html', {base: 'temp'})
    .pipe(plugins.useref({searchPath: ['temp', '.']})) // 指定html里写的引入文件去哪里找
    // 找到的html、js、css文件分别做不同的处理
    .pipe(plugins.if(/\.js$/, plugins.uglify()))
    .pipe(plugins.if(/\.css$/, plugins.cleanCss()))
    .pipe(plugins.if(/\.html$/, plugins.htmlmin({
      collapseWhitespace: true,
      minifyCSS: true, // 对html里的css代码做压缩
      minifyJS: true,
    })))
    .pipe(dest('dist'))
 }

const compile = parallel(style, script, page) // 因为三个任务互不影响所以用parallel
const develop = series(compile, serve) // 要先编译才有temp目录,才能监听变化,所以用series
// 先删除旧目录,再执行构建任务生成对应文件
const build = series(clean, parallel(series(compile, useref), image, font, extra))

module.exports = {
  compile,
  develop,
  build,
}

你可能感兴趣的:(实现vue-cli(一):gulp自动化构建)