webpack 简单插件开发

2,624 阅读4分钟

webpack 简单插件开发

本文主要内容

  • 对 webpack 插件的认识
  • 常用 api 的介绍
  • demo 讲解和 github demo 地址

对插件的认识

官方解释:插件是 webpack 的支柱功能。webpack 自身也是构建于,你在 webpack 配置中用到的相同的插件系统之上!插件目的在于解决 loader 无法实现的其他事。

webpack 的插件有很多,有内置插件当然还有社区提供的插件,当然你自己也可以开发一个插件。社区的插件我们需要使用 npm 先进行安装,然后再引入使用。内置的插件在 webpack.optimize 对象上,我们可以直接使用。常见的内置插件

准备工作

文档

首先需要看下官方教程和api官方教程

了解插件的基本组成

插件开发组成
  • 一个 JavaScript 命名函数。
  • 在插件函数的 prototype 上定义一个 apply 方法。
  • 指定一个绑定到 webpack 自身的事件钩子。
  • 处理 webpack 内部实例的特定数据。
  • 功能完成后调用 webpack 提供的回调。

伪代码如下所示:

class MyWebpackPlugin {
    constructor (options) {
        this.options = options
    }

    apply (compiler) {
        
    }
}

module.exports = MyWebpackPlugin

常用 api

前面讲了 webpack 编写插件时提供了大量的 API, 好多是我们不经常用的,下面总结了一些常用的 API, 这些 API 主要存储在 compilation 对象中

常用对象 提供的钩子函数
Compiler run,compile,compilation,
make,emit,done
Compilation buildModule,normalModuleLoader,
succeedModule,finishModules,seal,
optimize,after-seal
Module Factory beforeResolver,afterResolver,
module,parser
Parser program,statement,call,expression
Template hash,bootstrap,localVars,render
  • compilation.modules:编译后的(内置输入的)模块数组。每个模块管理控制来源代码库 (source library) 中的原始文件 (raw file) 的构建。 module.fileDependencies:模块中引入的源文件路径构成的数组。这包括源 JavaScript 文件本身(例如:index.js)以及它所需的所有依赖资源文件(样式表、图像等)。审查依赖,可以用于查看一个模块有哪些从属的源文件。

  • compilation.chunks:编译后的(构建输出的)chunk 数组。每个 chunk 所管理控制的最终渲染资源的组合,在 chunk 中还有以下属性:

    • chunk.modules:chunk 中引入的模块构成的数组。通过扩展 (extension) 可以审查每个模块的依赖,来查看哪些原始源文件被注入到 chunk 中。
    • chunk.files:chunk 生成的输出文件名构成的数组。你可以从 compilation.assets 表中访问这些资源来源。
  • compilation.assets 包含所有模块的对象,我们可以通过他来获取某个文件信息和内容,也可也可以修改获取的文件,并且也可以向该对象中添加文件(或文件夹)

    • assets[filename].source() 读取输出资源的内容
    • assets[filename].size() 读取输出资源的文件大小
  • compiler.options.plugins 获取配置中使用的插件列表

  • watch.compiler.watchFileSystem.watcher.mtimes 获取发生变化的文件列表

// 监听文件变化事件
compiler.plugin('watch-run',(watch,callback)=>{
    // 获取发生变化的文件列表(数组)
   let  filesChange = watch.compiler.watchFileSystem.watcher.mtimes;
   callback();
})

compiler.plugin('after-compile', (compilation, callback) => {
    // 把要观察的文件路径filePath 添加到webpack观察数组中
    let filesAry = [...compilation.fileDependencies]
    filesAry.push(filePath);
    compilation.fileDependencies = filesAry;
    callback();
});

实践编写一个简单插件插件

首先第一步,使用 compiler 的 emit 钩子

emit 事件是将编译好的代码发射到指定的 stream 中触发,在这个钩子执行的时候,我们能从回调函数返回的 compilation 对象上拿到编译好的 stream。

compiler.hooks.emit.tap('aaa',(compilation)=>{})
第二步, 遍历 compilation.assets 对象,然后调用Object.source () 得到内容

我们需要compilation.assets 通过遍历它,我们可以得到bound.js

准备好一个去掉注释的正则

 const reg = /("([^\\\"]*(\\.)?)*")|('([^\\\']*(\\.)?)*')|(\/{2,}.*?(\r|\n))|(\/\*(\n|.)*?\*\/)|(\/\*\*\*\*\*\*\/)/g
第三步,更新 compilation.assets [data] 对象
compilation.assets[data] = {
    source(){
        return content
    },
    size(){
        return content.length
    }
}

完整代码如下
const DEFAULT_OPTIONS = {
    name: 'test'
}
class MyWebpackPlugin {
    constructor(options = {}) {
        this.options = Object.assign({}, DEFAULT_OPTIONS, options)
    }

    apply(compiler) {
        // console.log(JSON.stringify(compiler, null, 4))
        compiler.plugin('compile', (params) => {
            console.log('=====开始编译=====')
        })

        compiler.plugin("compilation", (compilation) => {
            console.log("=====正在编译ing=====")

        })

        const reg = /("([^\\\"]*(\\.)?)*")|('([^\\\']*(\\.)?)*')|(\/{2,}.*?(\r|\n))|(\/\*(\n|.)*?\*\/)|(\/\*\*\*\*\*\*\/)/g
        compiler.hooks.emit.tap('MyWebpackPlugin', (compilation) => {
            Object.keys(compilation.assets).forEach((data) => {
                let content = compilation.assets[data].source()
                content = content.replace(reg, (word) => {
                    return /^\/{2,}/.test(word) || /^\/\*!/.test(word) || /^\/\*{3,}\//.test(word) ? "" : word;
                });
                compilation.assets[data] = {
                    source() {
                        return content
                    },
                    size() {
                        return content.length
                    }
                }
            })
        })
        compiler.hooks.done.tapAsync('DonePluginv', (name, callback) => {
            console.log(' 全部编译完成')
            callback()
        })
    }
}

module.exports = MyWebpackPlugin
最后 在 webpack 中引用插件
const path = require('path');
const MyWebpackPlugin = require('./webpack-plugin/index')
const MyAddOptionPlugin = require('./webpack-plugin/add-name')

module.exports = {
  mode: 'development',
  entry: './index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" }
    ]
  },
  plugins: [
    new MyWebpackPlugin(),
    
    new MyAddOptionPlugin({ name: 'chant-lee' })
  ]
};

插件地址