当前位置:编程学习 > > 正文

webpack源码解析(80行代码写一个Webpack插件并发布到npm)

时间:2022-03-28 01:28:08类别:编程学习

webpack源码解析

80行代码写一个Webpack插件并发布到npm1. 前言

最近在学习 Webpack 相关的原理,以前只知道 Webpack 的配置方法,但并不知道其内部流程,经过一轮的学习,感觉获益良多,为了巩固学习的内容,我决定尝试自己动手写一个插件。

这个插件实现的功能比较简单:

2. Webpack 的构建流程以及 plugin 的原理

2.1 Webpack 构建流程

Webpack 的主要构建流程,可以分为三个阶段:

如果 Webpack 打包生产环境文件时,只会执行一次构建,以上阶段会按顺序执行一遍。但是在开启监听模式时,如开发环境,Webpack 会持续的进行构建。

webpack源码解析(80行代码写一个Webpack插件并发布到npm)

2.2 plugin 原理

Webpack 插件通常是一个带有 apply 函数的类,其中 constructor 可以接收传入的配置项。插件被安装时,apply 函数会被调用一次,并接收 Compiler 对象,然后我们可以在 Compiler 对象上监听不同的事件钩子,从而进行插件功能的开发。

  • // 定义一个插件
    class MyPlugin {
      // 构造函数,接收插件的配置项 options 
      constructor(options) {
        // 获取配置项,初始化插件
      }
    
      // 插件安装时会调用 apply,并传入 compiler
      apply(compiler) {
        // 获取 comolier 独享,可以监听事件钩子
        // 功能开发 ... 
      }
    }
    
  • 2.3 compiler 和 compilation 对象

    在开发 Plugin 过程中最常用的两个对象就是 CompilerCompilation

    3. 插件开发

    3.1 项目目录

    该插件实现的功能比较简单,文件目录也不复杂。首先新建一个空文件夹 remove-console-Webpack-plugin,并在该文件夹目录下运行 npm init,根据提示来填写 package.json 相关信息。然后再新建一个 src 文件夹,插件主要代码就放在 src/index.js 里面。如果你需要把项目放到 github 上,最好也添加一下 .gitignoreREADME.md 等文件。

  • // remove-console-Webpack-plugin
    ├─src
    │  └─index.js  
    ├─.gitignore
    ├─package.json
    └─README.md 
    
  • 3.2 插件代码

    插件代码逻辑也并不复杂,主要有几点:

  • class RemoveConsoleWebpackPlugin {
      // 构造函数接受配置参数
      constructor(options) {
        let include = options && options.include;
        let removed = ['log']; // 默认清除的方法
    
        if (include) {
          if (!Array.isArray(include)) {
            console.error('options.include must be an Array.');
          } else if (include.includes('*')) {
            // 传入 * 表示清除所有 console 的方法
            removed = Object.keys(console).filter(fn => {
              return typeof console[fn] === 'function';
            })
          } else {
            removed = include; // 根据传入配置覆盖
          }
        }
    
        this.removed = removed;
      }
    
      // Webpack 会调用插件实例的 apply 方法,并传入compiler 对象
      apply(compiler) {
        // js 资源代码处理函数
        let assetsHandler = (assets, compilation) => {
          let removedStr = this.removed.reduce((a, b) => (a + '|' + b));
    
          let reDict = {
            1: [RegExp(`\\.console\\.(${removedStr})\\(\\)`, 'g'), ''],
            2: [RegExp(`\\.console\\.(${removedStr})\\(`, 'g'), ';('],
            3: [RegExp(`console\\.(${removedStr})\\(\\)`, 'g'), ''],
            4: [RegExp(`console\\.(${removedStr})\\(`, 'g'), '(']
          }
    
          Object.entries(assets).forEach(([filename, source]) => {
            // 匹配js文件
            if (/\.js$/.test(filename)) {
              // 处理前文件内容
              let outputContent = source.source();
    
              Object.keys(reDict).forEach(i => {
                let [re, s] = reDict[i];
                outputContent = outputContent.replace(re, s);
              })
    
              compilation.assets[filename] = {
                // 返回文件内容
                source: () => {
                  return outputContent
                },
                // 返回文件大小
                size: () => {
                  return Buffer.byteLength(outputContent, 'utf8')
                }
              }
            }
          })
        }
    
        /**
         * 通过 compiler.hooks.compilation.tap 监听事件
         * 在回调方法中获取到 compilation 对象
         */
        compiler.hooks.compilation.tap('RemoveConsoleWebpackPlugin',
          compilation => {
            // Webpack 5
            if (compilation.hooks.processAssets) {
              compilation.hooks.processAssets.tap(
                { name: 'RemoveConsoleWebpackPlugin' },
                assets => assetsHandler(assets, compilation)
              );
            } else if (compilation.hooks.optimizeAssets) {
              // Webpack 4
              compilation.hooks.optimizeAssets.tap(
                'RemoveConsoleWebpackPlugin', 
                assets => assetsHandler(assets, compilation)
              );
            }
          })
      }
    }
    
    // export Plugin
    module.exports = RemoveConsoleWebpackPlugin;
    
  • 4. 发布到npm

    希望别人能使用到你的插件,就需要把插件发布到 npm 上,发布的主要流程:

    首先在 npm 官网上注册账号,然后打开命令行工具,在任意目录下输入 npm login 并按提示登录;

    webpack源码解析(80行代码写一个Webpack插件并发布到npm)

    登录后可用 npm whoami 查看是否登录成功;

    webpack源码解析(80行代码写一个Webpack插件并发布到npm)

    发布前检查一下根目录下的 package.json 文件信息是否填写正确,主要字段:

    webpack源码解析(80行代码写一个Webpack插件并发布到npm)

    一切准备就绪后,切换到插件所在的目录下,运行 npm publish 即可上传插件;

    webpack源码解析(80行代码写一个Webpack插件并发布到npm)

    上传成功后,到 npm 官网上搜索,看看是否能搜到插件;

    webpack源码解析(80行代码写一个Webpack插件并发布到npm)

    5. 结尾

    到此这篇关于80行代码写一个Webpack插件并发布到npm的文章就介绍到这了,更多相关Webpack插件发布到npm内容请搜索开心学习网以前的文章或继续浏览下面的相关文章希望大家以后多多支持开心学习网!

    标签:
    上一篇下一篇

    猜您喜欢

    热门推荐