自定義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