美文网首页
学习笔记(八)——自动化构建

学习笔记(八)——自动化构建

作者: 彪悍de文艺青年 | 来源:发表于2020-09-09 14:54 被阅读0次

    最近参加拉勾教育大前端高薪训练营,开始对前端知识体系进行一个系统的扩展跟学习,通过每天定期的学习,对一些不常使用的知识点进行了了解和补充,同时对已经使用过的知识点温故而知新
    在此记录学习笔记,并根据学习进度定时跟新

    自动化构建

    一切重复的工作都应该被自动化

    自动化构建是前端工程化过程中一个重要的组成部分

    自动化构建工作流可以使我们在开发阶段使用一些高效的语法、规范、标准和工具,脱离运行环境兼容性带来的问题,并最终自动转换成能够被运行环境支持的可执行代码

    自动化构建初体验

    尝试使用sass开发页面样式,并自动编译为css文件

    • 创建项目目录,并使用 yarn init -y 初始化

    • 添加index.html页面用于测试

    • yarn add sass --dev 添加sass模块作为开发依赖

      • 此时可以使用sass编写样式文件*.scss,并使用 yarn sass <source> <target> 命令将scss文件编译为css文件
    • 在package.json文件中,添加script属性,定义npm运行脚本

      • 此时可以通过 npm run <command>yarn <command> 运行script中定义的命令
    • yarn add npm-run-all --dev 安装npm-run-all作为开发依赖,可以用来顺序同时运行多个script中定义的命令

    • yarn add browser-sync --dev 安装browser-sync作为开发依赖,可以监控指定目录下的文件改动,并自动刷新浏览器

    • 上述步骤实现了开发过程中,自动将scss文件编译为css文件,并自动监听文件变化,刷新浏览器实时查看最新页面效果

      // package.json
      {
        "name": "sample",
        "version": "1.0.0",
        "main": "index.js",
        "license": "MIT",
        "scripts": {
          "build": "sass scss/main.scss css/main.css --watch",
          "serve": "browser-sync . --files \"*.html, css/*.css\"",
          "start": "run-p build serve"
        },
        "devDependencies": {
          "browser-sync": "^2.26.12",
          "sass": "^1.26.10",
          "npm-run-all": "^4.1.5"
        }
      }
      
      

    常用的自动化构建工具

    image-20200903162834889
    • Grunt
      • 插件生态丰富,可以实现任意类型项目的构建
      • 工作过程基于临时文件,磁盘读写频繁,构建速度较慢
    • Gulp
      • 插件生态丰富
      • 工作过程基于内存,构建速度较快
      • 支持同时进行多个构建任务
    • FIS
      • 由百度前端团队推出的构建工具
      • 捆绑式全家桶
      • 适合新手
    • Webpack?
      • Webpack属于模块打包工具

    Grunt

    • 基本使用

      • 新建项目目录,并使用 yarn init -y 命令初始化

      • yarn add grunt 添加grunt模块

      • 添加gruntfile.js文件

        • Grunt的入口文件
        • 用于定义Grunt自动执行的任务
        • 需要导出一个函数
        • 函数接收grunt形参,用于提供创建任务时将会用到的的API
      • grunt.registerTask 注册待执行的task

        • 第一个参数为task的名称,如果名称为default,则为默认task,执行 yarn grunt 不指定task明时默认执行 default task
        • 第二个参数如果是回调函数,则表示task执行时要执行的内容
        • 第二个参数如果是字符串,则是对该task的描述信息,当执行yarn grunt --help 时会显示相应任务的描述
        • 第二个参数如果是数组,则接收由task名称组成的字符串数组,执行该task将会依次执行数组中指定的task
        • 如果task执行的是一个异步任务,需要使用调用 this.async() 返回的函数来标记异步操作执行完成,此时grunt会等待异步操作执行完成
      • yarn grunt <task> 执行gruntfile.js中定义的task

        // gruntfile.js
        module.exports = grunt => {
            grunt.registerTask('foo', () => {
                console.log('hello grunt')
            })
        
            grunt.registerTask('bar', 'description', () => {
                console.log('hello bar')
            })
        
            grunt.registerTask('default', ['foo', 'bar'])
        
            grunt.registerTask('async-task', function() {
                const done = this.async();
                setTimeout(()=> {
                    console.log('async task done')
                    done()
                }, 1000)
            })
        }
        
    • 标记任务失败

      • 同步任务通过在一个task的回调函数中 return false 来实现

      • 顺序执行多个任务时,当有一个任务被标记失败,后续任务将不再继续执行

      • 使用 --force 参数来强制执行所有任务

      • 异步任务标记失败,需要使用 this.async() 返回的函数,传入false

        // gruntfile.js
        module.exports = grunt => {
            grunt.registerTask('foo', () => {
                console.log('hello grunt')
            })
        
            grunt.registerTask('bar', 'description', () => {
                console.log('hello bar')
            })
        
            grunt.registerTask('bad', ()=> {
                console.log('bad task')
                return false
            })
        
            grunt.registerTask('default', ['foo', 'bad', 'bar'])
        
            grunt.registerTask('async-task', function() {
                const done = this.async();
                setTimeout(()=> {
                    console.log('async task fail')
                    done(false)
                }, 1000)
            })
        }
        
    • 配置方法

      • 使用 grunt.initConfig({}) 方法进行grunt配置参数的初始化

      • 在task的回调函数中,通过 grunt.config(key) 方法可以获取参数配置对象中的值

        // gruntfile.js
        module.exports = grunt => {
            grunt.initConfig({
                hello: {
                    what: 'world'
                }
            })
        
            grunt.registerTask('foo', () => {
                console.log(`hello ${grunt.config('hello.what')}`)
            })
        }
        
    • 多目标任务

      多目标模式,可以让任务根据配置形成多个子任务

      • 使用 grunt.registerMultiTask(<task>, <func> 来注册多目标任务

      • 多目标任务需要通过 grunt.initConfig 来配置相应的子任务

        • 以task名称作为key,value是配置对象
        • 配置对象以子任务名称作为key,在任务运行时可以通过 this.target 获取当前执行的子任务名称
        • 配置对象的值为运行时的配置数据,在任务运行时可以通过 this.data 获取当前执行的子任务的配置数据
        • 当配置对象的key为options时,代表任务运行时的选项配置,而不代表一个子任务
        • 子任务的配置数据中也可以包含options,此时该子任务中配置的options覆盖任务全局的options
        // gruntfile.js
        module.exports = grunt => {
            grunt.initConfig({
                build: {
                    options: {
                        hello: 'world'
                    },
                    css: {
                        value: 'css'
                    },
                    js: {
                        options: {
                            hello: 'grunt'
                        },
                        value: 'js'
                    }
                },
                hello: {
                    what: 'world'
                }
            })
            grunt.registerMultiTask('build', function() {
                console.log(this.options())
                console.log(this.target)
                console.log(this.data)
            })
        }
        
    • 插件的使用

      • 安装相应的grunt插件模块,例如 yarn add grunt-contrib-clean

      • 使用 grunt.loadNpmTasks 加载插件,并在 grunt.initConfig() 中配置相关的参数(这里grunt-contrib-clean是多目标任务)

      • 执行插件相关的task

        // gruntfile.js
        module.exports = grunt => {
            grunt.initConfig({     
                clean: {
                    temp: 'temp/*.txt'
                }
            })
            grunt.loadNpmTasks('grunt-contrib-clean')
        }
        
    • 常用插件及总结

      • grunt-sass 用于scss转css
        • yarn add grunt-sass sass --dev
      • grunt-babel 用于js语法转换
        • yarn add grunt-babel @babel/core @babel/present-env --dev
      • grunt-contrib-watch 开发阶段监控文件改动,并自动执行配置指定的task
        • yarn add grunt-contirb-watch --dev
      • load-grunt-tasks 用于自动加载所有grunt插件中的任务
        • yarn add load-grunt-tasks --dev

    Gulp

    • gulp的基本使用

      • 新建项目目录,并使用 yarn init -y 命令初始化

      • yarn add gulp 添加gulp模块

      • 添加gulpfile.js文件

        • gulp的入口文件
        • 用于定义gulp自动执行的任务
        • gulp 4.0以上的版本通过exports.task的方式导出一个task,exports.default为默认的task
        • gulp定义任务为异步任务,需要在任务执行的函数中调用参数传入的方法,去标记任务是否执行完成
      • yarn gulp <task> 执行gulpfile.js中定义的task

        // gulpfile.js  gulp的入口文件
        
        exports.foo  = done => {
            console.log('gulp foo')
            done()
        }
        
        exports.default = done => {
            console.log('gulp default')
            done()
        }
        
        // gulp 4.0以前版本定义task方式
        const gulp = require('gulp')
        
        gulp.task('bar', done => {
            console.log('gulp bar')
            done()
        })
        
    • gulp的组合任务

      • gulp提供了 seriesparallel 两种方式来组合执行多个任务
      • series为串行执行多个任务
      • parallel为并行执行多个任务
      // gulpfile.js
      const { series, parallel } = require('gulp')
      
      const task1 = done => {
          setTimeout(() => {
              console.log('task 1 working')
              done()
          }, 1000);
      }
      
      const task2 = done => {
          setTimeout(() => {
              console.log('task 2 working')
              done()
          }, 1000);
      }
      
      const task3 = done => {
          setTimeout(() => {
              console.log('task 3 working')
              done()
          }, 1000);
      }
      
      exports.series = series(task1, task2, task3)
      
      exports.parallel = parallel(task1, task2, task3)
      
    • gulp的异步任务常用的几种方式

      • 回调函数方式

        • gulp异步任务函数接收一个回调函数参数,通过该回调函数,可以标记异步任务是否执行完成,或者发生异常,当发生异常,后续任务将不再继续执行

          exports.callback = done => {
              console.log('callback')
              done()
          }
          
          exports.callback_error = done => {
              console.log('callback error')
              done(new Error('callback error'))
          }
          
      • Promise

        • gulp异步任务支持使用Promise方式,标记异步任务执行完成,返回一个resolved状态的Promise对象,标记任务失败,返回一个rejected状态的Promise对象

          exports.promise = () => {
              console.log('promise')
              return Promise.resolve()
          }
          
          exports.promise_error = () => {
              console.log('promise error')
              return Promise.reject(new Error('promise error'))
          }
          
      • async/await

        • async/await是Promise的语法糖,使用node版本8以上时,gulp也支持使用该方式处理异步任务

          const timeout = time => {
              return new Promise((resolve) => {
                  setTimeout(resolve, time)
              })
          }
          
          exports.async = async () => {
              await timeout(1000)
              console.log('async timeout')
          }
          
          
      • stream

        • 通常在自动化构建过程中需要处理大量的文件,stream也是gulp异步任务处理中常用的方式

        • 通过返回一个stream对象,当stream的end方法被调用时,异步任务会被标记完成

          const fs = require('fs')
          
          exports.stream = () => {
              const readStream = fs.createReadStream('package.json')
              const writeStream = fs.createWriteStream('temp.txt')
              readStream.pipe(writeStream)
              return readStream
          }
          // 等同于
          exports.stream_callback = done => {
              const readStream = fs.createReadStream('package.json')
              const writeStream = fs.createWriteStream('temp.txt')
              readStream.pipe(writeStream)
              readStream.on('end', () => {
                  done()
              })   
          }
          
    • gulp构建过程核心工作原理

      官方对于gulp的定义是the streaming build system

      即基于流的构建系统

      • 构建的流程通常是:读取文件 -> 处理读取内容 -> 写入文件

      • 对应gulp的工作过程:读取流(read stream) -> 转换流(transform stream) -> 写入流 (write stream)

        // 尝试模拟实现css的压缩
        const fs = require('fs')
        const { Transform } = require('stream')
        
        exports.transform = () => {
            const read = fs.createReadStream('style.css')
            const write = fs.createWriteStream('style.min.css')
        
            const transform = new Transform({
                transform: (chunk, encoding, callback) => {
                    const input = chunk.toString()
                    const output = input.replace(/\s+/g, '').replace(/\/\*.+?\*\//g, '') // 去除空格及注释
                    callback(null, output)
                }
            })
        
            return read
                .pipe(transform)
                .pipe(write)
        }
        
    • gulp文件操作API

      • 使用gulp提供的src与dest来实现文件的读取流与写入流

        // 使用src批量读取文件,使用dest批量写入文件
        // 借助gulp-clean-css插件压缩css
        // 借助gulp-rename插件重命名
        const { src, dest } = require('gulp')
        const cleanCss = require('gulp-clean-css')
        const rename = require('gulp-rename')
        
        exports.minify = () => {
            return src('style.css')
                .pipe(cleanCss())
                .pipe(rename({
                    extname: '.min.css'
                }))
                .pipe(dest('dist'))
        }
        
    • gulp案例

      这里通过一个工程样例来展示,在实际开发过程中,对一个前端工程使用gulp进行自动化构建可能会涉及到的各种编译配置

      例如样式文件sass的编译,脚本ES6+的转换,HTML文件的转换等等

      工程demo见 https://github.com/zce/zce-gulp-demo.git

      • 样式编译

        const { src, dest } = require('gulp');
        const sass = require('gulp-sass');
        
        const style = () => {
            return src('src/assets/styles/*.scss', { base: 'src' })
                .pipe(sass({
                    outputStyle: 'expanded'
                }))
                .pipe(dest('dist/styles'))
        }
        
        module.exports = {
            style,
        }
        
      • 脚本编译

        // gulpfile.js
        const { src, dest } = require('gulp');
        const babel = require('gulp-babel');
        
        const script = () => {
            return src('src/assets/scripts/*.js', { base: 'src' })
                .pipe(babel({
                    presets: ['@babel/preset-env']
                }))
                .pipe(dest('dist'))
        }
        
        module.exports = {
            script,
        }
        
      • 页面模板编译

        // gulpfile.js
        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()
        }
        
        const page = () => {
            return src('src/*.html', { base: 'src' })
                .pipe(plugins.swig({ data, defaults: { cache: false } })) // 关闭缓存防止修改不实时生效
                .pipe(dest('temp'))
        }
        
        module.exports = {
            page,
        }
        
      • 图片和字体文件转换

        // gulpfile.js
        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,
        }
        
      • 其他文件及文件清除

        const del = require('del');
        
        const extra = () => {
            return src('public/**', { base: 'public' })       
                .pipe(dest('dist'))
        }
        
        const clean = () => {
            return del(['dist'])
        }
        
      • 自动加载插件

        const loadPlugins = require('gulp-load-plugins');
        const plugins = loadPlugins();
        // 使用gulp-load-plugins插件自动加载所有已安装的以gulp-开头的插件,并使用plugins.xxxx访问相应的插件
        // 例如使用plugins.sass访问gulp-sass插件
        
      • 开发服务器

        const browserSync = require('browser-sync');
        
        const bs = browserSync.create();
        
        const serve = () => {
            bs.init({
                notify: false,
                files: 'dist/**',
                server: {
                    baseDir: 'dist',
                    routes: {
                        '/node_modules': './node_modules'
                    }
                }
            })
        }
        
      • 监视变化以及构建优化

        • 使用gulp提供的watch API来监控指定文件,并在文件变化时,执行相应的task
        • watch接收两个参数,第一个参数是文件通配符字符串或者通配符字符串数组,表示要监视的文件,第二个参数是文件变化时要执行的task函数
        • 在开发阶段,对于图片及字体的压缩以及静态资源的拷贝意义不大,同时会增加构建任务的开销,将这些文件保留在源文件目录(需指定baseDir)并直接通过watch对这些目录进行监视,在文件发生变化时,执行bs.reload刷新页面
        // gulpfile.js
        const { src, dest, series, parallel, watch } = require('gulp');
        
        const bs = browserSync.create();
        
        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)
        
            bs.init({
                notify: false,
                files: 'dist/**',
                server: {
                    baseDir: ['dist', 'src', 'public'],
                    routes: {
                        '/node_modules': './node_modules'
                    }
                }
            })
        }
        
      • useref文件引用处理

        • 依赖gulp-useref插件,可以将html中依赖的js、css根据注释提取并生成到指定的文件中,并替换依赖的资源文件路径为新生成的文件路径
        const useref = () => {
            return src('dist/*.html', { base: 'dist' })
                .pipe(plugins.useref({
                    searchPath: ['dist', '.']
                }))
                .pipe(dest('dist'))
        }
        
      • 文件压缩

        • 使用相关的gulp插件对相应类型的文件进行压缩处理
          • gulp-uglify 压缩js文件
          • gulp-clean-css 压缩css文件
          • gulp-htmlmin 压缩html文件
        • 为避免文件读写冲突,可以将压缩后的代码放入另外的文件夹,例如release
        const useref = () => {
            return src('dist/*.html', { base: 'dist' })
                .pipe(plugins.useref({
                    searchPath: ['dist', '.']
                }))
                .pipe(plugins.if(/\.js$/, plugins.uglify()))
                .pipe(plugins.if(/\.css$/, plugins.cleanCss()))
                .pipe(plugins.if(/\.html$/, plugins.htmlmin({
                    collapseWhitespace: true,
                    minifyCSS: true,
                    minifyJS: true,
                })))
                .pipe(dest('release'))
        }
        
      • 重新规划构建过程

        • 对上述构建任务进行重新整理
        • style、script、page可以通过parallel组合成并行处理任务compile const compile = parallel(style, script, page),生成的文件由于需要后续进行useref压缩处理,构建过程中临时存放在temp目录,最终压缩后放入dist目录
        • useref需要先进行compile生成临时文件,可以使用series组合成串行处理任务,最终组合成构建任务build const build = series(clean, parallel(series(compile, useref), image, font, extra))
        • 去除不必要exports的任务
        • 将对应的构建任务加入package.json的script中
        • 完整的gulpfile.js如下
         // gulpfile.js
        const { src, dest, series, parallel, watch } = require('gulp');
        const loadPlugins = require('gulp-load-plugins');
        const browserSync = require('browser-sync');
        const del = require('del');
        
        const plugins = loadPlugins();
        
        const bs = browserSync.create();
        
        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()
        }
        
        const style = () => {
            return src('src/assets/styles/*.scss', { base: 'src' })
                .pipe(plugins.sass({
                    outputStyle: 'expanded'
                }))
                .pipe(dest('temp'))
        }
        
        const script = () => {
            return src('src/assets/scripts/*.js', { base: 'src' })
                .pipe(plugins.babel({
                    presets: ['@babel/preset-env']
                }))
                .pipe(dest('temp'))
        }
        
        const page = () => {
            return src('src/**/*.html', { base: 'src' })
                .pipe(plugins.swig({ data }))
                .pipe(dest('temp'))
        }
        
        const image = () => {
            return src('src/assets/images/**', { base: 'src' })
                .pipe(plugins.imagemin())
                .pipe(dest('dist'))
        }
        
        const font = () => {
            return src('src/assets/fonts/**', { base: 'src' })
                .pipe(plugins.imagemin())
                .pipe(dest('dist'))
        }
        
        const extra = () => {
            return src('public/**', { base: 'public' })        
                .pipe(dest('dist'))
        }
        
        const clean = () => {
            return del(['dist', 'temp'])
        }
        
        const compile = parallel(style, script, page)
        
        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)
        
        
            bs.init({
                notify: false,
                files: 'temp/**',
                server: {
                    baseDir: ['temp', 'src', 'public'],
                    routes: {
                        '/node_modules': './node_modules'
                    }
                }
            })
        }
        
        const useref = () => {
            return src('temp/*.html', { base: 'temp' })
                .pipe(plugins.useref({
                    searchPath: ['temp', '.']
                }))
                .pipe(plugins.if(/\.js$/, plugins.uglify()))
                .pipe(plugins.if(/\.css$/, plugins.cleanCss()))
                .pipe(plugins.if(/\.html$/, plugins.htmlmin({
                    collapseWhitespace: true,
                    minifyCSS: true,
                    minifyJS: true,
                })))
                .pipe(dest('dist'))
        }
        
        const build = series(clean, parallel(series(compile, useref), image, font, extra))
        
        const dev = series(compile, serve)
        
        module.exports = {
            clean,
            build,
            dev,
        }
        
    • 封装工作流

      多个项目中复用gulpfile中的task

      • 准备

        • 创建项目托管repository(github/gitee等)
        • 创建项目目录xxx,并初始化 yarn init -y
        • 创建目录结构及文件lib/index.js
      • 提取gulpfile

        • 将原gulpfile.js中的内容复制到lib/index.js文件中
        • 将原package.json中devDependencies中的依赖添加到当前package.json的dependencies中
        • yarn安装相应的依赖
        • yarn link 建立软连接
        • 进入待构建项目目录,执行yarn link xxx,执行相应的命令yarn xxx,测试当前的封装
      • 解决模块中的问题

        • 将原gulpfile.js中使用到的data数据,改为读取配置文件xxx.config.js
        • 对babel使用的插件模块,使用require引入
      • 抽象路径配置

        • 将原gulpfile.js中使用到的项目路径,通过config进行配置,并提供默认值,同时替换所有使用位置为config读取

          // lib/index.js
          const { src, dest, series, parallel, watch } = require('gulp');
          const loadPlugins = require('gulp-load-plugins');
          const browserSync = require('browser-sync');
          const del = require('del');
          
          const plugins = loadPlugins();
          
          const bs = browserSync.create();
          
          const cwd = process.cwd()
          let config = {
              // default config
              build: {
                  src: 'src',
                  temp: 'temp',
                  dist: 'dist',
                  public: 'public',
                  paths: {
                      styles: 'assets/styles/*.scss',
                      scripts: 'assets/scripts/*.js',
                      pages: '*.html',
                      images: 'assets/images/**',
                      fonts: 'assets/fonts/**',
                  }
              }
          }
          
          try {
              const loaded = require(`${cwd}/pages.config.js`)
              config = { ...config, ...loaded }
          } catch(e) {}
           
          
          const style = () => {
              return src(config.build.paths.styles, { base: config.build.src, cwd: config.build.src })
                  .pipe(plugins.sass({
                      outputStyle: 'expanded'
                  }))
                  .pipe(dest(config.build.temp))
          }
          
          const script = () => {
              return src(config.build.paths.scripts, { base: config.build.src, cwd: config.build.src })
                  .pipe(plugins.babel({
                      presets: [require('@babel/preset-env')]
                  }))
                  .pipe(dest(config.build.temp))
          }
          
          const page = () => {
              return src(config.build.paths.pages, { base: config.build.src, cwd: config.build.src })
                  .pipe(plugins.swig({ data: config.data }))
                  .pipe(dest(config.build.temp))
          }
          
          const image = () => {
              return src(config.build.paths.images, { base: config.build.src, cwd: config.build.src })
                  .pipe(plugins.imagemin())
                  .pipe(dest(config.build.dist))
          }
          
          const font = () => {
              return src(config.build.paths.fonts, { base: config.build.src, cwd: config.build.src })
                  .pipe(plugins.imagemin())
                  .pipe(dest(config.build.dist))
          }
          
          const extra = () => {
              return src('**', { base: config.build.public, cwd: config.build.public })        
                  .pipe(dest(config.build.dist))
          }
          
          const clean = () => {
              return del([config.build.dist, config.build.temp])
          }
          
          const compile = parallel(style, script, page)
          
          const serve = () => {
              watch(config.build.paths.styles, { cwd: config.build.src}, style)
              watch(config.build.paths.scripts, { cwd: config.build.src}, script)
              watch(config.build.paths.pages, { cwd: config.build.src}, page)
              // watch('src/assets/images/**', image) 开发阶段意义不大
              // watch('src/assets/fonts/**', font) 开发阶段意义不大
              // watch('public/**', extra) 开发阶段意义不大
              watch([
                  config.build.paths.images,
                  config.build.paths.fonts,        
              ], { cwd: config.build.src }, bs.reload)
          
              watch([
                  '**',        
              ], { cwd: config.build.public }, bs.reload)
          
              bs.init({
                  notify: false,
                  files: `${config.build.temp}/**`,
                  server: {
                      baseDir: [config.build.temp, config.build.src, config.build.public],
                      routes: {
                          '/node_modules': './node_modules'
                      }
                  }
              })
          }
          
          const useref = () => {
              return src(config.build.paths.pages, { base: config.build.temp, cwd: config.build.temp })
                  .pipe(plugins.useref({
                      searchPath: [config.build.paths.temp, '.']
                  }))
                  .pipe(plugins.if(/\.js$/, plugins.uglify()))
                  .pipe(plugins.if(/\.css$/, plugins.cleanCss()))
                  .pipe(plugins.if(/\.html$/, plugins.htmlmin({
                      collapseWhitespace: true,
                      minifyCSS: true,
                      minifyJS: true,
                  })))
                  .pipe(dest(config.build.dist))
          }
          
          const build = series(clean, parallel(series(compile, useref), image, font, extra))
          
          const dev = series(compile, serve)
          
          module.exports = {
              clean,
              build,
              dev,
          }
          
      • 包装Gulp CLI

        • 项目目录下新建bin/xxx.js

        • package.js中增加bin选项配置对应的文件

        • bin/xxx.js中,读取process.argv命名行参数数组,并添加gulp执行相关参数

        • require('gulp/bin/gulp') 执行gulp命令

          #!/usr/bin/env node
          
          process.argv = process.argv.concat(['--cwd', process.cwd(), '--gulpfile', require.resolve('..')])
          
          require('gulp/bin/gulp')
          
      • 发布并使用模块

        • 执行yarn publish --registry=https://registry.yarnpkg.com发布封装的工作流模块到npm
        • 在待使用的项目中,执行yarn add xxx --dev添加相应的依赖
        • xxx build 进行使用

    FIS

    由百度前端团队推出的自动化构建工具,内置了很多常用的任务以及devServer,不需要开者自己去配置

    • 基本使用
      • yarn add fis3 --dev 安装fis3模块
      • fis3 release 编译,使用-d参数可以指定输出目录 fis3 release -d output
      • 添加配置文件fis-config.js,通过配置文件可以对fis3编译进行配置
    • 详细用法

    相关文章

      网友评论

          本文标题:学习笔记(八)——自动化构建

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