loader和plugin区别

  • loader : 文件加载器、告诉webpack如何转化和处理某一类型文件、并引入到打包处的文件当中
  • plugin : 自定义打包过程的方式、webpack提供了非常多的生命周期钩子我们可以在钩子函数里吗做一些我们想处理的逻辑、比如我想在输出文件的时候添加一段js、在或者我想在指定文件当中删除某些代码、都是可以的

    开始

    webpack插件可以是一个构造器或者一个对象、但是里面必须要有一个apply方法、apply方法会被webpack compiler调用、apply方法接收一个compiler对象、你可以往该对象挂在一些生命周期的callback 在不同的生命周期的钩子里面我们可以处理不同事情、所以我们只需要了解不同生命周期的方法和方法对应给到的参数就可以做业务逻辑处理了、说到底还是:增删改查

学习compiler & compilation钩子流程

先了解compiler和compilation是什么东西、有什么关联!

  • Compiler 对象包含了 Webpack 环境所有的的配置信息,包含 options,loaders,plugins 这些信息,这个对象在 Webpack 启动时候被实例化,它是全局唯一的,可以简单地把它理解为 Webpack 实例;
  • Compilation 对象包含了当前的模块资源、编译生成资源、变化的文件等。当 Webpack 以开发模式运行时每当检测到一个文件变化,一次新的 Compilation 将被创建。Compilation 对象也提供了很多事件回调供插件做扩展。通过 Compilation 也能读取到 Compiler 对象。

    webpack基础搭建

    ```javascript // 第一步创建文件夹webpack-test yarn init //初始化 yarn add webpack webpack-cli webpack-dev-server -D // 安装必须要的插件

// 第二步 创建src文件夹 下面main.js console.log(‘2222’)

// 第三步根节点创建webpack.config.js const path = require(‘path’); module.exports = { entry: ‘./src/main.js’, devtool: ‘inline-source-map’, devServer: { static: ‘dist’, compress: true, port: 9000, }, mode: ‘development’, plugins: [], output: { filename: ‘[name].bundle.js’, path: path.resolve(__dirname, ‘dist’), clean: true, }, };

// 第四步配置script脚本命令 “scripts”: { “build”: “webpack —config webpack.config.js”, “start”: “webpack serve” }

// 启动 yarn start

  1. > 正常情况下你就能够看到启动成功的日志、在浏览器访问[http://localhost:9000](http://localhost:9000/) 就能正常的访问、这样基础cli就完成了、现在开始插件的基础使用
  2. <a name="a5OU0"></a>
  3. ### 插件的基础使用
  4. - 根节点创建build文件夹、并创建plugins文件夹
  5. - 创建webpack-plugin-test 、然后在当前文件夹创建index.js
  6. ```javascript
  7. // /build/plugins/webpack-plugin-test/index.js
  8. class WebpackPluginTest{
  9. constructor(){}
  10. apply(compiler){
  11. console.log(compiler)
  12. }
  13. }
  14. module.exports = WebpackPluginTest
  15. // webpack.config.js
  16. const WebpackPluginTest = require("./build/plugins/webpack-plugin-test/index");
  17. // 使用插件
  18. plugins: [
  19. new WebpackPluginTest({
  20. type:"dev",
  21. number:1
  22. })
  23. ]

启动打包执行插件

yarn build 能发现我们这个compiler对象输出还是挺多的内容的

打包流程的生命周期

我们需要先了解下webpack不同的生命周期钩子、钩子的用法、文档在开头、可以自行查看

  1. class WebpackPluginTest{
  2. constructor(options){
  3. console.log('test',options);
  4. }
  5. apply(compiler){
  6. // 输出 asset 到 output 目录之前执行、更多生命周期请查阅官网:https://webpack.docschina.org/api/compiler-hooks/#emit
  7. compiler.hooks.emit.tap("WebpackPluginTest", (compilation) =>{
  8. })
  9. }
  10. }
  11. module.exports = WebpackPluginTest

emit 钩子事件发生时,代表源文件的转换和组装已经完成,在这里可以读取到最终将输出的资源、代码块、模块及其依赖,并且可以修改输出资源的内容

常用的生命周期钩子

  • entryOption : 在 webpack 选项中的 entry 配置项 处理过之后,执行插件。
  • afterPlugins : 设置完初始插件之后,执行插件。
  • compilation : 编译创建之后,生成文件之前,执行插件。。
  • emit : 生成资源到 output 目录之前。
  • done : 编译完成。

    读取输出资源、代码块、模块及其依赖

    在增删改查前、我们需要先了解下compilation 里面有那些属性是我们常用到的、和可能用到的

compilation

chunks

entry 入口文件配置了几个就会有几个对象包信息,根据若干个module依赖打包生成的一个包、每个chunk管理最终渲染资源的组合

  1. {
  2. id: 0,
  3. rendered: true,
  4. initial: false,
  5. //是否含有 Webpack 的 runtime 环境,通过 CommonChunkPlugin 处理后,runtime 环境被提到最高层级的 chunk
  6. entry: false,
  7. recorded: undefined,
  8. extraAsync: false,
  9. size: 296855,
  10. //require.ensure 不是通过 Webpack 配置的,所以 chunk 的 names 是空
  11. names: [],
  12. //该 chunk 产生的输出文件,即输出到特定文件路径下的文件名称
  13. files: [ '0.bundle.js' ],
  14. //chunk 的 hash,即 chunkHash
  15. hash: '42fbfbea594ba593e76a',
  16. //父级 chunk 的 id 值
  17. parents: [ 2 ],
  18. //该 chunk 是如何产生的
  19. origins: [ [Object] ]
  20. }

assets

所有需要输出的资源会存放在 compilation.assets 、我们可以通过输出的资源包名称compilation.assets[name].source() 获取到当前文件内容操作

  1. {
  2. "main.bundle.js":{
  3. _childern,
  4. ....
  5. }
  6. }

modules

保存着依赖模块的列表信息、通过扩展,您可以查看每个模块的依赖关系,以查看传递到chunk中的原始源文件

  1. [
  2. {
  3. binary:false,
  4. blocks:[],
  5. buildInfo:{}
  6. ....
  7. }
  8. ]

案例

生成一个新的文件输出出去

  1. class WebpackPluginTest{
  2. constructor(options){
  3. this.options = options
  4. }
  5. apply(compiler){
  6. // (不要直接 require/import webpack)
  7. const { webpack } = compiler;
  8. // Compilation 对象提供了对一些有用常量的访问。
  9. const { Compilation } = webpack;
  10. // RawSource 是其中一种 “源码”("sources") 类型,用来在 compilation 中表示资源的源码
  11. const { RawSource } = webpack.sources;
  12. const { fileName } = this.options
  13. compiler.hooks.thisCompilation.tap("WebpackPluginTest", (compilation) =>{
  14. compilation.hooks.processAssets.tap(
  15. {
  16. name: fileName,
  17. stage: Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE,
  18. },
  19. (assets) =>{
  20. // "assets" 是一个包含 compilation 中所有资源(assets)的对象。 该对象的键是资源的路径, 值是文件的源码
  21. const content = `const name = 'gf'`
  22. // 向 compilation 添加新的资源, 这样 webpack 就会自动生成并输出到 output 目录
  23. compilation.emitAsset(
  24. fileName,
  25. new RawSource(content)
  26. );
  27. }
  28. )
  29. })
  30. }
  31. }
  32. module.exports = WebpackPluginTest;
  33. // webpack.config.js
  34. plugins: [
  35. new WebpackPluginTest({
  36. fileName:"gf.js"
  37. })
  38. ],

修改某个文件的内容

重写某个文件的source方法

  1. class WebpackPluginTest {
  2. constructor(options) {
  3. this.options = options
  4. }
  5. apply(compiler) {
  6. const { fileName, fileContent, replaceContent } = this.options;
  7. compiler.hooks.emit.tap("WebpackPluginTest", (compilation) => {
  8. const content = compilation.assets[fileName].source();
  9. const reg = new RegExp(`${fileContent}`);
  10. const targetContent = content.replace(reg, replaceContent);
  11. compilation.assets[fileName] = {
  12. source: () => {
  13. // targetContent 既可以是代表文本文件的字符串,也可以是代表二进制文件的 Buffer
  14. return targetContent
  15. },
  16. // 返回文件大小
  17. size: () => {
  18. return Buffer.byteLength(targetContent, 'utf8');
  19. }
  20. }
  21. })
  22. }
  23. }
  24. module.exports = WebpackPluginTest;
  25. // webpack.config.js
  26. plugins: [
  27. new WebpackPluginTest({
  28. fileName:"main.bundle.js",
  29. replaceContent:"main-GF-2222222222",
  30. fileContent:"mainJs"
  31. })
  32. ]
  33. // main.js
  34. console.log('mainJs')

了解整个生命周期钩子

钩子类型105d3dabd4d5699c5ad1168fb71b427.png

不同类型的生命周期钩子所挂载的监听方法也会有不同、上面提供一个全部钩子的流程图、然后在告诉一个小技巧就是我们这么多钩子我们如何去看他支持那种监听方法、这个时候我们就可以通过最传统的debugger方式去查找

image.png
这样我们就可以先找生命周期钩子方法、然后通过debugger、这样我们就能找到当前监听这个生命周期的方法名称了!