墙裂推荐plugin(pluginhybrid什么意思)

Mark wiens

发布时间:2023-12-25


Webpack Plugin 对于一些前端同学来说,算得上是既陌生又熟悉的存在刚好前段时间我实现了一个Webpack Plugin: I18nExtractorWebpackPlugin,其目的是收集代码中所有的I1

墙裂推荐plugin(pluginhybrid什么意思)

 

Webpack Plugin 对于一些前端同学来说,算得上是既陌生又熟悉的存在刚好前段时间我实现了一个Webpack Plugin: I18nExtractorWebpackPlugin,其目的是收集代码中所有的I18n string ID 并生成js文件

基于该插件最终实现自动注册 React/Vue app 中引用到的 Mojo 侧所有 I18n string ID,  解决了之前一直需要靠SE手动维护 I18n ID 的痛点借此机会我正好进入了 Webpack Plugin 这个“黑盒”一探究竟,去了解它内部的实现机制,并在此与大家做个分享。

图片源自网络,侵删Webpack Plugin定义首先来看看 Webpack 官网对 Plugin 的官方解释:Webpack 插件是一个具有 apply 方法的 JavaScript 对象apply 方法会被 Webpack Compiler 调用,并且在整个编译生命周期都可以访问 Compiler 对象。

它向第三方开发者提供了 Webpack 引擎中完整的能力使用阶段式的构建回调,开发者可以在 Webpack 构建流程中引入自定义的行为emmm,这解释就真的很官方了,我好像看明白,又好像没明白….

图片源自网络,侵删翻译成白话大概就是:Webpack Plugin 可以允许开发者在 Webpack 构建整个项目的过程中插入我们想要实现的自定义行为,Webpack 在构建的不同阶段都会向用户提供不同的钩子函数(hook), 用户可以在这些 hooks 中实现想做的事情。

还是不太明白?别着急,再往下看看Webpack架构虽然 Webpack 很复杂,但是其内部实现却很优雅,是一个典型的可插拔架构,并且灵活可扩展近几年出来的一些构建工具,大多也都参考了 Webpack 的这种架构方式。

Webpack 对项目构建打包的过程本质上是由许多个内置的 Plugin 实现的,在不同的阶段由不同的 Plugin 执行相关的工作关键点在于 Webpack 需要实现一种事件流将所有 Plugin 串联起来,而实现这一切的核心在于其内部借助了 。

Tapable:Tapable 是一个事件订阅发布的库,Webpack 通过 Tapable 注册自定义事件,并在合适的时机触发执行其实 Tapable 的概念和 Vue/React 中的生命周期钩子函数有点类似,都是在不同的生命周期执行不同的自定义函数。

Tapable的使用也非常简单,只需三步:实例化钩子函数注册事件触发事件module.exports = I18nExtractorWebpackPlugin;const { SyncHook } = require(

"tapable"); //这是一个同步钩子//第一步:实例化钩子函数,可以在这里定义形参const syncHook = new SyncHook(["author"]);//第二步:注册事件1syncHook.tap(

"监听器1", (name) => {  console.log("监听器1:", name);});//第二步:注册事件2syncHook.tap("监听器2", (name) => {  console.

log("监听器2", name);});//第三步:触发事件syncHook.call("起来干饭了!");执行结果:监听器1 起来干饭了!监听器2 起来干饭了!这里只是举了一个 Tapable 的例子,有兴趣的同学可以移步以下链接进一步了解强大的 Tapable:

https://www.npmjs.com/package/tapableWebpack构建过程如果把 Webpack 构建过程比喻成大饭店完成一场宴席工作,Tapable 就是一张时间计划表,大致上分为三个阶段:

准备食材——打包开始前的准备工作制作菜肴——编译阶段摆盘上菜——编译完成后的阶段光有时间表还不行,还得有一个大管家来安排这一切,这种时候 Compiler 就该出场了:Compiler对象代表了一次webpack构建过程中

完整的生命周期,它提供了webpack所有的设置和上下文环境,而插件Plugin就能通过compiler对象访问到webpack环境中的所有信息Compiler 模块是 Webpack 的主要引擎,它通过 CLI 或者 Node API 传递的所有选项创建出一个 。

Compilation 实例它扩展(extends)自 Tapable 类,用来注册和调用插件大多数面向用户的插件会首先在 Compiler 上注册在这场浩大的宴席工作中,最重要的莫过于制作菜肴这个过程了,也就是 Webpack 对项目进行编译打包的过程,这时候就该要介绍一下我们的厨师。

Compilation:Compilation 代表了一次资源版本构建,提供当前模块资源,编译生成的资源以及变化的文件等重要信息Compilation 模块会被 Compiler 用来创建新的 Compilation。

对象(或新的 build 对象)Compilation 实例能够访问所有的模块和它们的依赖(大部分是循环依赖)它会对应用程序依赖图中的所有模块, 进行字面上的编译(literal compilation)。

在编译阶段,模块会被加载(load)、封存(seal)、优化(optimize)、 分块(chunk)、哈希(hash)和重新创建(restore)Compilation 本身也提供了很多颗粒度更细的 hook 给插件 Plugin 选择使用。

下面用一张图对上文进行总结:

可前往以下链接获得关于 Webpack Plugin hook 更详细的介绍:https://webpack.docschina.org/api/compiler-hooks/#additionalPass

说了这么多,肯定有人会说:

图片源自网络,侵删那么接下来,马上开始介绍如何实现 I18nExtractorWebpackPluginWebpack Plugin的实现方法i18n-extractor-webpack-plugin 。

需求分析:基于 Mojo 开发的 hybrid app(React/Vue), 需要收集所有从 Mojo 侧引用的 I18n keys 进行静态注册以 React-Bridge 为例, 其包含多个APP, 每个APP又可能依赖于多个组件,手动维护 I18n ID 是一件令人头秃的事情,因此我们需要实现一个插件自动收集 I18n keys。

首先,Webpack Plugin 其实就是一个包含 apply 方法的函数,Webpack 在打包过程中会执行插件的 apply 方法,并且会把 Compiler 作为参数传入:classI18nExtractorWebpackPlugin

{   apply(compiler){//         console.log("this is a webpack plugin")   }}这就是Plugin的雏形那么为什么是 apply 方法呢?。

其实原因并不深奥,在 Webpack 源码中,它调用的就是插件的 apply 方法关键在于:如何在 apply 方法中调用各种生命周期 hook 函数以满足插件需求以项目为例,在 React-bridge 项目中对于 I18n 的引用方法一般为以下形式,而我们最终需要的是将第一个参数,也就是 string ID 抓取出来。

i18n(123,"default string")// i18n(id, string)在 Webpack 构建过程中,可以将代码看成是 stream,也就是字符串流因此可以利用正则表达式匹配 I18n function pattern:。

this.functionPattern = /i18n\(\s*(\d+),\s*.*?\)/搞定如何匹配 pattern 后,接下来需要解决何时匹配这个问题要出场的就是compilation.hooks.optimizeChunks。

这个 hook 了optimizeChunks 会在 Compilation 生成 chunk 之后,但在 minification 之前调用,此时 chunk 还未经压缩优化,因此仍然能获取到 chunk 对应的源码。

在该 hook 中,可以通过之前的 functionPattern 匹配抓取 string ID在遍历 chunk 时,一般我们不会关心 node_modules 路径下的 third library,因此需要设置一个 module filter 过滤掉我们不关心的文件。

module filter 可以用正则表达式表示:this.moduleFilter = [      /((?:[^!?\s]+?)(?:\.js|\.jsx|\.ts|\.tsx))$/,      /^((?!node_modules).)*$/,

];核心实现方法如下:apply(compiler){//compiler.hooks.compilation.tap 表示注册一个在compilation创建之后执行的hook函数,//当compilation创建完成之后webpack会调用这个hook并将compilation作为参数传入。

compiler.hooks.compilation.tap((compilation)=>{//compilation.hooks.optimizeChunks.tap 表示在对chunk进行优化阶段开始之前调用,

//此时的chunk由于还未被压缩优化,因此仍然可以获取到源码source code         compilation.hooks.optimizeChunks.tap((chunks)=>{               chunks.forEach((chunk)=>{

                 compilation.chunkGraph                   .getChunkModules(chunk)                   .forEach((

module)=>{if(filterModule(module)){const source = module._source._value;while (this.functionPattern.exec(source)) !== null) {

//收集所有string ID                        }                                          }                  

                   })                                  ...                              }) }) })}另外,在编译结束后我们需要将 string ID 以及匹配到的 I18n string 相关的信息进一步预处理后作为返回值暴露出去,

方便使用者基于插件做一些上层的逻辑处理,因此我们还需要使用到 compiler.hooks.done,该 hook 会在整个 Compilation 构建工作结束后被调用apply(compiler){。

...//ompiler.hooks.done 表示注册一个在 compilation完成后执行的操作事件 compiler.hooks.done.tap(()=>{   ...   if(this.done){

this.done(results)//this.done是创建插件时传进来的一个callback函数   } }) }最后,为了增强插件的扩展性, 我们需要将 functionPattern、moduleFilter、done 等关键字以参数的形式暴露给插件使用者:。

classI18nExtractorWebpackPlugin {  constructor(options) {    options = options || {};this.functionPattern = options.functionPattern || /i18n\(\s*(\d+),\s*.*?\)/;

this.done = options.done || undefined;this.groupIndex = options.groupIndex || [1];this.moduleFilter = options.moduleFilter || [

     /((?:[^!?\s]+?)(?:\.js|\.jsx|\.ts|\.tsx))$/,      /^((?!node_modules).)*$/,    ];    }     apply(compiler){

//        ...     }}使用 Plugin 的过程也很简单,只需要在 webpack.config.js 中创建插件实例并传入配置参数:module.exports  = { ...,

plugins:[ new I18nExtractorWebpackPlugin({            done: (json) => {        rimraf.sync(path.resolve(__dirname,

dist));        generateLoaders(json, path.resolve(__dirname, path/));      }        }) ]}Plugin 执行后,在指定路径下会生成

[bundleName]Loader.js 文件,该文件包含所有 React-bridge 项目中用到的 string ID,我们只需要在 Mojo 侧引入该 [bundleName]Loader.js

文件便可实现静态注册 string ID目前我们已经在 CI 中基于该插件,实现了一整套自动管理 React-bridge I18N string ID 的 workflow,  再也不用担心头秃啦

参考文献:https://juejin.cn/post/7170852747749621791#heading-2https://webpack.docschina.org/

免责声明:本站所有信息均搜集自互联网,并不代表本站观点,本站不对其真实合法性负责。如有信息侵犯了您的权益,请告知,本站将立刻处理。联系QQ:1640731186