自动化构建

自动化构建就是用机器代替手工,把开发的源代码转换为生产环境可运行的代码。这个流程叫自动化工作流。让我们可以脱离运行环境的兼容问题,让我们在开发阶段可以使用一些提高效率的语法或者规范等。

使用sass

创建一个scss文件

//scss/main.scss
$body-bg: #000;
$body-color: #f00;

body {
  margin: 0 auto;
  padding: 20px;
  max-width: 800px;
  background-color: $body-bg;
  color: $body-color;
}

yarn add sass --dev
通过路径找到命令
,\node_modules.bin\sass
指定sass输入路径及css输出路径
,\node_modules.bin\sass scss/main.scss css/style.css
执行后将scss转换为css 文件

但是每次都要重复输入复杂命令,他人也不知如何去构建任务
NPM Scripts用来解决这个问题,用它包装构建命令
在package.json中添加script字段

"scripts": {
    "build": "sass scss/main.scss css/style.css
  },

会自动找到node_modules下的bin目录中的命令 后面跟上输入输出路径

npm run build
yarn build
NPM Scripts是实现自动化构建工作流最简单的方式
yarn add browser-sync --dev
运行一个测试服务器 运行我们的项目

"scripts": {
    "build": "sass scss/main.scss css/style.css,
    "serve": "browser-sync . 
  },

运行yarn serve就会自动启动web服务器 打开浏览器

//在serve前先执行build命令
"scripts": {
    "build": "sass scss/main.scss css/style.css,
    "preserve":"yarn build",
    "serve": "browser-sync . 
  },

//监听scss文件变化 自动编译 --watch
"scripts": {
    "build": "sass scss/main.scss css/style.css --watch,
    "preserve":"yarn build",
    "serve": "browser-sync . 
  },

此时监听会阻塞serve


图片.png

安装执行所有模块npm-run-all

yarn add npm-run-all --dev

"scripts": {
    "build": "sass scss/main.scss css/style.css --watch,
    "serve": "browser-sync . ,
    "start":"run-p build serve" //同时执行build,serve命令
  },

yarn start
给browser-sync 添加--file监听文件变化 ,自动更新浏览器

"scripts": {
    "build": "sass scss/main.scss css/style.css --watch",
    "serve": "browser-sync . --files \"css/*.css\"",
    "start": "run-p build serve"
  },

NPM Scripts只适用于简单的构建 对于复杂的构建需要使用一些自动化构建工具
常用的构建工具

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

Grunt基本使用

mkdir grunt-test
cd grunt-test
yarn init --yes
yarn add grunt
code gruntfile.js //创建gruntfile.js是grunt的入口文件
图片.png
// Grunt 的入口文件 gruntfile.js
// 用于定义一些需要 Grunt 自动执行的任务
// 需要导出一个函数
// 此函数接收一个 grunt 的对象类型的形参
// grunt 对象中提供一些创建任务时会用到的 API
module.exports = grunt => {
  //grunt.registerTask注册一个任务 1.任务名 2.任务函数
  grunt.registerTask('test', () => {
    console.log('hello grunt')
  })

  //第二个参数字符串为任务帮助信息,grunt --help查看
  grunt.registerTask('help', '帮助信息', () => {
    console.log('hello grunt')
  })

  //默认任务 直接执行yarn grunt
  grunt.registerTask('default', '帮助信息', () => {
    console.log('grunt default task')
  })

  //默认任务 为数组就会依次执行数组中的任务
  grunt.registerTask('default', ['help', 'test'])

  //异步任务
  // grunt.registerTask('async-task',()=>{
  //   setTimeout(() => {
  //     console.log('async'); //并不会执行  grunt默认只支持同步任务
  //   }, 1000);
  // })
  // 由于函数体中需要使用 this,所以这里不能使用箭头函数
  grunt.registerTask('async-task', function () {
    const done = this.async()
    setTimeout(() => {
      console.log('async task working~')
      done()//标识任务完成  done执行 才会结束异步执行
    }, 1000)
  })
}

Grunt标记任务失败
在函数体中return false来实现

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

如果是在一个任务列表中 会导致后面任务无法执行

yarn grunt default --force
会强制执行所有任务 不会中断后面任务

异步任务标记失败

  grunt.registerTask('async-task', function () {
    const done = this.async() //要使用this.async函数
    setTimeout(() => {
      console.log('async task working~')
      done(false)//标识失败 传false实参
    }, 1000)
  })

Grunt 配置选项方法
压缩文件配置压缩路径

module.exports = grunt => {
  //键名一般与任务名保持一致
  grunt.initConfig({
    foo:'bar'
  })

  grunt.registerTask('foo',()=>{
    grunt.config('foo')//获取配置方法
  })
}

Grunt 多目标任务

module.exports = grunt => {

  grunt.initConfig({
    build:{
      css:'1',
      js:'2'
    }
  })

  //多目标模式,可以让任务根据配置形成多个子任务
   grunt.registerMultiTask('build',function() {
    console.log('multiTask')
  })
}

yarn grunt build
就会运行配置下的多个目标任务
yarn grunt build:css
运行指定目标任务

module.exports = grunt => {

  grunt.initConfig({
    //build中指定的每个属性的键都会成为一个目标,除了options
    //options中指定的信息会作为任务配置选项出现
    build:{
      // css:'1',
      css:{
        options:{
          foo:'sss'
        }
      },//目标对象中存在options则会覆盖外部options  this.options值为{foo:sss}
      js:'2',
      options:{
        foo:'bar'
      }
    }
  })

  //多目标模式,可以让任务根据配置形成多个子任务
  grunt.registerMultiTask('build',function() {
    `target:${this.target} data:${this.data}`
    //this.target拿到当前target this.data拿到当前值
    this.options() //可以拿到当前任务的所有任务配置选项对象
  })
}

Grunt 插件使用

  • npm 安装相关插件
  • 在gruntfiles中grunt.loadNpmTasks加载进来
  • 配置相关任务
module.exports = grunt => {
  grunt.initConfig({
    clean: {
      temp: 'temp/**' //配置相关配置名  匹配temp目录下所有文件
    }
  })
  
  grunt.loadNpmTasks('grunt-contrib-clean')
}

yarn grunt clean
删除temp目录下所有文件

Grunt sass

yarn add grunt-sass sass --dev

const sass = require('sass')
module.exports = grunt => {

  grunt.initConfig({
    sass:{
      options:{
        sourceMap:true,
        //指定在grunt-sass中指定哪一个模块对sass进行编译
        implementation:sass
      },
      main:{
        //files  key:输出路径 val:输入路径
        files:{
          'dist/css/main.css':'src/scss/main.scss'
        }
      }
    }
  })

  grunt.loadNpmTasks('grunt-sass') //载入模块

}

Grunt babel
安装插件及插件所需核心模块 bable核心模块及babel预设

yarn add grunt-babel @babel/core @babel/preset-env --dev
grunt.loadNpmTasks() 每次引入一个新的插件都要通过loadNpmTasks载入模块 此时可以通过安装load-grunt-tasks 解决这个问题
yarn add load-grunt-tasks --dev

const loadGruntTasks = require('load-grunt-tasks')

module.exports = grunt => {
  grunt.initConfig({
    babel: {
      options: {
        sourceMap: true,
        //转换对应特性 preset-env就是转换最新的es特性
        presets: ['@babel/preset-env']
      },
      main: {
        files: {
          'dist/js/app.js': 'src/js/app.js'
        }
      }
    }
  })

  loadGruntTasks(grunt) // 自动加载所有的 grunt 插件中的任务
}

Grunt watch
在项目中监听文件自动编译

yarn add grunt-contrib-watch --dev

const sass = require('sass')
const loadGruntTasks = require('load-grunt-tasks')

module.exports = grunt => {
  grunt.initConfig({
    sass: {
      options: {
        sourceMap: true,
        implementation: sass
      },
      main: {
        files: {
          'dist/css/main.css': 'src/scss/main.scss'
        }
      }
    },
    babel: {
      options: {
        sourceMap: true,
        presets: ['@babel/preset-env']
      },
      main: {
        files: {
          'dist/js/app.js': 'src/js/app.js'
        }
      }
    },
    watch: {
      js: {
        files: ['src/js/*.js'],
        tasks: ['babel']
      },
      css: {
        files: ['src/scss/*.scss'],
        tasks: ['sass']
      }
    }
  })

  // grunt.loadNpmTasks('grunt-sass')
  loadGruntTasks(grunt) // 自动加载所有的 grunt 插件中的任务

//因为watch在一开始并不会执行任务,只有在文件发生变化时才执行,此时做一个映射  在启动时就执行任务
  grunt.registerTask('default', ['sass', 'babel', 'watch'])
}

yarn grunt (watch)

当文件发生变化时 执行任务

Gulp的使用

mkdir gulp-sample
cd gulp-sample
yarn init
yarn add gulp --dev
创建gulpfile.js

// // 导出的函数都会作为 gulp 任务
// exports.foo = () => {
//   console.log('foo task working~')
// }

// gulp 的任务函数都是异步的
// 可以通过调用回调函数标识任务完成
exports.foo = done => {
  console.log('foo task working~')
  done() // 标识任务执行完成
}

// default 是默认任务
// 在运行是可以省略任务名参数
exports.default = done => {
  console.log('default task working~')
  done()
}

// v4.0 之前需要通过 gulp.task() 方法注册任务
const gulp = require('gulp')

gulp.task('bar', done => {
  console.log('bar task working~')
  done()
})

gulp组合任务

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)

// 让多个任务同时执行
exports.bar = parallel(task1, task2, task3)

Gulp异步任务的三种方式

  1. 回调函数
exports.callback = done => {
  console.log('callback task')
  done()
}
//错误优先,第一个参数传递错误信息可以抛出错误
exports.callback_error = done => {
  console.log('callback task')
  done(new Error('task failed'))
}
  1. promise方式
exports.promise = () => {
  console.log('promise task')
  return Promise.resolve()
}
//失败任务
exports.promise_error = () => {
  console.log('promise task')
  return Promise.reject(new Error('task failed'))
}

const timeout = time => {
  return new Promise(resolve => {
    setTimeout(resolve, time)
  })
}

exports.async = async () => {
  await timeout(1000)
  console.log('async task')
}
  1. stream方式
const fs = require('fs')

exports.stream = () => {
  const read = fs.createReadStream('yarn.lock')
  const write = fs.createWriteStream('a.txt')
  read.pipe(write)
  return read
}
//监听读写文件end事件 结束任务
// exports.stream = done => {
//   const read = fs.createReadStream('yarn.lock')
//   const write = fs.createWriteStream('a.txt')
//   read.pipe(write)
//   read.on('end', () => {
//     done()
//   })
// }

Gulp构建过程
将输入的文件经过加工自动导出处理后的文件
例:压缩代码

const fs = require('fs')

exports.default = () => {
  //文件读取流
  const read = fs.createReadStream('normalize.css')
  //文件写入流
  const write = fs.createWriteStream('normalize.min.css')

  //将读取的文件流导入写入文件流
  read.pipe(write)

  return read
}

执行yarn gulp


图片.png

写入文件,此时文件流是一模一样的 并未进行压缩处理

const fs = require('fs')
const { Transform } = require('stream')

exports.default = () => {
  //文件读取流
  const read = fs.createReadStream('normalize.css')
  //文件写入流
  const write = fs.createWriteStream('normalize.min.css')

  //文件转换流
  const transform = new Transform({
    transform: (chunk, encoding, callback) => {
      //核心转换过程实现
      //chunk读取流中流内容(Buffer)
      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写入流

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

exports.default = () => {
  return src('normalize.css')
    .pipe(dest('dist'))
}
图片.png

安装 gulp-clean-css 插件压缩css代码 提供压缩转换流

yarn add gulp-clean-css --dev
安装gulp-rename插件 可以修改转换后的文件名
yarn add gulp-rename --dev

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'))
}

yarn安装gulp-sass或者node-sass有时会失败 是因为node-sass的包在国外网络限制可能会超时下载失败
解决方法
为sass设置淘宝镜像

yarn config set sass-binary-site http://cdn.npm.taobao.org/dist/node-sass -g
yarn add gulp-sass --dev

Gulp构建案例

项目结构:

图片.png

gulpfile.js

const del = require('del')

const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()
//热更新开发服务器  安装 browser-sync 模块
const broeserSync = require('browser-sync')
const bs = broeserSync.create() //创建一个开发服务器

const { src, dest, parallel, series, watch } = require('gulp')
// const sass = require('gulp-sass')
// const babel = require('gulp-babel')
// const swig = require('gulp-swig')
// const imagemin = require('gulp-imagemin')
const { data } = require('./pages.config')


//样式编译
const style = () => {
  //base将src后面的文件路径保留下来
  return src('src/assets/styles/*.scss', { base: 'src' })
    .pipe(plugins.sass({ outputStyle: 'expanded' }))//生成css样式完全展开
    .pipe(dest('dist'))
}
//.pipe(bs.reload({stream:true})) 
//以流方式推给浏览器 相当于下面bs.init中的files 监听

//脚本编译 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('dist'))
}

//页面模板编译 gulp-swig
const page = () => {
  return src('src/*.html')
    .pipe(plugins.swig({ data: data }))
    .pipe(dest('dist'))
}

//图片转换 gulp-imagemin
const image = () => {
  return src('src/assets/images/**', { base: 'src' })
    .pipe(plugins.imagemin())
    .pipe(dest('dist'))
}

//字体转换 gulp-imagemin
const font = () => {
  return src('src/assets/fonts/**', { base: 'src' })
    .pipe(plugins.imagemin())
    .pipe(dest('dist'))
}

//额外文件如public直接拷贝过去
const extra = () => {
  return src('public/**')
    .pipe(dest('dist'))

}

//安装del 插件移除指定文件  是一个promise方法
const clean = () => {
  return del(['dist'])
}

//服务
const serve = () => {
  //watch监视文件变化,重新构建 参数1:通配符 参数2: 任务
  //源代码编辑->执行相应任务->dist编译文件变化->触发watch->浏览器热更新
  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,//页面提示
    port: 8080,//端口
    open: true,//自动打开页面
    files: 'dist/**',//监听文件的路径 
    server: {
      // baseDir: 'dist',//网页根目录
      baseDir: ['dist', 'src', 'public'],//请求先找dist下文件  找不到继续找src  再找public
      routes: {
        '/node_modules': 'node_modules'//指定引用路径
      }
    }
  })
}

//组合任务
const compile = parallel(style, script, page)

const build = series(clean, parallel(compile, extra, image, font)) //先删除之前的dist 上线之前任务

const dev = series(compile, serve)

//自动加载插件 gulp-load-plugins


module.exports = {
  // style,
  // script,
  // page,
  // image,
  // font,
  clean,
  compile,
  build,
  dev
}

在项目中,当文件有类似引用时 我们无法将依赖的文件其打包到dist文件中

<link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css">
此时可以使用 useref 插件
它会自动处理html中的构建注释 标记引入类型 及引用路径 将之间的文件打包到一个文件中vendor.css
在此过程中还可以进行自动压缩等

  <!-- 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 -->

yarn add gulp-useref --dev

const del = require('del')

const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()
//热更新开发服务器  安装 browser-sync 模块
const broeserSync = require('browser-sync')
const bs = broeserSync.create() //创建一个开发服务器

const { src, dest, parallel, series, watch } = require('gulp')
// const sass = require('gulp-sass')
// const babel = require('gulp-babel')
// const swig = require('gulp-swig')
// const imagemin = require('gulp-imagemin')
const { data } = require('./pages.config')


//样式编译
const style = () => {
  //base将src后面的文件路径保留下来
  return src('src/assets/styles/*.scss', { base: 'src' })
    .pipe(plugins.sass({ outputStyle: 'expanded' }))//生成css样式完全展开
    .pipe(dest('dist'))
}
//.pipe(bs.reload({stream:true})) 
//以流方式推给浏览器 相当于下面bs.init中的files 监听

//脚本编译 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('dist'))
}

//页面模板编译 gulp-swig
const page = () => {
  return src('src/*.html')
    .pipe(plugins.swig({ data: data }))
    .pipe(dest('dist'))
}

//图片转换 gulp-imagemin
const image = () => {
  return src('src/assets/images/**', { base: 'src' })
    .pipe(plugins.imagemin())
    .pipe(dest('dist'))
}

//字体转换 gulp-imagemin
const font = () => {
  return src('src/assets/fonts/**', { base: 'src' })
    .pipe(plugins.imagemin())
    .pipe(dest('dist'))
}

//额外文件如public直接拷贝过去
const extra = () => {
  return src('public/**')
    .pipe(dest('dist'))

}

//安装del 插件移除指定文件  是一个promise方法
const clean = () => {
  return del(['dist'])
}

//服务
const serve = () => {
  //watch监视文件变化,重新构建 参数1:通配符 参数2: 任务
  //源代码编辑->执行相应任务->dist编译文件变化->触发watch->浏览器热更新
  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,//页面提示
    port: 8080,//端口
    open: true,//自动打开页面
    files: 'dist/**',//监听文件的路径 
    server: {
      // baseDir: 'dist',//网页根目录
      baseDir: ['dist', 'src', 'public'],//请求先找dist下文件  找不到继续找src  再找public
      routes: {
        '/node_modules': 'node_modules'//指定引用路径
      }
    }
  })
}

//使用依赖  gulp-useref
const useref = () => {
  return src('dist/*.html', { base: 'dist' })
    .pipe(plugins.useref({ searchPath: ['dist', '.'] })) //查找构建文件目录
    //html js css 分别做压缩操作
    //gulp-htmlmin gulp-uglify gulp-clean-css
    //此时需要判断不同文件进行不同的流操作 gulp-if
    .pipe(plugins.if(/\.css$/, plugins.cleanCss()))
    .pipe(plugins.if(/\.js$/, plugins.uglify()))
    .pipe(plugins.if(/\.html$/, plugins.htmlmin({ collapseWhitespace: true, minifyCSS: true, minifyJS: true })))

    .pipe(dest('release')) //输出文件为dist目录  因为读写操作的是同一个目录 可能会出现写不进去的空文件  
}


//组合任务
const compile = parallel(style, script, page)

const build = series(clean, parallel(compile, extra, image, font)) //先删除之前的dist 上线之前任务

const dev = series(compile, serve)

//自动加载插件 gulp-load-plugins


module.exports = {
  // style,
  // script,
  // page,
  // image,
  // font,
  clean,
  compile,
  build,
  dev
}

因为最后上线打包的文件都应在dist文件中,但是此时因为使用gulp-useref遇到读写问题,我们将构建注释依赖放在了release目录中,
为了解决这个问题 我们可以在任务中将流文件先放入一个临时文件中。temp

const del = require('del')

const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()
//热更新开发服务器  安装 browser-sync 模块
const broeserSync = require('browser-sync')
const bs = broeserSync.create() //创建一个开发服务器

const { src, dest, parallel, series, watch } = require('gulp')
// const sass = require('gulp-sass')
// const babel = require('gulp-babel')
// const swig = require('gulp-swig')
// const imagemin = require('gulp-imagemin')
const { data } = require('./pages.config')


//样式编译
const style = () => {
  //base将src后面的文件路径保留下来
  return src('src/assets/styles/*.scss', { base: 'src' })
    .pipe(plugins.sass({ outputStyle: 'expanded' }))//生成css样式完全展开
    .pipe(dest('temp'))
}
//.pipe(bs.reload({stream:true})) 
//以流方式推给浏览器 相当于下面bs.init中的files 监听

//脚本编译 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'))
}

//页面模板编译 gulp-swig
const page = () => {
  return src('src/*.html')
    .pipe(plugins.swig({ data: data }))
    .pipe(dest('temp'))
}

//图片转换 gulp-imagemin
const image = () => {
  return src('src/assets/images/**', { base: 'src' })
    .pipe(plugins.imagemin())
    .pipe(dest('dist'))
}

//字体转换 gulp-imagemin
const font = () => {
  return src('src/assets/fonts/**', { base: 'src' })
    .pipe(plugins.imagemin())
    .pipe(dest('dist'))
}

//额外文件如public直接拷贝过去
const extra = () => {
  return src('public/**')
    .pipe(dest('dist'))

}

//安装del 插件移除指定文件  是一个promise方法
const clean = () => {
  return del(['dist', 'temp'])
}

//服务
const serve = () => {
  //watch监视文件变化,重新构建 参数1:通配符 参数2: 任务
  //源代码编辑->执行相应任务->dist编译文件变化->触发watch->浏览器热更新
  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,//页面提示
    port: 8080,//端口
    open: true,//自动打开页面
    files: 'dist/**',//监听文件的路径 
    server: {
      // baseDir: 'dist',//网页根目录
      baseDir: ['temp', 'src', 'public'],//请求先找dist下文件  找不到继续找src  再找public
      routes: {
        '/node_modules': 'node_modules'//指定引用路径
      }
    }
  })
}

//使用依赖  gulp-useref
const useref = () => {
  return src('temp/*.html', { base: 'temp' })
    .pipe(plugins.useref({ searchPath: ['temp', '.'] })) //查找构建文件目录
    //html js css 分别做压缩操作
    //gulp-htmlmin gulp-uglify gulp-clean-css
    //此时需要判断不同文件进行不同的流操作 gulp-if
    .pipe(plugins.if(/\.css$/, plugins.cleanCss()))
    .pipe(plugins.if(/\.js$/, plugins.uglify()))
    .pipe(plugins.if(/\.html$/, plugins.htmlmin({ collapseWhitespace: true, minifyCSS: true, minifyJS: true })))

    // .pipe(dest('release')) //输出文件为dist目录  因为读写操作的是同一个目录 可能会出现写不进去的空文件  
    .pipe(dest('dist'))
}


//组合任务
const compile = parallel(style, script, page)

// const build = series(clean, parallel(compile, extra, image, font)) //先删除之前的dist 上线之前任务
// 上线之前执行的任务
const build = series(
  clean,
  parallel(
    series(compile, useref),
    image,
    font,
    extra
  )
)

const dev = series(compile, serve)

//自动加载插件 gulp-load-plugins


module.exports = {
  // style,
  // script,
  // page,
  // image,
  // font,
  clean,
  compile,
  build,
  dev
}

配置package.json

 "scripts": {
    "clean": "gulp clean",
    "build": "gulp build",
    "develop": "gulp develop"
  },

配置gitignore
.gitignore 添加忽略文件dist temp

封装自动化构建工作流

在开发类似项目时,需要重复使用相同的构建任务,复用相同的gulpfile。此时对gulpfile进行一些修改等操作,所有都要手动去修改一遍,不利于我们去操作与整体维护。提取一个可复用的自动化工作流。
先创建一个模块 --> 将模块发布在npm仓库上 --> 在项目中使用模块

npm安装不上问题解决的方法

一.设置 npm 镜像源

# 设置为国内镜像源
npm config set registry http://registry.npm.taobao.org

# 查看当前镜像源
npm config get registry

# 恢复原来镜像源
npm config set registry http://registry.npmjs.org

二.设置 .npmrc 文件

虽然已设置国内镜像源, 有时候 A 包中需要下载 B 包, 这时还可能到国外站点下载 B 包

.npmrc 文件可以提供「变量」设置某些包的下载地址也为国内镜像.

文件位置一般为 C:/Users/Administrator(当前用户名)/.npmrc

把下面常见包地址复制到 .npmrc 中,从而提高下载成功率

sharp_dist_base_url = https://npm.taobao.org/mirrors/sharp-libvips/v8.9.1/
profiler_binary_host_mirror = https://npm.taobao.org/mirrors/node-inspector/
fse_binary_host_mirror = https://npm.taobao.org/mirrors/fsevents
node_sqlite3_binary_host_mirror = https://npm.taobao.org/mirrors
sqlite3_binary_host_mirror = https://npm.taobao.org/mirrors
sqlite3_binary_site = https://npm.taobao.org/mirrors/sqlite3
sass_binary_site = https://npm.taobao.org/mirrors/node-sass
electron_mirror = https://npm.taobao.org/mirrors/electron/
puppeteer_download_host = https://npm.taobao.org/mirrors
chromedriver_cdnurl = https://npm.taobao.org/mirrors/chromedriver
operadriver_cdnurl = https://npm.taobao.org/mirrors/operadriver
phantomjs_cdnurl = https://npm.taobao.org/mirrors/phantomjs
python_mirror = https://npm.taobao.org/mirrors/python
registry = https://registry.npm.taobao.org/
disturl = https://npm.taobao.org/dist

三.设置 hosts 文件

有些包在国内镜像中没有及时更新, 或者根本没有.

国外站点下载不通畅多是因为在「域名 => IP」阶段受阻

我们的解决方案就是提前把「域名与IP的对应关系」准备好,放在本在 hosts 文件中

编辑 C:\Windows\System32\drivers\etc\hosts 文件

# GitHub Start (chinaz.com) =================================================
52.74.223.119 github.com
54.169.195.247 api.github.com
140.82.112.25 live.github.com
59.24.3.173 gist.github.com

185.199.108.154 github.githubassets.com
# 185.199.109.154 github.githubassets.com
# 185.199.110.154 github.githubassets.com
# 185.199.111.154 github.githubassets.com

34.196.246.152 collector.githubapp.com
# 52.206.227.240 collector.githubapp.com
52.216.207.115 github-cloud.s3.amazonaws.com

140.82.112.21 central.github.com

151.101.108.133 raw.githubusercontent.com
151.101.108.133 user-images.githubusercontent.com
151.101.108.133 desktop.githubusercontent.com
199.232.96.133  raw.githubusercontent.com
151.101.76.133  raw.githubusercontent.com
151.101.196.133 raw.githubusercontent.com

151.101.108.133 avatars.githubusercontent.com
151.101.108.133 avatars0.githubusercontent.com
151.101.108.133 avatars1.githubusercontent.com
151.101.108.133 avatars2.githubusercontent.com
151.101.108.133 avatars3.githubusercontent.com
151.101.108.133 avatars4.githubusercontent.com
151.101.108.133 avatars5.githubusercontent.com
151.101.108.133 avatars6.githubusercontent.com
151.101.108.133 avatars7.githubusercontent.com
151.101.108.133 avatars8.githubusercontent.com
151.101.108.133 avatars9.githubusercontent.com
151.101.108.133 avatars10.githubusercontent.com
151.101.108.133 avatars11.githubusercontent.com
151.101.108.133 avatars12.githubusercontent.com
151.101.108.133 avatars13.githubusercontent.com
151.101.108.133 avatars14.githubusercontent.com
151.101.108.133 avatars15.githubusercontent.com
151.101.108.133 avatars16.githubusercontent.com
151.101.108.133 avatars17.githubusercontent.com
151.101.108.133 avatars18.githubusercontent.com
151.101.108.133 avatars19.githubusercontent.com
151.101.108.133 avatars20.githubusercontent.com
# GitHub End ===================================================================

补充:里面内容不是百分百固定, 遇到报错提示 某个域名连接失败, 就到 https://ip138.com/ 查找其相应的 IP 地址, 然后把 IP地址与域名的对应信息追加到上面 hosts 文件中。

四.最后的办法

# 安装 cnpm , 之后使用 cnpm 下载安装包
npm install -g cnpm --registry=https://registry.npm.taobao.org

资料来源:拉勾教育-前端训练营

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容

  • 自动化构建 一切重复的工作都应该被自动化自动化构建是前端工程化过程中一个重要的组成部分自动化构建工作流可以使我们在...
    彪悍de文艺青年阅读 332评论 0 0
  • 简介 自动化构建是前端工程化的一个重要组成部分,将源代码转换为生成代码;这样就可以在开发过程中使用提高效率的语法、...
    翔子丶阅读 402评论 0 0
  • 什么是自动化构建 通过机器代替手工完成一些工作,目的是能使用提高效率的语法,规范和标准。 比如一些es6+,sas...
    洲行阅读 365评论 0 0
  • 前言 关于自动化构建 中途如果遭遇挫折,运行失败,或者打退堂鼓,请直接查看 小结 部分,有我的 最终攻略 除了第一...
    WEB前端含光阅读 605评论 0 0
  • 你是否还在手动压缩图片、js、css?是否还在手动编译sass、less、coffee?是否还在手动合并精灵图?是...
    Jack_Lo阅读 14,154评论 22 95