美文网首页
实现vue-cli(一):gulp自动化构建

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

作者: Da_xiong | 来源:发表于2021-12-04 15:38 被阅读0次

    一、大概思路

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

    (一)开发阶段的构建
    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文件)
    <!-- build:css assets/styles/vendor.css -->
    <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css">
    <link rel="stylesheet" href="/node_modules/test/test.css">
    <!-- endbuild -->
    
    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自动化构建

          本文链接:https://www.haomeiwen.com/subject/milxrltx.html