webpack06----自定義babel-loader、tapable、compiler的hooks使用、compilation的使用、自定義copy-webpack-plugin插件、自定義webpack


自定義babel-loader:

1、babelSchema.json:----提供校驗loader中options的規則:屬性名為presets,它的類型是array,"additionalProperties": true 表示可以追加其他屬性

{
  "type": "object",
  "properties": {
    "presets": {
      "type": "array"
    }
  },
  "additionalProperties": true
}

2、babelLoader.js:

const { getOptions } = require('loader-utils') // getOptions()獲取loader中的options參數----loader-utils庫需要下載
const { validate } = require('schema-utils') // validate()驗證loader中的options是否符合規范
const babel = require('@babel/core')
const util = require('util')

const babelSchema = require('./babelSchema.json') // 校驗options的規則

const transform = util.promisify(babel.transform) // promisify()將普通的異步函數轉換為基於promise的異步方法,babel.transform用來編譯代碼的方法

module.exports = function (content, map, meta) {
  const options = getOptions(this) || {} // 獲取options
  validate(babelSchema, options, { name: 'Babel Loader' }) // 校驗options是否合法
  const callback = this.async() // 創建異步
  transform(content, options) // 使用babel編譯
    .then(({ code, map }) => {
      callback(null, code, map, meta)
    })
    .catch((e) => {
      callback(e)
    })
}

3、webpack.config.js:

const path = require('path')

module.exports = {
  // webpack5默認有entry和output的配置,這里不用寫了
  module: {
    rules: [
      {
        test: /\.js$/,
        // loader: path.resolve(__dirname, 'loaders', 'loader1')
        // loader: 'loader1' // 配置loader解析規則后可以簡寫
        // use: ['loader1', 'loader2', 'loader3'] // 多個loader用use:執行到loader1時,先執行它的pitch方法;執行loader2時,先執行它的pitch方法;執行loader3時,先執行它的pitch方法。執行完pitch后,從下到上,從又到左執行loader。如果某個loader需要提前做一些處理,可以在pitch中處理
        // use: [
        //   'loader1',
        //   'loader2',
        //   {
        //     loader: 'loader3',
        //     options: { name: '孫藝珍', age:12 } // 如果想要追加屬性,schema.json中設置 "additionalProperties": true
        //   }
        // ]
        loader: 'babelLoader',
        options: {
          presets: ['@babel/preset-env'] // presets對應babelSchema.json中定義的屬性名

        }
      }
    ]
  },
  // 配置loader解析規則
  resolveLoader: {
    modules: ['node_modules', path.resolve(__dirname, 'loaders')] // 先去node_modules中找,找不到去loaders目錄中找
  }
}

tapable:

  1、下載插件:npm i tapable -D

  2、tapable.js:

const {
  SyncHook,
  SyncBailHook,
  AsyncParallelHook,
  AsyncSeriesHook
} = require('tapable')

class Lesson {
  constructor() {
    // 初始化hooks容器
    this.hooks = {
      // go: new SyncHook(['address']) // 同步鈎子(hook),任務會依次執行
      go: new SyncBailHook(['address']), // 一旦有返回值就會退出
      // leave: new AsyncParallelHook(['name', 'age']), // 異步並行鈎子:先輸出0618再輸出0518
      leave: new AsyncSeriesHook(['name', 'age']) // 異步串行(同步)鈎子:先輸出0518再輸出0618
    }
  }
  // 往hooks容器中注冊事件,添加回調函數
  tap() {
    this.hooks.go.tap('class0318', (address) => {
      console.log('class0318', address)
      return 100 // SyncBailHook一旦return后有值就會退出
    })
    this.hooks.go.tap('class0418', (address) => {
      console.log('class0418', address)
    })
    this.hooks.leave.tapAsync('class0518', (name, age, cb) => {
      setTimeout(() => {
        console.log('class0518', name, age)
        cb()
      }, 1000)
    })
    this.hooks.leave.tapPromise('class0618', (name, age) => {
      return new Promise((resolve) => {
        setTimeout(() => {
          console.log('class0618', name, age)
          resolve()
        }, 999)
      })
    })
  }
  start() {
    this.hooks.go.call('c318') // 觸發hooks。將hooks容器中的hook都觸發
    this.hooks.leave.callAsync('孫藝珍', 20, () => {
      console.log('end....') // 回調函數 表示所有leave容器中的鈎子都觸發完了才會執行
    })
  }
}

let l = new Lesson()
l.tap()
l.start()

compiler的hooks使用:

  1、新建plugins/Plugin1.js:

class Plugin1 {
  apply(complier) {
    // complier鈎子:生命周期函數從上往下依次執行
    complier.hooks.emit.tap('Plugin1', (compilation) => {
      console.log('emit.tap 111')
    })

    complier.hooks.emit.tapAsync('Plugin1', (compilation, cb) => {
      setTimeout(() => {
        console.log('emit.tapAsync 111')
        cb()
      }, 1000)
    })

    complier.hooks.emit.tapPromise('Plugin1', (compilation) => {
      return new Promise((resolve) => {
        setTimeout(() => {
          console.log('emit.tapPromise 111')
          resolve()
        }, 1000)
      })
    })

    complier.hooks.afterEmit.tap('Plugin1', (compilation) => {
      console.log('afterEmit.tap 111')
    })

    complier.hooks.done.tap('Plugin1', (stats) => {
      console.log('done.tap 111')
    })
  }
}

module.exports = Plugin1

  2、webpack.config.js:

const Plugin1 = require('./plugins/Plugin1')

module.exports = {
  plugins: [new Plugin1()]
}

compilation的使用:

  1、Plugin2.js:

const fs = require('fs') // fs.readFile()讀取文件
const util = require('util') // 利用util.promisify將異步代碼轉為promise異步
const path = require('path') // 處理路徑
const webpack = require('webpack')
const { RawSource } = webpack.sources // 可以創建一個基於webpack風格的文件類型
const readFile = util.promisify(fs.readFile) // 將 fs.readFile 方法轉為基於promise風格的異步方法
class Plugin2 {
  apply(compiler) {
    compiler.hooks.thisCompilation.tap('Plugin2', (compilation) => {
      compilation.hooks.additionalAssets.tapAsync('Plugin2', async (cb) => {
        // additionalAssets 異步串行鈎子
        // debugger
        // return console.log(compilation)
        const content = 'hello plugin2'
        compilation.assets['a.txt'] = {
          // 往要輸出資源中,添加一個a.txt
          size() {
            // 文件大小
            return content.length
          },
          source() {
            // 文件內容
            return content
          }
        }
        const data = await readFile(path.resolve(__dirname, 'b.txt')) // 讀取某個文件(b.txt)打包到dist中
        compilation.assets['b.txt'] = new RawSource(data) // 往要輸出的資源中,添加一個b.txt文件,new RawSource()中傳入數據轉為a.txt創建時的對象結構(不用自己寫那么多函數),創建文件
        // compilation.emitAsset('b.txt', new RawSource(data)) // webpack5
        cb()
      })
    })
  }
}

module.exports = Plugin2

  2、webpack.config.js:

const Plugin2 = require('./plugins/Plugin2')

module.exports = {
  plugins: [new Plugin2()]
}

自定義copy-webpack-plugin插件:

  1、下載插件:npm i globby schema-utils -D

  2、schema.json:

{
  "type": "object",
  "properties": {
    "from": { "type": "string" },
    "to": { "type": "string" },
    "ignore": { "type": "array" }
  },
  "additionalProperties": false
}

  3、CopyWebpackPlugin.js

const path = require('path')
const fs = require('fs')
const { promisify } = require('util')
const { validate } = require('schema-utils') // 校驗插件,需要下載
const globby = require('globby') // 用於匹配文件列表,需要下載
const schema = require('./schema.json')
const { Compilation } = require('webpack')
const webpack = require('webpack')
const readFile = promisify(fs.readFile)
const { RawSource } = webpack.sources
class CopyWebpackPlugin {
  constructor(options = {}) {
    validate(schema, options, {
      name: 'CopyWebpackPlugin'
    })
    this.options = options
  }
  apply(compiler) {
    // 初始化compilation
    compiler.hooks.thisCompilation.tap('CopyWebpackPlugin', (compilation) => {
      // 添加資源的hooks
      compilation.hooks.additionalAssets.tapAsync(
        'CopyWebpackPlugin',
        async (cb) => {
          const { from, ignore } = this.options // 將from中的資源復制到to中,輸出出去
          const to = this.options.to ? this.options.to : '.' // to的默認值是 . 表示當前目錄
          const context = compiler.options.context // context就是webpack配置,運行指令的目錄  context和process.cmd()一樣
          const adsoluteFrom = path.isAbsolute(from)
            ? from
            : path.resolve(context, from) // 將輸入路徑編程絕對路徑
          const paths = await globby(adsoluteFrom, { ignore }) // 1、過濾掉ignore的文件夾    globby(要處理的文件夾,options)
          console.log(paths)
          // 2、讀取paths中的所有資源
          const files = await Promise.all(
            paths.map(async (absolutePath) => {
              const data = await readFile(absolutePath) // 讀取文件
              const relativePath = path.basename(absolutePath) // basename得到最后的文件名稱
              const filename = path.join(to, relativePath) // 和to屬性結合,沒有to----reset.css;有to----css/reset.css
              return {
                data, // 文件數據
                filename // 文件名稱
              }
            })
          )
          // 3、生成webpack格式的資源
          const assets = files.map((file) => {
            const source = new RawSource(file.data)
            return {
              source,
              filename: file.filename
            }
          })
          // 4、添加compilation中,輸出出去
          assets.forEach((asset) => {
            compilation.emitAsset(asset.filename, asset.source)
          })
          cb()
        }
      )
    })
  }
}

module.exports = CopyWebpackPlugin

  4、webpack.config.js

// const Plugin1 = require('./plugins/Plugin1')
// const Plugin2 = require('./plugins/Plugin2')
const path = require('path')
// const CopyWebpackPlugin = require('copy-webpack-plugin')
const CopyWebpackPlugin = require('./plugins/CopyWebpackPlugin')

module.exports = {
  plugins: [
    // new Plugin1()
    // new Plugin2()
    new CopyWebpackPlugin({
      // patterns: [{ from: 'public', to: 'css' }]
      // from: path.resolve(__dirname,'./public'),
      from: './public',
      to: 'css',
      ignore: ['index.html']
    })
  ]
}

自定義webpack:

  D:\desktop\webpack-study\webpack06\04myWebpack

 

 

 

 

 

 

 

 

x


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM