webpack pluign作用-带你一步步开发webpack5.x-plugin(保姆教程)

2023-09-03 0 7,786 百度已收录

小知识,大挑战! 本文是参与《程序员必备知识》创作活动。

前言

春节到了,祝伟大的祖国春节快乐! ! !

Webpack 5.x 已经更新很长时间了。 与4.x版本相比,有很多变化。 许多API已被放弃,并且添加了其他一些特别有用的方法。 本文主要关注webpack 5.x版本插件的开发。 详细分析变化,保姆级别,对新人更友好。 如果你从未自己开发过 webpack-plugin,或者只写过 webpack4.x 插件,那么阅读它是合适的。 另外,如果你对如何开发webpack-loader感兴趣,可以阅读我之前包看宝慧的文章《如何编写webpack-loader》。

plugin1.webpack打包的基础知识有哪些

作者决心分享这篇文章,让刚接触后端的新手能够清楚地理解,所以我们将从最基本的开始,解释一下什么是插件。 我们通过一个demo来讲解,创建一个write-plugin文件夹,npminit -y后,执行npmiwebpackwebpack-cli创建如图所示的目录结构:

在index.js中编写一些代码

// src > index.js
function add(a, b) {
  return a + b
}
add(1, 2)
console.log('hello world 123');

我希望这个js文件能够打包供index.html使用,所以现在就做webpack配置

// webpack.config.js
const path = require('path')
module.exports = {
  mode: 'development',
  entry: {
    main: path.resolve(__dirname, './src/index.js')
  },
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, './dist')
  }
}

在package.json中配置一个build命令,方便我们运行package

"scripts": {
    "test": "echo "Error: no test specified" && exit 1",
    "build": "webpack --config webpack.config.js"
  },

完成此操作后,执行npmrunbuild。 您将看到根目录下的 dist 文件夹中打包了一个 main.js 文件。 这就是我们的 src>index.js 打包后的样子。 完成了这个基本操作我们再来说说插件

2. 插件的应用

听说现在打包好的main.js需要引入到html文件中才能生效,所以我们将其引入到根目录下的index.html中,并且因为CDN网络分发的内容会保留在服务器上是缓存,所以我们每次更新代码时都会打包一个带有哈希值的文件名(一般情况)并打包,比如更改webpack.config.js中的代码

// webpack.config.js
(...省略部分代码)
output: {
  filename: 'main.[hash:8].js',  // +++带上hash值
  path: path.resolve(__dirname, './dist')
},

再次运行npmrunbuild,你会看到打包的文件名发生了变化:

那么第一个问题就来了。 每次我们更新和更改代码时,都会打包一个新的文件名。 我们需要自动改变html中引入的js地址。 这也太麻烦了吧! 如果有什么办法可以直接导入我手动打包成HTML的文件就好了。 其实这种方法很常见。 为项目安装 html-webpack-plugin 并添加 webpack 配置:

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin') // +++ 引入插件
module.exports = {
  (...省略部分代码)
  
  plugins: [
    // 使用插件
    new HtmlWebpackPlugin({
      // 指明该插件需要处理的html模版
      template: path.resolve(__dirname, './index.html') 
    })
  ]
}

到这里打包后webpack pluign作用,你会听到dist目录下出现了index.html文件和最新的main.js文件。 打开index.html

呃!这就解决了我们刚才担心的问题,不再需要自动引入了。

不过,这里我们遇到了另一个问题,就是每个包后面都会出现一个新的main.js,而dist目录中仍然保留着最后一个包。 难道不是说,如果我把代码改一百次,最后还会多改九十九次吗? 残留文件的意义,就像是这个问题的解决方案。

虽然这个问题已经有很优雅的解决方案了,安装 clean-webpack-plugin 插件并使用

// webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')  // +++ 引入插件
module.exports = {
 (...省略部分代码)
 
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, './index.html')
    }),
    new CleanWebpackPlugin() // 使用插件
  ]
}

再次执行npmrunbuild,就没有剩余文件了。 以上是最基本的用途。 现在你了解了插件在 webpack 中扮演的角色了。

webpack 生命周期

听完上面的疗效,现在我们一起思考一个问题,我们使用的两个插件webpack pluign作用,html-webpack-plugin(帮助我们引入js)clean-webpack-plugin(删除最后一个打包的文件),这个哪个两个插件起作用了,谁起作用了?思考三秒,你应该就有答案了。 clean-webpack-plugin 首先起作用。 删除所有文件后,html-webpack-plugin会生成一个新的打包文件。 如果顺序颠倒,打包的文件将立即被删除。 删除

从这里我们可以看出,webpack在打包过程中是有时间轴的概念的。确实,webpack官方提供了28个生命周期钩子。 点击该文档,您将看到编译器模块。 我们需要仔细地理解它。

1.阅读并理解一段中的编译器

Compiler模块是webpack的主要引擎。 它创建一个编译实例,其中所有选项都通过 CLI 或 NodeAPI 传递(稍后会详细介绍)。 它扩展(extends)自Tapable类,用于注册和调用插件。 大多数面向用户的插件将首先向编译器注册。

webpack的所有生命周期钩子(钩子函数)都是由编译器暴露出来的。我们可以把编译器看成是webpack提供的一个实例化上下文对象

列出几个常见的编译器钩子:

EntryOption:webpack读取entry(入口文件)后调用

afterPlugins:设置并初始化内部插件后

编译:创建编译时,生成文件之前

done:编译完成后

发出:在生成资源到输出目录之前

2. 阅读并理解段落中的汇编

编译器将使用编译模块来创建新的编译对象(或新的构建对象)。 编译实例可以访问所有模块及其依赖项(主要是循环依赖项)。 它对应用程序依赖图中的所有模块执行字面编译。 在编译阶段,模块被加载、密封、优化、分块、散列和恢复。

也就是说webpack提供了Compilation模块。 上下文对象编译器可以调用此模块来创建新对象。 这个对象还可以访问webpack的所有模块,并且这个对象会编译代码中需要的依赖。而且Compilation类继承自Tapable,还提供了一系列的生命周期钩子。

列出几个常见的编译钩子:

buildModule:在模块构建之前触发,可用于更改模块

rebuildModule:在重建模块之前触发

successModule:模块创建成功时执行

failedModule:模块构建失败时执行

了解这两个模块的功能可以为插件开发奠定基础。

如何开发一个plugin1.plugin原理

在开发自己的插件之前,我们先来看看官网吧! 官方网站! 网! 官网告诉我们几点:

1.plugin是一个命名函数

2、会在webpack的某个生命周期内执行

3.它的原型上有一个apply方法(想想vue插件中的install函数,你就会明白很多)

2. 制定要求

有了这样的基础作为基础,我们就可以开始尝试开发一个插件了,首先制定一个需求,我们希望webpack每次打包的文件名可以不同,以避免缓存的影响,所以我们选择打包在文件名前面添加hash值。 如果我们不添加哈希值怎么办? 有没有其他方法可以让html引用的文件名每次都不一样?

试想一下:如果我们能在打包的html中这样做的话,我们也能保证每次请求的js资源都不一样,这样就不会命中缓存了! 那么打包的时候就不需要带hash了。

应当指出的是:

拼接时间戳应该在html文件生成之前完成。

我们的插件应该在 html-webpack-plugin 将脚本嵌入到 index.html 文件之前执行

3. 开发插件

既然有了想法,就不要只停留在思考阶段。 我们需要一个如上所述的插件,并在根目录中创建一个 stamp-webpack-plugin.js 文件。 重要的解释在代码注释中:

// 根目录 > stamp-webpack-plugin.js
class StampPlugin { // 创建一个时间戳插件
  apply(compiler) {
    // 使用 compilation 生命周期保证 plugin 执行在 文件创建之前
    // compiler 创建的 compilation 对象在回调中被使用
    compiler.hooks.compilation.tap('StampWebpackPlugin', (compilation, callback) => { // 注册一个StampWebpackPlugin方法
      // 测试compilation对象在模块构建之前能得到什么
      // 将逻辑注册在同一个 StampWebpackPlugin 方法上
      compilation.hooks.buildModule.tap('StampWebpackPlugin',
        (data, cb) => {
          console.log(data);
        })
    })
  }
}
module.exports = StampPlugin

webpack 提供了三种触发 hooks 的方式:

tap:同步触发hook

tapAsync:异步触发钩子

tapPromise:在异步方法中触发钩子并返回Promise

我们使用自己的StampPlugin插件,目前完整的webpack配置文件:

// webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const StampWebpackPlugin = require('./stamp-webpack-plugin') // +++ 引入我们写的plugin
module.exports = {
  mode: 'development',
  entry: {
    main: path.resolve(__dirname, './src/index.js')
  },
  output: {
    filename: 'main.js',  // +++ 打包的文件不再需要 hash
    path: path.resolve(__dirname, './dist')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, './index.html')
    }),
    new CleanWebpackPlugin(),
    new StampWebpackPlugin()  // 使用我们的plugin
  ]
}

重新执行打包,可以看到编译反弹函数的参数数据被复制了(也可以直接复制data.source看效果):

这里复制的内容太长,所以截取了一部分来解释一下。 可以看到我们复制的内容虽然是一个对象,但是该对象中包含了三个模块(一个插件模块、一个js模块、一个html模块),因为目前我们的dome中只有这三个模块,在其他的也就是说,编译对象可以在创建模块之前获取webpack需要创建的所有模块

明白了这一点,似乎世界都明亮了。 我觉得在html-webpack-plugin完成之前让我们的插件执行是可控的,我开心地笑了。

嘟嘟三声后,我又陷入了沉思。 现在当 webpack 读取 html-webpack-plugin 模块时我可以做一些事情,但是 html-webpack-plugin 会做两件事。 一是保存我们的根目录。 html文件解析一次,生成新的html到指定的dist目录; 第二个是在新生成的html文件中引入

解决这个问题的核心是:能否获取html-webpack-plugin插件的执行时间节点,如果可以的话可以在html-webpack-plugin执行到某个阶段的时候执行我们自己的插件

当你打开html-webpack-plugin的时候,你不禁感叹这个插件的作者真是太牛了。 看到文档Events module,作者已经考虑到了这一点,给我们提供了6个钩子函数(这里大家可以自己看一下,是哪6个,get就好)。这样我们的插件就已经准备好了

// 根目录 > stamp-webpack-plugin.js
 
const HtmlWebpackPlugin = require('html-webpack-plugin');  // +++ 引入html-webpack-plugin
class StampPlugin { // 创建一个时间戳插件
  apply(compiler) {
    // 使用 compilation 生命周期保证 plugin 执行在 文件创建之前
    // compiler 创建的 compilation 对象在回调中被使用
    compiler.hooks.compilation.tap('StampWebpackPlugin', (compilation, callback) => { // 注册一个StampWebpackPlugin方法
      
      // HtmlWebpackPlugin在webpack刚创建编译的时候执行自带的beforeAssetTagGeneration生命周期
      HtmlWebpackPlugin.getHooks(compilation).beforeAssetTagGeneration.tap('StampWebpackPlugin', 
        (htmlPluginData, cb) => {
          console.log(htmlPluginData);
        }
      )
    })
  }
}
module.exports = StampPlugin

删除编译对象的测试代码,调整代码如上,并注释解释。 执行打包让我们看看在 html-webpack-plugin 执行将脚本插入到 index.html 之前得到了什么:

注意,此时html-webpack-plugin接收到的内容只是一个js链表。 这个js链表中存储的元素就是html-webpack-plugin将放在src属性上的值,案例已经解决了! ! !

那岂不是只要我改变了js字段中的元素,src上放置的内容也会改变! 还记得我们的最终目标是什么吗?需要拼出时间戳,完成它

最终代码:

// 根目录 > stamp-webpack-plugin.js
 
const HtmlWebpackPlugin = require('html-webpack-plugin');  // +++ 引入html-webpack-plugin
class StampPlugin { // 创建一个时间戳插件
  apply(compiler) {
    // 使用 compilation 生命周期保证 plugin 执行在 文件创建之前
    // compiler 创建的 compilation 对象在回调中被使用
    compiler.hooks.compilation.tap('StampWebpackPlugin', (compilation, callback) => { // 注册一个StampWebpackPlugin方法
      
      // HtmlWebpackPlugin在webpack刚创建编译的时候执行自带的beforeAssetTagGeneration生命周期
      HtmlWebpackPlugin.getHooks(compilation).beforeAssetTagGeneration.tap('StampWebpackPlugin', 
        (htmlPluginData, cb) => {
          let jsSrc = htmlPluginData.assets.js[0]  // ++++++
          // 直接修改js数组内的元素
          htmlPluginData.assets.js[0] = `${jsSrc}?${new Date().getTime()}` // +++++++
        }
      )
    })
  }
}
module.exports = StampPlugin

npmrunbuild 打包看看效果:

完美的! ! !

如有需要请点击:源码地址

结语

思考和动手只有一步之遥。 希望这篇文章能够帮助你更好的理解webpack,让你在笔试和工作中遇到插件开发时不再害怕。

文章参考:

Webpack插件开发

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悟空资源网 webpack webpack pluign作用-带你一步步开发webpack5.x-plugin(保姆教程) https://www.wkzy.net/game/192472.html

常见问题

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务