美文网首页
gulp--搭建基础构建过程

gulp--搭建基础构建过程

作者: 伍超波 | 来源:发表于2021-06-27 21:12 被阅读0次

    定义

    官方定义是the streaming build system,基于流的构建系统。使用文件流目的是实现构建管道的概念,这样使用插件的时候,就可以有一种比较统一的方式。

    基本使用https://gulpjs.com/

    组合任务

    // series 组合串行任务
    // parallel 同步执行任务
    const { series, parallel } = require('gulp')
    
    const task1 = done => {
      setTimeout(() => {
        console.log('task1 working~')
        done()
      }, 1000)
    }
    
    const task2 = done => {
      setTimeout(() => {
        console.log('task2 working~')
        done()
      }, 1000)  
    }
    
    const task3 = done => {
      setTimeout(() => {
        console.log('task3 working~')
        done()
      }, 1000)  
    }
    
    // 让多个任务按照顺序依次执行,编译和部署是串行,可以串行执行
    exports.foo = series(task1, task2, task3)
    
    // 让多个任务同时执行,css和js可以同步执行
    exports.bar = parallel(task1, task2, task3)
    
    

    异步任务

    const fs = require('fs')
    
    // 1. 通过回调方式执行,错误优先
    exports.callback = done => {
      console.log('callback task')
      done()
    }
    // 多任务同时执行时,报错后,后面的任务不会再次执行
    exports.callback_error = done => {
      console.log('callback task')
      done(new Error('task failed'))
    }
    
    // 2. promise方式
    exports.promise = () => {
      console.log('promise task')
      // 需要return Promise对象,不需要返回任何的值,因为gulp会忽略所有返回参数
      return Promise.resolve()
    }
    
    exports.promise_error = () => {
      console.log('promise task')
      return Promise.reject(new Error('task failed'))
    }
    
    // 3. es7 提供的async,node版本为8以上可以使用
    const timeout = time => {
      return new Promise(resolve => {
        setTimeout(resolve, time)
      })
    }
    
    exports.async = async () => {
      await timeout(1000)
      console.log('async task')
    }
    
    // 4. 较为常见的stream方式,读取文件流,返回stream对象
    exports.stream = () => {
      // 读取文件文件流
      const read = fs.createReadStream('yarn.lock')
      // 写入文件流
      const write = fs.createWriteStream('a.txt')
      // 起到文件复制的作用,从一个流 流到另一个流,将read的写入write中
      read.pipe(write)
      //结束时机是read end事件触发的时候,read都有一个end事件
      return read
    }
    
    // 模拟gulp监听end事件
    exports.stream = done => {
      const read = fs.createReadStream('yarn.lock')
      const write = fs.createWriteStream('a.txt')
      read.pipe(write)
      // gulp接收到了readStream,会注册一个end事件,去监听这个任务的结束
      read.on('end', () => {
        done()
      })
    }
    

    构建过程核心原理

    读入:读取文件、 加工:压缩文件 、输出:写入文件

    屏幕快照 2021-06-20 22.36.04.png
    const fs = require('fs')
    const { Transform } = require('stream')
    
    exports.default = () => {
      // 文件读取流
      const readStream = fs.createReadStream('normalize.css')
    
      // 文件写入流
      const writeStream = fs.createWriteStream('normalize.min.css')
    
      // 文件转换流
      const transformStream = new Transform({
        // 核心转换过程
        transform: (chunk, encoding, callback) => {
          const input = chunk.toString()
          const output = input.replace(/\s+/g, '').replace(/\/\*.+?\*\//g, '') // 将css注释删掉
          // node的callback是错误优先的
          callback(null, output)
        }
      })
    
      // return 出去,gulp就可以根据流的状态判断任务是否完成
      return readStream
        .pipe(transformStream) // 转换
        .pipe(writeStream) // 写入
    }
    
    

    gulp 文件操作API

    对比node的文件操作API,gulp的更加强大和更容易使用,对于文件的转换流,一般都是使用插件来完成。流程一般为通过gulp的src, dest两个方法,来实现文件的读写。

    const { src, dest } = require('gulp')
    const cleanCSS = require('gulp-clean-css')
    const rename = require('gulp-rename')
    
    exports.default = () => {
      return src('src/*.css')
        .pipe(cleanCSS())
        .pipe(rename({ extname: '.min.css' }))
        .pipe(dest('dist'))
    }
    

    gulp 实战

    1. gulp样式编译。
    • 安装gulp-sass,内部会自动安装node-sass,会对C++有依赖
    const {src, dest} = require('gulp')
    const sass = require('gulp-sass') // 基本每个插件都返回一个函数
    
    const style = () => {
      return src('src/assets/styles/*.scss', {base: 'src'}) // 当使用 dest() 写入文件系统时,将从输出路径中删除 base ,以保留目录结构。这里指定下base是src。
        .pipe(sass({outputStyle: 'expanded'})) // sass默认不处理下滑线的scss文件,会认为下划线的是主文件(main.scss)依赖的文件,所以不会转换会被忽略掉,例如_icons.scss
        .pipe(dest('dist')) // 以覆盖的方式写入,不会删掉以前的文件
    }
    
    module.exports = {
      style
    }
    
    
    1. 脚本编译(es6)
    • 安装gulp-babel、@babel/core、@babel/preset-env
    const {src, dest} = require('gulp')
    const babel = require('gulp-babel') // 基本每个插件都返回一个函数
    // 单独安装gulp-babel只提供一个转换平台,具体转换流程是依赖插件的,但这个只是帮助调用@babel/core里面的api,所以要安装其他babel插件
    
    const script = () => {
      return src('src/assets/scripts/*.js', {base: 'src'}) // 当使用 dest() 写入文件系统时,将从输出路径中删除 base ,以保留目录结构。这里指定下base是src。
        .pipe(babel({presets: ['@babel/preset-env']})) // 要是没有写@babel/preset-env,则有转换无效的感觉
        .pipe(dest('dist')) // 以覆盖的方式写入,不会删掉以前的文件
    }
    
    module.exports = {
      script
    }
    
    1. 页面模板编译
    • 安装gulp-swig
    const {src, dest} = require('gulp')
    const swig = require('gulp-swig') // 基本每个插件都返回一个函数
    
    const data = {
      menus: [
        {
          name: 'Home',
          icon: 'aperture',
          link: 'index.html'
        },
        {
          name: 'Features',
          link: 'features.html'
        },
        {
          name: 'About',
          link: 'about.html'
        },
        {
          name: 'Contact',
          link: '#',
          children: [
            {
              name: 'Twitter',
              link: 'https://twitter.com/w_zce'
            },
            {
              name: 'About',
              link: 'https://weibo.com/zceme'
            },
            {
              name: 'divider'
            },
            {
              name: 'About',
              link: 'https://github.com/zce'
            }
          ]
        }
      ],
      pkg: require('./package.json'),
      date: new Date()
    }
    
    // 注意:有时直接修改模板,不会立即热更新,主要是因为swig模板引擎缓存的机制导致页面不会变化
    // 此时,需要额外将swig选项中的cache设置为false
    const page = () => {
      return src('src/*.html', { base: 'src' })
        .pipe(plugins.swig({ data, defaults: { cache: false } })) // 防止模板缓存导致页面不能及时更新
        .pipe(dest('temp'))
        .pipe(bs.reload({ stream: true }))
    }
    module.exports = {
      page
    }
    
    
    1. 图片压缩和处理图片字体,因为图片包含一些二进制信息,在我们的生产环境是不需要用到的。
    • 安装gulp-imagemin(内部依赖c++的模块,需要在GitHub上下载一些程序集)
    const {src, dest} = require('gulp')
    const imagemin = require('gulp-imagemin') // 基本每个插件都返回一个函数
    
    const image = () => {
      return src('src/assets/images/**', { base: 'src' })
        .pipe(imagemin())
        .pipe(dest('dist'))
    }
    
    const font = () => {
      return src('src/assets/fonts/**', { base: 'src' })
        .pipe(imagemin())
        .pipe(dest('dist'))
    }
    
    module.exports = {
      image,
      font
    }
    
    
    1. 删除文件和其他文件
    • 安装del ,这个不是gulp插件,但是能在gulp里面使用,因为gulp可以通过自定义实现一个插件
      的形式,来在gulp里面的pipe使用,而del为一个promise
    const {src, dest} = require('gulp')
    
    const del = require('del')
    
    const extra = () => {
      return src('public/**', { base: 'public' })
        .pipe(dest('dist'))
    }
    
    const clean = () => {
      return del(['dist'])
    }
    
    1. 自动加载插件
    • 安装gulp-load-plugins,名称是gulp-sass,则通过plugins.sass,如果是gulp-sass-dev,则为gulp.sassDev
    1. 开发服务器:热更新开发服务器
    • 安装browser-sync,支持在代码修改过后,自动热更新,不是gulp插件,只是通过gulp来管理
    const browserSync = require('browser-sync')
    
    // 通过create方法创建服务
    const bs = browserSync.create()
    // 启动服务
    // 单独定义一个任务启动
    const serve = () => {
      bs.init({
        server: {
          notify: false,
          port: 3002,
          open: false, // 是否自动打开浏览器
          // 网页跟目录,填写网页加工过后的根目录
          baseDir: 'dist',
          // 优先与basedir,先找routes里面配置的文件,没有再找baseDir的
          routes: {
            '/node_modules': 'node_modules',
          },
        },
      })
    }
    
    module.exports = {
    serve
    }
    
    
    1. 监听变化以及构建优化
    • 使用gulp的watch
    • 使用browser-sync启动服务
    1. 先在bs.init 下的files配置写入配置
    const serve = () => {
      bs.init({
        server: {
          notify: false,
          port: 3002,
          files: 'dist/**', // 用来去被browsersync监听的文件,可以配置通配符,指定被监听的文件
          open: false, // 是否自动打开浏览器
          // 网页跟目录,填写网页加工过后的根目录
          baseDir: 'dist',
          // 优先与basedir,先找routes里面配置的文件,没有再找baseDir的
          routes: {
            '/node_modules': 'node_modules',
          },
        },
      })
    }
    
    1. 监听src下面文件的变化
    const { watch } = require('gulp')
    
    const serve = () => {
      watch('src/assets/styles/*.scss', style)
      watch('src/assets/scripts/*.js', script)
      watch('src/*.html', page)
      // watch('src/assets/images/**', image)
      // watch('src/assets/fonts/**', font)
      // watch('public/**', extra)
      // 在开发阶段一般不会经常改,所以不需要监听以下几个文件
      // 只要在发布上线前,编译一遍就好了
      // 这样开发环境就减少了一起编译
      watch(['src/assets/images/**', 'src/assets/fonts/**', 'public/**'], bs.reload) // 三种文件变化过后,执行reload方法
    
      bs.init({
        server: {
          notify: false,
          port: 3002,
          files: 'dist/**', // 用来去被browsersync可以配置通配符,指定被监听的文件
          open: false, // 是否自动打开浏览器
          // 网页跟目录,填写网页加工过后的根目录
          baseDir: 'dist',
          // 优先与basedir,先找routes里面配置的文件,没有再找baseDir的
          routes: {
            '/node_modules': 'node_modules',
          },
        },
      })
    }
    
    1. useref文件引用处理
    • 安装gulp-useref(引用关系),gulp-if
    • gulp-useref这是一款可以将HTML引用的多个CSS和JS合并起来,减小依赖的文件个数,从而减少浏览器发起的请求次数。gulp-useref根据gulp的构建注释将HTML中需要合并压缩的区块找出来,对区块内的所有文件进行合并,然后删掉构建注释。注意:它只负责合并,不负责压缩!,如果需要做其他操作,可以配合gulp-if插件使用
    // 结果放在dist,构建放在temp,目的是防止读写冲突,写不进文件
    const useref = () => {
      return (
        src('temp/*.html', { base: 'temp' }) // 这里找的是打包后的目录下的文件,temp是打包后的文件夹
          .pipe(plugins.useref({ searchPath: ['temp', '.'] })) // 确定注释里面的文件,可以在哪个目录文件找,这里是指定temp文件夹找,找不到就在根目录下找
          // useref处理后会有三种文件类型html js css
          .pipe(plugins.if(/\.js$/, plugins.uglify())) // gulp-if用来判断是哪种类型的文件
          .pipe(plugins.if(/\.css$/, plugins.cleanCss()))
          .pipe(
            plugins.if(
              /\.html$/,
              plugins.htmlmin({
                collapseWhitespace: true, // 自动压缩空白符号和换行符
                minifyCSS: true, // 压缩内敛的css
                minifyJS: true, // 压缩内敛的js
              })
            )
          )
          .pipe(dest('dist'))
      )
    }
    
    1. 补充
    • 开发leader写好了构建流程后,在pacakge.json文件里写好外露的api
    • 在.gitngnore里写忽略文件
    "scripts": {
        "clean": "gulp clean",
        "build": "gulp build",
        "develop": "gulp develop"
      },
    

    相关文章

      网友评论

          本文标题:gulp--搭建基础构建过程

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