1. Introduction to automated builds

Introduction to the

  • Automate the conversion of our source code into production code.
    • This process of transformation is called the automated build flow
    • Out-of-runtime compatibility issues
    • Use efficient syntax, specifications, and standards in your development environment
      • Such as:
      • ESMAScript Next
      • Sass
      • Template engines (most of these uses are not directly supported by browsers)
    • Use build tools to transform unsupported “features”

For example

  • sass.scss –> css
# download sass
npm i sass
# to perform
./node_modules/.bin/sass sass/main.scss css/style.css

NPM Scripts can be used
"scripts": {
    "build": "sass sass/main.scss css/style.css"
}
Copy the code
  • Using NPM Scripts is the simplest way to automate the build workflow
// browser-sync is used to start a test server to run our project
scrips: {
    "preserve": "npm run build"// This command is automatically executed before serving similar to Pre hook
    "serve": "browser-sync ."
}

// --watch

// To execute multiple commands, use the 'npm-run-all' module
// With this module
scripts: {
    "start": "run-p build"."serve" // Run build and serve at the same time
}

// This can be done with serve
scrips: {
    "serve": "browser-sync . --files \"css/*.css\"" // This will automatically synchronize the changed CSS to the browser
}
Copy the code

Common automated build tools

  • Grunt
  • Gulp
  • FIS

Note: Webpack is a module packaging tool

2. Use of Grunt

1. Basic use of Grunt

  • npm init
  • npm i grunt
  • increasegruntfile.js(Grunt entry file)
// Grunt entry file
// Users define tasks that Grunt automatically performs
// A function needs to be exported
// This function takes a grunt parameter and provides some API that can be used to create tasks

module.exports = grunt= > {
    grunt.registerTask('foo'.() = > {
        console.log('hello grunt! ')
    })

    grunt.registerTask('bar'.'Task Description'.() = > {
        console.log('other task! ')})// YARN grunt executes the default task by default
    // grunt.registerTask('default', () => {
    // console.log('default task')
    // })

    // So you can do this
    grunt.registerTask('default'['foo'.'bar']);

    // Asynchronous operation
    grunt.registerTask('async-task'.function () {
        const done = this.async()
        setTimeout(() = > {
            console.log('async task workding~');
            done();
        }, 1000); })}Copy the code

1.1 Failed to mark the Grunt task

module.exports = grunt= > {
  grunt.registerTask('bad'.() = > {
    console.log('bad workding')
    return false
  })

  grunt.registerTask('foo'.() = > {
    console.log('foo task')
  })

  grunt.registerTask('bar'.() = > {
    console.log('bar task')})// 1. Use YARN grunt default. If bad ends and fails, bar does not run
  // 2. Use yarn grunt default --force
  grunt.registerTask('default'['foo'.'bad'.'bar'])


  / / asynchronous
  grunt.registerTask('bad-async'.function () {
    const done = this.async()
    setTimeout(() = > {
      console.log('bad async')
      done(false)},1000)})}Copy the code

2. Grunt configuration method

module.exports = grunt= > {
  grunt.initConfig({
    // foo: 'bar'
    foo: {
      bar: 123
    }
  })

  grunt.registerTask('foo'.() = > {
    // console.log(grunt.config('foo'))
    console.log(grunt.config('foo.bar'))})}Copy the code

3. Grunt Multi-objective missions

module.exports = grunt= > {
  grunt.initConfig({
    build: {
      // The configured key will be the following this.target value will be this.data note, except options
      options: { // appears as a configuration option for the task
        foo: 'bar'
      },
      // CSS: '1', // can also be objects
      css: {
        options: { // This will replace the configuration in build options
          foo: 'bar-css'}},js: '2'}})// Multi-target mode allows tasks to form multiple sub-tasks according to configuration
  grunt.registerMultiTask('build'.function() {
    console.log(`build task -- target:The ${this.target}, data: The ${this.data}`)})}Copy the code

4. Use of the Grunt plugin

  • For example,grun-contrib-cleanAutomatic cleanup of temporary files that we automatically generated during project development
  • useloadNpmTasksLoad the plug-in
  • performyarn grunt clean
module.exports = grunt= > {
  grunt.initConfig({
    clean: {
      // temp: 'temp/app.js', // app.js in the temp directory
      // Wildcard characters can also be used
      temp: 'temp/*.txt'
    }
  })
  grunt.loadNpmTasks('grunt-contrib-clean')}// Use YARN grunt clean to delete temp/app.js
Copy the code

4.1 Common Grunt plug-ins

  • grunt-sass sass
  • grunt-babel @babel/core @babel/preset-env
  • grunt-contrib-watchMonitoring file changes
const sass = require('sass')
const loadGruntTasks = require('load-grunt-tasks')
module.exports = grunt= > {
  grunt.initConfig({
    sass: {
      // Need to add otions
      options: {
        sourceMap: true.// This can be added according to the error message
        implementation: sass
      },
      main: {
        files: {
          'dist/css/main.css': 'src/scss/main.scss'}}},babel: {
      options: {
        presets: ['@babel/preset-env']},main: {
        files: {
          'dist/js/app.js': 'src/js/app.js'}}},watch: {
      js: {
        files: ['src/js/*.js'].tasks: ['babel'] // Tasks that need to be performed when changes are detected
      },
      css: {
        files: ['src/scss/*.scss'].tasks: ['sass']}}})// grunt.loadNpmTasks('grunt-sass')
  // Too many tasks are loaded, which can be handled by the community load-grunt- Tasks module
  loadGruntTasks(grunt)

  // Yarn grunt watch Does not execute tasks under watch immediately
  // So make a mapping
  grunt.registerTask('default'['sass'.'babel'.'watch'])}Copy the code

3. Gulp

1. Basic use of Gulp

  • The streaming build system can be used to build The streaming system

Core features: high efficiency, easy to use

// Gulp entry lets you speak

//$ yarn gulp foo
exports.foo = done= > {
  console.log('this is foo task working~');
  done() // Indicate that the task is complete
}

//$ yarn gulp
exports.default = done= > {
  console.log('default task working~');
  done()
}
Before 4.0, registering gulp tasks required methods in the gulp module.
// It is not recommended to use it
const gulp = require('gulp')
gulp.task('bar'.done= > {
  console.log('bar working ~');
  done();
})
Copy the code

2. Combined tasks of Gulp

  • seriesCreating a serial task
  • parallelCreating parallel Tasks
const { series, parallel } = require('gulp')
// Unexported tasks are treated as private tasks
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); // Create a serial task
exports.bar = parallel(task1, task2, task3); // Create parallel tasks
Copy the code

3. Gulp asynchronous tasks (three ways)

  • The callback function
  • Promise
  • async await
// 1. Callback function
exports.callback = done= > {
  console.log('callback task~')
  done()
}
// Node's callback function is a standard, which is called error first callback function
exports.callback_error = done= > {
  console.log('callback_error task~')
  done(new Error('task failed:')) // Can block subsequent work execution
}
// 2. Gulp supports promises
exports.promise = () = > {
  console.log('promise task~')
  return Promise.resolve() // resolve does not need an argument because Gulp ignores this value
}
exports.promise_error = () = > {
  console.log('promise_error task~')
  return Promise.reject(new Error('task failed:')) // resolve does not need an argument because Gulp ignores this value
}
// 3. Node environment above 8, mainly supports async await
const timeout = time= > {
  return new Promise(resolve= > {
    setTimeout(resolve, time)
  })
}
exports.async = async() = > {await timeout(1000)
  console.log('async task~');
}

// In addition to the above three types, there are some similar ones in gulp
const fs = require('fs')
/ / file stream
exports.stream = done= > {
  const readStream = fs.createReadStream('package.json')
  const writeStream = fs.createWriteStream('temp.txt')
  readStream.pipe(writeStream)
  // return readStream // as follows
  readStream.on('end'.() = > {
    done()
  })
}
Copy the code

4. Core working principle of Gulp construction process

  • Input – Reads the stream
  • Processing – conversion flow
  • Output – write to the stream

5. Gulp file operation API

const { src, dest } = require('gulp')
const cleanCss = require('gulp-clean-css')
const rename = require('gulp-rename')
exports.default = () = > {
  // return src('src/normalize.css').pipe(dest('dist'))
  return src('src/*.css')
  .pipe(cleanCss())  // Add gulp-rename as an example
  .pipe(rename({extname: '.min.css'}))
  .pipe(dest('dist'))}// gulp-clean-css // Compress the CSS conversion stream

Copy the code

4. Gulp

1. Style, script, page template compilation

1.1 Style Compilation

Reference code github.com/zce/zce-gul…

const { src, dest } = require('gulp')
const sass = require('gulp-sass')

// 1. Style compilation
const style = () = > {
  //, options.base is the base path
  return src('src/assets/styles/*.scss', { base: 'src' })
    //sass can pass parameters
    //.pipe(sass()) // sass is a plug-in for converting streams
    .pipe(sass({ outputStyle: 'expanded' }))
    .pipe(dest('dist'))}module.exports = {
  style
}
Copy the code

1.2. Script compilation

const { src, dest } = require('gulp')
const babel = require('gulp-babel')

// 2. Script compile gulp-babel@babel/core@babel /preset-env
const script = () = > {
  return src('src/assets/scripts/*.js', { base: 'src' })
    .pipe(babel({ presets: ['@babel/preset-env'] }))
    .pipe(dest('dist'))}module.exports = {
  script
}
Copy the code

1.3. Page template compilation

const { src, dest } = require('gulp')
const swig = require('gulp-swig')
// 3. Page templates are compiled using the swig template engine
const data = {} // Fill in the template structure, only the name is replaced here
const page = () = > {
  return src('src/*.html', { base: 'src' })
    .pipe(swig({ data: data }))
    .pipe(dest('dist'))}module.exports = {
  page
}
Copy the code

1.4 Combine the three

Create parallel tasks using PARALLEL

const { src, dest, parallel } = require('gulp')
const sass = require('gulp-sass')
const babel = require('gulp-babel')
const swig = require('gulp-swig')

// 1. Style compilation
const style = () = > {
  //, options.base is the base path
  return src('src/assets/styles/*.scss', { base: 'src' })
    //sass can pass parameters
    //.pipe(sass()) // sass is a plug-in for converting streams
    .pipe(sass({ outputStyle: 'expanded' }))
    .pipe(dest('dist'))}// 2. Script compile gulp-babel@babel/core@babel /preset-env
const script = () = > {
  return src('src/assets/scripts/*.js', { base: 'src' })
    .pipe(babel({ presets: ['@babel/preset-env'] }))
    .pipe(dest('dist'))}// 3. Page templates are compiled using the swig template engine
const data = {} // Fill in the template structure, only the name is replaced here
const page = () = > {
  return src('src/*.html', { base: 'src' })
    .pipe(swig({ data: data }))
    .pipe(dest('dist'))}// Combine the three tasks
const compile = parallel(style, script, page)

module.exports = {
  compile
}
Copy the code

2. Picture and font file conversion

// 4. Use gulp-imagemin to convert images
const image = () = > {
  return src('src/assets/images/**', { base: 'src' })
    .pipe(imagemin())
    .pipe(dest('dist'))}// 5. Fonts for SVG are also images
const font = () = > {
  return src('src/assets/fonts/**', { base: 'src' })
    .pipe(imagemin())
    .pipe(dest('dist'))}Copy the code

3. Other files and file removal

  • Clear installation del
  • yarn add del -D
const { src, dest, parallel, Series} = require('gulp') const del = require('del') const clean = () => {return del(['dist']) Const compile = parallel(style, script, page, image, font) const build = series(clean, parallel(compile, extra))Copy the code

4. Automatically load plug-ins

  • gulp-load-plugins

const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()  // use gulp-sass. If gulp-sass-dev is gulp-sass-dev, it becomes plugins.sassdev

// When used in tasks, the following is an example
// 1. Style compilation
const style = () = > {
  //, options.base is the base path
  return src('src/assets/styles/*.scss', { base: 'src' })
    //sass can pass parameters
    //.pipe(sass()) // sass is a plug-in for converting streams
    // This is replaced by plugins.sass
    // Note that when the plugin is named gulp-sass-xxx, it is plugins.sassxxx
    .pipe(plugins.sass({ outputStyle: 'expanded' }))
    .pipe(dest('dist'))}Copy the code

5. Development server

  • yarn add browser-sync -D
const browserSync = require('browser-sync')
const bs = browserSync.create()

// 7. Create a serve task
const serve = () = > {
  bs.init({
    notify: false.// This is a successful connection message in the upper right corner of the browser
    port: 2080./ / port
    // open: false, // Whether to open the browser by default
    files: 'dist/**'.// Listen on the file below dist, automatically synchronize to the browser after modification
    server: {
      baseDir: 'dist'.routes: {
        '/node_modules': 'node_modules' Have a direct reference node_modules / / dist inside HTML inside the bootstrap/dist/CSS/bootstrap CSS so here to add a map}}})}Copy the code

Monitor changes and build optimizations

  • usegulpIn thewatch
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) // For these three types of files, there is no need to compile them during development, just reload them
  
 [dist", "SRC ", "public"] [dist", "dist", "dist"]
 // if baseDir is an array, dist will be searched first, SRC will be searched if it is not found, and so on

Copy the code
  • Note in bs.init({files: ‘dist/**’})

This file can be removed without listening, and then used in the task via.pipe(bs.reload({stream: true})). Stream: true: in stream mode

// 1. Style compilation
const style = () = > {
  //, options.base is the base path
  return src('src/assets/styles/*.scss', { base: 'src' })
    //sass can pass parameters
    //.pipe(sass()) // sass is a plug-in for converting streams
    .pipe(plugins.sass({ outputStyle: 'expanded' }))
    .pipe(dest('dist'))
    .pipe(bs.reload({ stream: true}}))Copy the code

7. Useref file reference processing

There are reference comments in HTML

  • Since the mapping of node_modules was added before and the CSS file in node_modules was used, the HTML file of the packaged dist has the path referring to node_modules

  • Using useref

  <! -- This is in packing up dist, because it needs to be handled -->
  <! -- build:css assets/styles/vendor.css -->
  <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css">
  <! -- endbuild -->
  <! -- build:css assets/styles/main.css -->
  <link rel="stylesheet" href="assets/styles/main.css">
  <! -- endbuild -->
Copy the code
// Create useref task
const useref = () = > {
  return src('dist/*.html', { base: 'dist' })
    .pipe(plugins.useref( { searchPath: ['dist'.'. '] } ))
    .pipe(dest('dist'))}Copy the code

8. File compression

  • gulp-htmlmin
  • gulp-uglif
  • gulp-clean-css
  • gulp-if
const useref = () = > {
  return src('dist/*.html', { base: 'dist' })
    .pipe(plugins.useref( { searchPath: ['dist'.'. ']}))// There are three types of files here. To compress HTML, JS and CSS, you need to download three compression plug-ins
    .pipe(plugins.if(/\.js$/, plugins.uglify()))
    .pipe(plugins.if(/\.css$/, plugins.cleanCss())) // 
    .pipe(plugins.if(/\.html$/, plugins.htmlmin( 
      {
        collapseWhitespace: true.// Whitespace characters
        minifyCSS: true./ / compress CSS
        minifyJS: true  / / scripts
      }
    )))
    .pipe(dest('temp')) // There is dist package and temp package
}
Copy the code

9. Redesign the build process

  • Generate packages fordistTemporary package calledtempNeed to make some adjustments in the code
/ / for

// 1. Style compilation
const style = () = > {
  //, options.base is the base path
  return src('src/assets/styles/*.scss', { base: 'src' })
    //sass can pass parameters
    //.pipe(sass()) // sass is a plug-in for converting streams
    .pipe(plugins.sass({ outputStyle: 'expanded' }))
    .pipe(dest('temp')) // Change it here
    .pipe(bs.reload({stream: true}}))// 2. Script compile gulp-babel@babel/core@babel /preset-env
const script = () = > {
  return src('src/assets/scripts/*.js', { base: 'src' })
    .pipe(plugins.babel({ presets: ['@babel/preset-env'] }))
    .pipe(dest('temp')) // Change it here
    .pipe(bs.reload({stream: true}}))// And so on
Copy the code

10. Build tasks into modules

  • Let’s take the task to bepackage.jsonIn thescripts
{
    "scripts": {
        "clean": "gulp clean"."build": "gulp build"."develop": "gulp develop"}}Copy the code
// Extract tasks to expose only common ones
module.exports = {
  clean,
  build,
  develop
}
Copy the code

11. Encapsulate workflows

  • Gulpfile + Gulp = Build workflow
  • Gulpfile + Gulp CLI = Workflow module (EG: ZCE-Pages)
  • Convention specific configuration files are similar to vue. Config. js. Here we can have a pages.config.js
  • Use closed workflows for local debuggingnpm linkPut it in the global context