自動化構(gòu)建

自動化構(gòu)建就是用機器代替手工,把開發(fā)的源代碼轉(zhuǎn)換為生產(chǎn)環(huán)境可運行的代碼。這個流程叫自動化工作流。讓我們可以脫離運行環(huán)境的兼容問題,讓我們在開發(fā)階段可以使用一些提高效率的語法或者規(guī)范等。

使用sass

創(chuàng)建一個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
執(zhí)行后將scss轉(zhuǎn)換為css 文件

但是每次都要重復(fù)輸入復(fù)雜命令,他人也不知如何去構(gòu)建任務(wù)
NPM Scripts用來解決這個問題,用它包裝構(gòu)建命令
在package.json中添加script字段

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

會自動找到node_modules下的bin目錄中的命令 后面跟上輸入輸出路徑

npm run build
yarn build
NPM Scripts是實現(xiàn)自動化構(gòu)建工作流最簡單的方式
yarn add browser-sync --dev
運行一個測試服務(wù)器 運行我們的項目

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

運行yarn serve就會自動啟動web服務(wù)器 打開瀏覽器

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

//監(jiān)聽scss文件變化 自動編譯 --watch
"scripts": {
    "build": "sass scss/main.scss css/style.css --watch,
    "preserve":"yarn build",
    "serve": "browser-sync . 
  },

此時監(jiān)聽會阻塞serve


圖片.png

安裝執(zhí)行所有模塊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" //同時執(zhí)行build,serve命令
  },

yarn start
給browser-sync 添加--file監(jiān)聽文件變化 ,自動更新瀏覽器

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

NPM Scripts只適用于簡單的構(gòu)建 對于復(fù)雜的構(gòu)建需要使用一些自動化構(gòu)建工具
常用的構(gòu)建工具

  • Grunt
    • 插件生態(tài)豐富,可以實現(xiàn)任意類型項目的構(gòu)建
    • 工作過程基于臨時文件,磁盤讀寫頻繁,構(gòu)建速度較慢
  • Gulp
    • 插件生態(tài)豐富
    • 工作過程基于內(nèi)存,構(gòu)建速度較快
    • 支持同時進行多個構(gòu)建任務(wù)
  • FIS
    • 由百度前端團隊推出的構(gòu)建工具
    • 捆綁式全家桶
    • 適合新手
  • (webpack嚴(yán)格來說是一個模塊打包工具)

Grunt基本使用

mkdir grunt-test
cd grunt-test
yarn init --yes
yarn add grunt
code gruntfile.js //創(chuàng)建gruntfile.js是grunt的入口文件
圖片.png
// Grunt 的入口文件 gruntfile.js
// 用于定義一些需要 Grunt 自動執(zhí)行的任務(wù)
// 需要導(dǎo)出一個函數(shù)
// 此函數(shù)接收一個 grunt 的對象類型的形參
// grunt 對象中提供一些創(chuàng)建任務(wù)時會用到的 API
module.exports = grunt => {
  //grunt.registerTask注冊一個任務(wù) 1.任務(wù)名 2.任務(wù)函數(shù)
  grunt.registerTask('test', () => {
    console.log('hello grunt')
  })

  //第二個參數(shù)字符串為任務(wù)幫助信息,grunt --help查看
  grunt.registerTask('help', '幫助信息', () => {
    console.log('hello grunt')
  })

  //默認(rèn)任務(wù) 直接執(zhí)行yarn grunt
  grunt.registerTask('default', '幫助信息', () => {
    console.log('grunt default task')
  })

  //默認(rèn)任務(wù) 為數(shù)組就會依次執(zhí)行數(shù)組中的任務(wù)
  grunt.registerTask('default', ['help', 'test'])

  //異步任務(wù)
  // grunt.registerTask('async-task',()=>{
  //   setTimeout(() => {
  //     console.log('async'); //并不會執(zhí)行  grunt默認(rèn)只支持同步任務(wù)
  //   }, 1000);
  // })
  // 由于函數(shù)體中需要使用 this,所以這里不能使用箭頭函數(shù)
  grunt.registerTask('async-task', function () {
    const done = this.async()
    setTimeout(() => {
      console.log('async task working~')
      done()//標(biāo)識任務(wù)完成  done執(zhí)行 才會結(jié)束異步執(zhí)行
    }, 1000)
  })
}

Grunt標(biāo)記任務(wù)失敗
在函數(shù)體中return false來實現(xiàn)

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

如果是在一個任務(wù)列表中 會導(dǎo)致后面任務(wù)無法執(zhí)行

yarn grunt default --force
會強制執(zhí)行所有任務(wù) 不會中斷后面任務(wù)

異步任務(wù)標(biāo)記失敗

  grunt.registerTask('async-task', function () {
    const done = this.async() //要使用this.async函數(shù)
    setTimeout(() => {
      console.log('async task working~')
      done(false)//標(biāo)識失敗 傳false實參
    }, 1000)
  })

Grunt 配置選項方法
壓縮文件配置壓縮路徑

module.exports = grunt => {
  //鍵名一般與任務(wù)名保持一致
  grunt.initConfig({
    foo:'bar'
  })

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

Grunt 多目標(biāo)任務(wù)

module.exports = grunt => {

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

  //多目標(biāo)模式,可以讓任務(wù)根據(jù)配置形成多個子任務(wù)
   grunt.registerMultiTask('build',function() {
    console.log('multiTask')
  })
}

yarn grunt build
就會運行配置下的多個目標(biāo)任務(wù)
yarn grunt build:css
運行指定目標(biāo)任務(wù)

module.exports = grunt => {

  grunt.initConfig({
    //build中指定的每個屬性的鍵都會成為一個目標(biāo),除了options
    //options中指定的信息會作為任務(wù)配置選項出現(xiàn)
    build:{
      // css:'1',
      css:{
        options:{
          foo:'sss'
        }
      },//目標(biāo)對象中存在options則會覆蓋外部options  this.options值為{foo:sss}
      js:'2',
      options:{
        foo:'bar'
      }
    }
  })

  //多目標(biāo)模式,可以讓任務(wù)根據(jù)配置形成多個子任務(wù)
  grunt.registerMultiTask('build',function() {
    `target:${this.target} data:${this.data}`
    //this.target拿到當(dāng)前target this.data拿到當(dāng)前值
    this.options() //可以拿到當(dāng)前任務(wù)的所有任務(wù)配置選項對象
  })
}

Grunt 插件使用

  • npm 安裝相關(guān)插件
  • 在gruntfiles中g(shù)runt.loadNpmTasks加載進來
  • 配置相關(guān)任務(wù)
module.exports = grunt => {
  grunt.initConfig({
    clean: {
      temp: 'temp/**' //配置相關(guān)配置名  匹配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預(yù)設(shè)

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,
        //轉(zhuǎn)換對應(yīng)特性 preset-env就是轉(zhuǎn)換最新的es特性
        presets: ['@babel/preset-env']
      },
      main: {
        files: {
          'dist/js/app.js': 'src/js/app.js'
        }
      }
    }
  })

  loadGruntTasks(grunt) // 自動加載所有的 grunt 插件中的任務(wù)
}

Grunt watch
在項目中監(jiān)聽文件自動編譯

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 插件中的任務(wù)

//因為watch在一開始并不會執(zhí)行任務(wù),只有在文件發(fā)生變化時才執(zhí)行,此時做一個映射  在啟動時就執(zhí)行任務(wù)
  grunt.registerTask('default', ['sass', 'babel', 'watch'])
}

yarn grunt (watch)

當(dāng)文件發(fā)生變化時 執(zhí)行任務(wù)

Gulp的使用

mkdir gulp-sample
cd gulp-sample
yarn init
yarn add gulp --dev
創(chuàng)建gulpfile.js

// // 導(dǎo)出的函數(shù)都會作為 gulp 任務(wù)
// exports.foo = () => {
//   console.log('foo task working~')
// }

// gulp 的任務(wù)函數(shù)都是異步的
// 可以通過調(diào)用回調(diào)函數(shù)標(biāo)識任務(wù)完成
exports.foo = done => {
  console.log('foo task working~')
  done() // 標(biāo)識任務(wù)執(zhí)行完成
}

// default 是默認(rèn)任務(wù)
// 在運行是可以省略任務(wù)名參數(shù)
exports.default = done => {
  console.log('default task working~')
  done()
}

// v4.0 之前需要通過 gulp.task() 方法注冊任務(wù)
const gulp = require('gulp')

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

gulp組合任務(wù)

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

// 讓多個任務(wù)按照順序依次執(zhí)行
exports.foo = series(task1, task2, task3)

// 讓多個任務(wù)同時執(zhí)行
exports.bar = parallel(task1, task2, task3)

Gulp異步任務(wù)的三種方式

  1. 回調(diào)函數(shù)
exports.callback = done => {
  console.log('callback task')
  done()
}
//錯誤優(yōu)先,第一個參數(shù)傳遞錯誤信息可以拋出錯誤
exports.callback_error = done => {
  console.log('callback task')
  done(new Error('task failed'))
}
  1. promise方式
exports.promise = () => {
  console.log('promise task')
  return Promise.resolve()
}
//失敗任務(wù)
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
}
//監(jiān)聽讀寫文件end事件 結(jié)束任務(wù)
// exports.stream = done => {
//   const read = fs.createReadStream('yarn.lock')
//   const write = fs.createWriteStream('a.txt')
//   read.pipe(write)
//   read.on('end', () => {
//     done()
//   })
// }

Gulp構(gòu)建過程
將輸入的文件經(jīng)過加工自動導(dǎo)出處理后的文件
例:壓縮代碼

const fs = require('fs')

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

  //將讀取的文件流導(dǎo)入寫入文件流
  read.pipe(write)

  return read
}

執(zhí)行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')

  //文件轉(zhuǎn)換流
  const transform = new Transform({
    transform: (chunk, encoding, callback) => {
      //核心轉(zhuǎn)換過程實現(xiàn)
      //chunk讀取流中流內(nèi)容(Buffer)
      const input = chunk.toString()
      const output = input.replace(/\s+/g, '').replace(/\/\*.+?\*\//g, '')//去除空格與注釋
      callback(null, output)//錯誤優(yōu)先函數(shù)  第一個參數(shù)為錯誤對象
    }
  })

  //將讀取的文件流導(dǎo)入寫入文件流
  return read
    .pipe(transform)//先轉(zhuǎn)換
    .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代碼 提供壓縮轉(zhuǎn)換流

yarn add gulp-clean-css --dev
安裝gulp-rename插件 可以修改轉(zhuǎn)換后的文件名
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的包在國外網(wǎng)絡(luò)限制可能會超時下載失敗
解決方法
為sass設(shè)置淘寶鏡像

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

Gulp構(gòu)建案例

項目結(jié)構(gòu):

圖片.png

gulpfile.js

const del = require('del')

const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()
//熱更新開發(fā)服務(wù)器  安裝 browser-sync 模塊
const broeserSync = require('browser-sync')
const bs = broeserSync.create() //創(chuàng)建一個開發(fā)服務(wù)器

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})) 
//以流方式推給瀏覽器 相當(dāng)于下面bs.init中的files 監(jiān)聽

//腳本編譯 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'))
}

//圖片轉(zhuǎn)換 gulp-imagemin
const image = () => {
  return src('src/assets/images/**', { base: 'src' })
    .pipe(plugins.imagemin())
    .pipe(dest('dist'))
}

//字體轉(zhuǎn)換 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'])
}

//服務(wù)
const serve = () => {
  //watch監(jiān)視文件變化,重新構(gòu)建 參數(shù)1:通配符 參數(shù)2: 任務(wù)
  //源代碼編輯->執(zhí)行相應(yīng)任務(wù)->dist編譯文件變化->觸發(fā)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) //文件變化 重新構(gòu)建  瀏覽器重新獲取文件

  bs.init({
    notify: false,//頁面提示
    port: 8080,//端口
    open: true,//自動打開頁面
    files: 'dist/**',//監(jiān)聽文件的路徑 
    server: {
      // baseDir: 'dist',//網(wǎng)頁根目錄
      baseDir: ['dist', 'src', 'public'],//請求先找dist下文件  找不到繼續(xù)找src  再找public
      routes: {
        '/node_modules': 'node_modules'//指定引用路徑
      }
    }
  })
}

//組合任務(wù)
const compile = parallel(style, script, page)

const build = series(clean, parallel(compile, extra, image, font)) //先刪除之前的dist 上線之前任務(wù)

const dev = series(compile, serve)

//自動加載插件 gulp-load-plugins


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

在項目中,當(dāng)文件有類似引用時 我們無法將依賴的文件其打包到dist文件中

<link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css">
此時可以使用 useref 插件
它會自動處理html中的構(gòu)建注釋 標(biāo)記引入類型 及引用路徑 將之間的文件打包到一個文件中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()
//熱更新開發(fā)服務(wù)器  安裝 browser-sync 模塊
const broeserSync = require('browser-sync')
const bs = broeserSync.create() //創(chuàng)建一個開發(fā)服務(wù)器

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})) 
//以流方式推給瀏覽器 相當(dāng)于下面bs.init中的files 監(jiān)聽

//腳本編譯 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'))
}

//圖片轉(zhuǎn)換 gulp-imagemin
const image = () => {
  return src('src/assets/images/**', { base: 'src' })
    .pipe(plugins.imagemin())
    .pipe(dest('dist'))
}

//字體轉(zhuǎn)換 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'])
}

//服務(wù)
const serve = () => {
  //watch監(jiān)視文件變化,重新構(gòu)建 參數(shù)1:通配符 參數(shù)2: 任務(wù)
  //源代碼編輯->執(zhí)行相應(yīng)任務(wù)->dist編譯文件變化->觸發(fā)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) //文件變化 重新構(gòu)建  瀏覽器重新獲取文件

  bs.init({
    notify: false,//頁面提示
    port: 8080,//端口
    open: true,//自動打開頁面
    files: 'dist/**',//監(jiān)聽文件的路徑 
    server: {
      // baseDir: 'dist',//網(wǎng)頁根目錄
      baseDir: ['dist', 'src', 'public'],//請求先找dist下文件  找不到繼續(xù)找src  再找public
      routes: {
        '/node_modules': 'node_modules'//指定引用路徑
      }
    }
  })
}

//使用依賴  gulp-useref
const useref = () => {
  return src('dist/*.html', { base: 'dist' })
    .pipe(plugins.useref({ searchPath: ['dist', '.'] })) //查找構(gòu)建文件目錄
    //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目錄  因為讀寫操作的是同一個目錄 可能會出現(xiàn)寫不進去的空文件  
}


//組合任務(wù)
const compile = parallel(style, script, page)

const build = series(clean, parallel(compile, extra, image, font)) //先刪除之前的dist 上線之前任務(wù)

const dev = series(compile, serve)

//自動加載插件 gulp-load-plugins


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

因為最后上線打包的文件都應(yīng)在dist文件中,但是此時因為使用gulp-useref遇到讀寫問題,我們將構(gòu)建注釋依賴放在了release目錄中,
為了解決這個問題 我們可以在任務(wù)中將流文件先放入一個臨時文件中。temp

const del = require('del')

const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()
//熱更新開發(fā)服務(wù)器  安裝 browser-sync 模塊
const broeserSync = require('browser-sync')
const bs = broeserSync.create() //創(chuàng)建一個開發(fā)服務(wù)器

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})) 
//以流方式推給瀏覽器 相當(dāng)于下面bs.init中的files 監(jiān)聽

//腳本編譯 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'))
}

//圖片轉(zhuǎn)換 gulp-imagemin
const image = () => {
  return src('src/assets/images/**', { base: 'src' })
    .pipe(plugins.imagemin())
    .pipe(dest('dist'))
}

//字體轉(zhuǎn)換 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'])
}

//服務(wù)
const serve = () => {
  //watch監(jiān)視文件變化,重新構(gòu)建 參數(shù)1:通配符 參數(shù)2: 任務(wù)
  //源代碼編輯->執(zhí)行相應(yīng)任務(wù)->dist編譯文件變化->觸發(fā)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) //文件變化 重新構(gòu)建  瀏覽器重新獲取文件

  bs.init({
    notify: false,//頁面提示
    port: 8080,//端口
    open: true,//自動打開頁面
    files: 'dist/**',//監(jiān)聽文件的路徑 
    server: {
      // baseDir: 'dist',//網(wǎng)頁根目錄
      baseDir: ['temp', 'src', 'public'],//請求先找dist下文件  找不到繼續(xù)找src  再找public
      routes: {
        '/node_modules': 'node_modules'//指定引用路徑
      }
    }
  })
}

//使用依賴  gulp-useref
const useref = () => {
  return src('temp/*.html', { base: 'temp' })
    .pipe(plugins.useref({ searchPath: ['temp', '.'] })) //查找構(gòu)建文件目錄
    //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目錄  因為讀寫操作的是同一個目錄 可能會出現(xiàn)寫不進去的空文件  
    .pipe(dest('dist'))
}


//組合任務(wù)
const compile = parallel(style, script, page)

// const build = series(clean, parallel(compile, extra, image, font)) //先刪除之前的dist 上線之前任務(wù)
// 上線之前執(zhí)行的任務(wù)
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

封裝自動化構(gòu)建工作流

在開發(fā)類似項目時,需要重復(fù)使用相同的構(gòu)建任務(wù),復(fù)用相同的gulpfile。此時對gulpfile進行一些修改等操作,所有都要手動去修改一遍,不利于我們?nèi)ゲ僮髋c整體維護。提取一個可復(fù)用的自動化工作流。
先創(chuàng)建一個模塊 --> 將模塊發(fā)布在npm倉庫上 --> 在項目中使用模塊

npm安裝不上問題解決的方法

一.設(shè)置 npm 鏡像源

# 設(shè)置為國內(nèi)鏡像源
npm config set registry http://registry.npm.taobao.org

# 查看當(dāng)前鏡像源
npm config get registry

# 恢復(fù)原來鏡像源
npm config set registry http://registry.npmjs.org

二.設(shè)置 .npmrc 文件

雖然已設(shè)置國內(nèi)鏡像源, 有時候 A 包中需要下載 B 包, 這時還可能到國外站點下載 B 包

.npmrc 文件可以提供「變量」設(shè)置某些包的下載地址也為國內(nèi)鏡像.

文件位置一般為 C:/Users/Administrator(當(dāng)前用戶名)/.npmrc

把下面常見包地址復(fù)制到 .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

三.設(shè)置 hosts 文件

有些包在國內(nèi)鏡像中沒有及時更新, 或者根本沒有.

國外站點下載不通暢多是因為在「域名 => IP」階段受阻

我們的解決方案就是提前把「域名與IP的對應(yīng)關(guān)系」準(zhǔn)備好,放在本在 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 ===================================================================

補充:里面內(nèi)容不是百分百固定, 遇到報錯提示 某個域名連接失敗, 就到 https://ip138.com/ 查找其相應(yīng)的 IP 地址, 然后把 IP地址與域名的對應(yīng)信息追加到上面 hosts 文件中。

四.最后的辦法

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

資料來源:拉勾教育-前端訓(xùn)練營

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容