• home > tools > Bundler > webpackTheory >

    webpack loader与plugin有何区别,分别如何实现?

    Author:zhoulujun Date:

    《webpack原理(1):Webpack热更新实现原理代码分析》,对webpack基本概念做了解析:webpack中loader和plugin作用loader是文件转换器,将webp

    webpack原理(1):Webpack热更新实现原理代码分析 》,对webpack基本概念做了解析:

    webpack中loader和plugin作用

    • loader是文件转换器,将webpack不能处理的模块转换为webpack能处理的模块

      用于对模块源码的转换,loader描述了webpack如何处理非javascript模块,并且在build中引入这些依赖。loader可以将文件从不同的语言(如TypeScript)转换为JavaScript。

    • plugin是功能扩展,干预webpack的打包过程,修改编译结果或打包结果

      目的在于解决loader无法实现的其他事,从打包优化和压缩,到重新定义环境变量,功能强大到可以用来处理各种各样的任务。

    简而言之,loader可以理解成webpack的横向广度,有了loader,webpack才可以打包处理各种的扩展语言。而plugin可以理解为webpack的纵向深度,在生命周期内注入不同的插件来扩展更多的能力

    官方解析:

    While loaders are used to transform certain types of modules, plugins can be leveraged to perform a wider range of tasks like bundle optimization, asset management and injection of environment variables.

    本篇更加详细地讲讲

    loader原理和实现

    webpack 本身只能处理.js 和.json 文件,loader 可以让webpack 去处理其他种类的文件,并将这些资源和文件转换为模组,让应用程式可以使用、并添加到依赖图中。以程式的角度来看, loader 本身就是一个 JavaScript 的函式,不同的 loader 即是能处理不同种文件的函式

    举例来说,如果我们想处理.css文件,可以使用 style-loader 和 css-loader 来处理,透过这两个 loader,让原本只懂 JavaScript 与 JSON 的 webpack 也可以处理 css,让.css文件能被打包到最终档案中。

    使用 loader

    有两种使用 loader 的方式,一是配置方式(Configuration),第二种是内联方式( Inline)。

    官方推荐的使用方法是第一种配置方式,使用方法是在  webpack.config.js 文件中指定 loader。

    module.exports = {
      module: {
        rules: [
          {
            test: /\.css$/,
            use: [
              { loader: "style-loader" },
              {
                loader: "css-loader",
                options: {
                  modules: true,
                },
              },
              { loader: "sass-loader" },
            ],
          },
        ],
      },
    };

    上方代码例子,执行顺序会是 sass-loader⇒ css-loader ⇒ style-loader


    那么如何实现一个loader呢?

    // demo-loader.js,来处理 
    const loaderUtils = require('loader-utils') 
    // 接收options配置
    module.exports = function(source) {    
        const options = loaderUtils.getOptions(this)    
        source = source.replace(/蒋梨花/g, options.name)    
        return `module.exports = ${JSON.stringify(sorce)}`    
        // 最终需要返回一段可执行的js脚本
    }

    上面的loader就处理如下rule

     {rules: [ { test: /\.txt$/, use: ['demo-loader'], options: { name: '梨花酱'}]}

    在实现一个 loader 的时候,要牢记几个原则哦:**单一职责;链式组合;模块化;无状态**

    Plugin 的原理与实现

    plugin是运行在webpak打包过程中的某段逻辑,它主要的作用是根据webpack提供的一些hooks来进行一些额外的操作,使 webpack 更加灵活扩展。

    Compiler 和Compilation

    • compiler 对象代表了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性建立,并配置好所有可操作的设置,包括 options,loader 和 plugin。当在 webpack 环境中应用一个插件时,插件将收到此 compiler 对象的引用。可以使用它来访问 webpack 的主环境。

    • compilation 对象代表了一次资源版本构建。当运行 webpack 开发环境中间件时,每当检测到一个文件变化,就会创建一个新的 compilation,从而生成一组新的编译资源。一个 compilation 对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。compilation 对象也提供了很多关键时机的回调,以供插件做自定义处理时选择使用。

    在 webpack 启动后,它会执行 new xxxPlugin(options) 来初始化插件实例。在初始化对象后,会去调用 xxxPlugin.apply(compiler) 并传入 compiler 对象。插件获得 compiler 对象后,可以通过

    compiler.plugin('事件名', 回调函数) 的方式进行监听 webpack 广播出来的事件了。

    那么具体如何实现一个 plugin 呢?

    从程式的角度来看,loader 是函式,而 plugin 则是带有apply 方法的物件,在apply 方法中注册 webpack 生命周期提供的钩子,当运行到该生命周期时,就会回调开发者写在apply 中的功能。

    // MyTestPlugin.js
    const { ConcatSource } = require("webpack-sources") // 用来写入
    class MyBannerPlugin {
      constructor(options) {
        this.msg = options.msg // 获取传入的option信息    
      },  // 我们需要一个apply方法(为了获取compiler),接收compiler作为参数表示这次打包的上下文。  
      apply (compiler) {
        const msg = this. msg    // 指定挂载的 webpack 钩子函数    
        // 使用compiler钩子compilation,即编译(compilation)创建之后,执行插件。    
        compiler.hooks.compilation.tap("MyTestPlugin", compilation => {
          // compilation的 optimizeChunkAssets 钩子,可以利用这个钩子实现为每个文件插入信息      
          compilation.hooks.optimizeChunkAssets.tap("MyTestPlugin", chunks => {
            for (const chunk of chunks) {
              for (const file of chunk.files) {
                compilation.updateAsset(file, old => {
                  return new ConcatSource(msg,"\n", old);
                });
              }
            }
          })
        })
      }
    }
    module.exports = MyTestPlugin

    可以看出,要实现一个plugin需要以下几步:

    1. 首先需要声明一个 class 构造函数

    2. 在class里面定义一个apply方法,接收compiler作为参数表示这次打包的上下文。

    3. 指定挂载的webpack事件钩子

    4. 处理webpack内部实例的特定数据

    5. 功能完成后调用webpack提供的回调


    参考文章:

    模块化后网页中引入的静态资源多了以后有什么问题? https://juejin.cn/post/6861784748491669511

    https://www.explainthis.io/zh-hans/swe/webpack-loader-plugin



    转载本站文章《webpack loader与plugin有何区别,分别如何实现?》,
    请注明出处:https://www.zhoulujun.cn/html/tools/Bundler/webpackTheory/9243.html