webpack 自定義 plugin ?

plugin 的本質 類 (構造函數)

1 重要概念 tapable 類

const {
SyncHook, // 同步 執行
SyncBailHook, // 同步執行,但是一旦有返回值,就執行退出,不再繼續執行其他
AsyncParallelHook, // 異步 並行執行
AsyncSeriesBailHook, // 異步 串行執行
} = require('tapable');

class Lesson {
constructor() {
// 初始化 hook容器
this.hooks = {
// 同步鉤子,任務會依次執行
// go: new SyncHook(['address']),
go: new SyncBailHook(['address']), // 一旦有返回值,就不會再往下執行了
// 異步hooks
// AsyncParallelHook 異步並行
// leave: new AsyncParallelHook(['name', 'age']),
leave: new AsyncSeriesBailHook(['name', 'age']),
};
}

tap() {
// 往hooks 容器中註冊事件/ 添加回調函數
this.hooks.go.tap('class001', (address) => {
console.log('class001', address);
return 111;
});
this.hooks.go.tap('class002', (address) => {
console.log('class002', address);
});

this.hooks.leave.tapAsync('class003', (name, age, cb) => {
  setTimeout(() => {
    console.log('class003', name, age);
    cb();
  }, 2000);
});
this.hooks.leave.tapPromise('class004', (name, age) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log('class004', name, age);
      resolve();
    }, 1000);
  });
});

}

start() {
this.hooks.go.call('c318');
this.hooks.leave.callAsync('zh', '18', () => {
// 代表所有leave容器中的函數觸發完了,才觸發
console.log('end');
});
}
}
const l = new Lesson();
l.tap();
l.start();



##### 2 complier 類
擁有各種鉤子函數
```js
class Plugin1 {
  apply(complier) {
    complier.hooks.emit.tap('Plugin1', () => {
      console.log('emit 111111111111');
    });

    complier.hooks.afterEmit.tap('Plugin1', () => {
      console.log('emit after 111111111111');
    });
  }
}
module.exports = Plugin1;

3 compilation 類 (通過compiler.hooks.thisCompilation 獲取),可對文件進行操作
const fs = require('fs');
const util = require('util');
const path = require('path');
const { RawSource } = require('webpack-sources');
// 將fs . readFile 的異步方法,改變成同步的寫法
const readFile = util.promisify(fs.readFile);
class Plugin2 {
  apply(compiler) {
    // 初始化compilation 的鉤子
    compiler.hooks.thisCompilation.tap('Plugin2', (compilation) => {
      // debugger;
      // console.log(compilation);
      compilation.hooks.additionalAssets.tapAsync('Plugin2', async (cb) => {
        const content = 'debug';
        // 往要輸出資源中,添加一個a.txt
        compilation.assets['a.txt'] = {
          size() {
            return content.length;
          }, // 文件大小
          source() {
            return content;
          }, // 文件內容
        };
        const data = await readFile(path.resolve(__dirname, '../b.txt'));
        compilation.assets['b.txt'] = new RawSource(data);
        compilation.emitAsset('c.txt', new RawSource(data));
        cb();
      });
    });
  }
}
module.exports = Plugin2;

自定義插件 - CopyWebpackPlugin

const path = require('path');
const fs = require('fs');
const {promisify} = require('util')

const { validate } = require('schema-utils');
const globby = require('globby');
const webpack = require('webpack');

const schema = require('./schema.json');
const { Compilation } = require('webpack');

const readFile = promisify(fs.readFile);
const {RawSource} = webpack.sources

class CopyWebpackPlugin {
  constructor(options = {}) {
    // 驗證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) => {
        // 將from中的資源複製到to中,輸出出去
        const { from, ignore } = this.options;
        const to = this.options.to ? this.options.to : '.';
        
        // context就是webpack配置
        // 運行指令的目錄
        const context = compiler.options.context; // process.cwd()
        // 將輸入路徑變成絕對路徑
        const absoluteFrom = path.isAbsolute(from) ? from : path.resolve(context, from);

        // 1. 過濾掉ignore的文件
        // globby(要處理的文件夾,options)
        const paths = await globby(absoluteFrom, { ignore });

        console.log(paths); // 所有要加載的文件路徑數組

        // 2. 讀取paths中所有資源
        const files = await Promise.all(
          paths.map(async (absolutePath) => {
            // 讀取文件
            const data = await readFile(absolutePath);
            // basename得到最後的文件名稱
            const relativePath = path.basename(absolutePath);
            // 和to屬性結合
            // 沒有to --> reset.css
            // 有to --> css/reset.css
            const filename = path.join(to, relativePath);

            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;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章