源码细读-深入了解terser-webpack-plugin的实现
terser-webpack-plugin 是一个基于 webpack 的插件,它利用 terser 库对 JavaScript 代码进行压缩和混淆。源码源代其核心功能在于通过在 webpack 的插件插件运行时钩子 optimizeChunkAssets 中注册,实现了代码优化过程。源码源代在 apply 函数中,插件插件它获取 compilation 实例,源码源代itop软件查看源码并通过 tapPromise 注册一个异步任务,插件插件当 webpack 执行优化阶段时,源码源代每个 chunk 会触发这个任务,插件插件执行 minify 函数进行压缩处理。源码源代
optimise 函数是插件插件实际的任务处理入口,它负责具体的源码源代优化流程。函数内部,插件插件scheduleTask 负责并行处理,源码源代如果开启 parallel 模式,插件插件会利用jest-worker提供的线程池进行并发工作,线程池管理复杂,根据 node 版本不同采用 worker_threads 或 child_process。minify 函数则是压缩和混淆代码的核心操作,它直接使用 terser 库完成任务。文字阅读 源码
总的来说,terser-webpack-plugin 的优化流程包括在 webpack 的优化阶段对代码进行压缩,使用 Jest 的 worker 线程池进行并行处理,以及通过 terser 库的实际压缩操作。理解这些核心环节,可以帮助开发者更深入地掌握该插件的使用和工作原理。
[Mybatis]-[基础支持层]-插件-plugin标签解析
该系列文章针对 Mybatis 3.5.1 版本
一、Mybatis 插件的作用
Mybatis 针对SQL映射语句执行过程进行拦截处理,而对应的拦截器 Mybaits 又称之为插件(这些插件就是Mybatis的扩展点)。
在 Mybaits 中允许用插件来拦截的方法包括:
通过插件的方式可以实现SQL打印、分页等插件功能实现。
二、Mybatis 插件配置
插件代码逻辑实现后还需要加载到 Mybatis 中才能生效,Mybatis 提供了配置标签,用来声明。
插件在mybatis-config.xml 中的配置案例,如下:
通过 标签,然后在指定的属性 interceptor 配置插件实现类的全路径即可。
三、e创源码`plugin` 标签解析
再来回顾一下,XMLConfigBuilder解析时序简图,如下:
在时序图中加载解析在XMLConfigBuilder#pluginElement中完成,相关解析代码如下:
如上述代码,`plugin` 的解析流程很简单
1、加载 `plugin` 下的子标签
2、获取 `plugin` 中 interceptor 属性中的 class 全路径名
3、class 必须实现了 Interceptor接口,如果满足,通过反射实例化类
4、把类加载到存放拦截器的拦截器容器,拦截器链 InterceptorChain
简单来看一下InterceptorChain 代码定义
如上述代码所示,InterceptorChain 本身就是一个容器,用来存放所有从 `plugin` 读取到的拦截器对象。
而这里的拦截器列表,在使用过程中,会通过代理的方式,对目标对象层层代理,通过责任链的源码资本etcp方式实现代码执行前后的层层过滤,相关逻辑图如下:
四、interceptor 过滤链代理处理代码
上面提到了 interceptor 过滤链的实现是通过代理的方式层层包裹实现的,下面来简单阅读代理流程源码
Mybatis 中只针对 Executor、ParameterHandler、ResultSetHandler、StatementHandler,这四种情况追加了过滤连的处理。
相关的处理方法入口为InterceptorChain#pluginAll,如下图
如上述代码,遍历所有的插件,调用插件本身的 plugin 方法来处理,也就是 Interceptor#plugin,来看通用实现
(也可以自定义实现逻辑),代码如下
如上述代码,通用的代理逻辑交由工具类 Plugin 来实现,
接着来看一下 Plugin#wrap 方法源码
如上述源码所示,通过配对当前 interceptor 是否符合目标对象 target ,如果配对,构建相应的分时阶梯源码代理对象。
以此类推,随后实现如下图的效果:
五、总结
通过上述源码解析能够知道一个插件,也就是一个 Interceptor 的定义需要满足两个条件
1、该插件实现类实现了 Interceptor 接口
2、该插件实现类通过注解 @Intercepts 指定了该插件需要拦截的对象,也就是 Executor、ParameterHandler、ResultSetHandler、StatementHandler 中的一种或者多种
`plugin` 标签中配置的是一系列拦截器,这些拦截器通过代理的方式组合起来实现了过滤器链。
而这些过滤器数据存储在 InterceptorChain 中,最终数据仍然会存在 Configuration 中,相关的 Configuration 逻辑图如下:
maven-compiler-plugin编译插件
在Java项目开发中,Maven的maven-compiler-plugin编译插件扮演了至关重要的角色,它简化了编译过程,允许自定义编译步骤。以下是关于该插件的详细说明:
默认情况下,maven-compiler-plugin负责了Java项目的编译任务,相当于命令行中的javac。为了进行个性化配置,开发者可以在pom.xml中添加如下内容:设定版本号,可自定义,maven会自动选择合适的版本。
配置编译器,如设置源代码编码、输出详细信息、指定编译器路径(executable),默认是利用JAVA_HOME,也可指定绝对路径。
管理内存使用,包括最小和最大内存,确保编译效率。
明确源代码和目标代码的语言级别,支持精确指定版本差异。
还可以通过release属性替代指定编译属性。
若想使用外部编译器(如Plexus Compiler),需在pom.xml中设置compilerId和fork标签。 对于JDK9+的兼容性,可能需要进行两次编译,配置方法有所变化,具体取决于兼容的JDK版本。 总的来说,虽然maven-compiler-plugin默认配置已能满足大部分项目,但对于特定需求,灵活的配置选项提供了强大的支持。一文搞懂Mybatis插件原理
Mybatis插件原理详解,让你轻松实现自定义功能
在深入Mybatis源码时,你可能会遇到名为"plugin"的包,这实际上用于扩展框架的功能,如PageHelper分页插件。本文将揭示插件的运作机制,教你如何理解其内部工作并自行实现,如分页或慢SQL统计。
使用PageHelper插件分页的简单步骤是:一,在pom.xml中添加依赖;二,在mybatis-config.xml中配置插件;三,在需要分页的mapper接口前调用startPage方法,并确保每次查询后关闭Page对象以避免数据冲突。
接下来,我们将探索插件的总体流程:首先,通过拦截器接口实现动态代理,如PageInterceptor,它会在Executor的query方法执行前后添加自定义逻辑。拦截器通过Invocation对象获取目标对象和方法参数,实现增强功能。
分页拦截器如PageInterceptor拦截query方法,若需要分页,会获取原始SQL并添加limit语句。配置插件时,在mybatis-config.xml中添加相应拦截器,如``标签和Interceptor实现类的全限定名。
当我们创建SqlSession时,拦截器通过Configuration的newExecutor方法注册并应用到Executor对象上,利用JDK动态代理生成增强的代理对象,从而在执行SQL时调用拦截器的intercept方法。
自定义慢SQL统计插件同样基于Interceptor,只需配置目标对象为StatementHandler,拦截方法为query、update或batch,然后在配置文件中添加插件的全限定名。
plugin历史渊源
插件的概念追溯至上世纪七十年代。当时在Univac /系列主机的Unisys VS/9操作系统中,运行着一个名为EDT的文本编辑器。此编辑器具有独特功能,能从编辑器中运行其他应用程序,并允许这些程序访问编辑器的缓冲区,让程序在内存中进行文本操作。这一功能随后被Waterloo Fortran编译器采用,与EDT编辑的Fortran程序进行交互编译。 插件功能在个人电脑上的首次应用可能出现在年的Macintosh上,HyperCard和QuarkXPress软件中均包含了此功能。年,Silicon Beach软件公司将其引入到Digital Darkroom和SuperPaint软件中,使得这些软件更加灵活与强大。然而,"插件"这一术语是由Ed Bomke提出并广泛使用的。 简言之,插件概念的起源可追溯至上世纪七十年代的主机操作系统,它允许应用程序访问共享缓冲区进行文本操作。随着个人电脑的普及,插件功能逐渐扩展至多种软件应用中,丰富了软件的功能性与灵活性。"插件"一词的正式命名则是在九十年代初由Ed Bomke提出,标志着插件这一概念在软件开发领域的广泛应用。扩展资料
Plugin(Plug-in,addin,add-in,addon或add-on)是一种计算机应用程序,它和主应用程序(host application)互相交互,以提供特定的功能。应用程序支持Plugin有许多原因,一些主要原因包括:使得第三方开发者有能力扩展应用程序,以提供无法先期预料的特色;减小应用程序的大小;由于软件版权之间的不兼容性将源代码和应用程序分享。详解webpackpluginçåçåç¼åä¸ä¸ªplugin
plugin解å³äºä»ä¹é®é¢ï¼
plugin解å³äºWebpackæ建çå½å¨æè¿ç¨ä¸çåè½å®å¶é®é¢ï¼å¯ä»¥å©ç¨pluginåä¸å°webpackæ建æµç¨ä¸çå个é¶æ®µå¹¶å«æåä¸äºä»£ç å¤çã
æ¯å¦ï¼æå åéè¦çæä¸ä¸ªhtmlæ件ï¼é£ä¹å°±å¯ä»¥ä½¿ç¨html-webpack-pluginãè¿æï¼å¨æå ä¹åædistæ件å é¤ï¼å°±å¯ä»¥ä½¿ç¨clean-webpack-pluginã
webpackæ¬èº«å°±æ¯ä¸ä¸ªæ建è¿ç¨çç¶ææºï¼å ¶èªèº«çæ ¸å¿åè½ä¹æ¯æ建å¨loaderåpluginçæºå¶ä¸çã
compileråcompilationå ·ä½æ¯å¹²ä»ä¹çï¼é¦å æ们æ¥çä¸ä¸ªwebpackèªå¸¦çæ件BannerPlugin代ç ï¼å ¶å®webpackçå¾å¤æ ¸å¿å ¬å¸å°±æ¯å©ç¨æ件æ¥å®ç°çã
æ件çæ ¼å¼
ä¸ä¸ªJavaScriptå½æ°æJavaScriptç±»;
å¨å®ååä¸å®ä¹çapplyæ¹æ³ï¼ä¼å¨å®è£ æ件æ¶è¢«è°ç¨ï¼å¹¶è¢«webpackcompilerè°ç¨ä¸æ¬¡;
æå®ä¸ä¸ªè§¦åå°webpackæ¬èº«çäºä»¶é©åï¼å³hooksï¼ç¨äºç¹å®æ¶æºå¤çé¢å¤çé»è¾;
classBannerPlugin{ constructor(options){ this.options=options;}apply(compiler){ constoptions=this.options;constbanner=this.banner;compiler.hooks.compilation.tap("BannerPlugin",compilation=>{ compilation.hooks.processAssets.tap({ name:"BannerPlugin"},()=>{ for(constchunkofcompilation.chunks){ for(constfileofchunk.files){ constdata={ chunk,filename:file};//çæ注éconstcomment=compilation.getPath(banner,data);//æ注éå å ¥å°æ件ä¸compilation.updateAsset(file,old=>{ constsource=options.footer?newConcatSource(old,"\n",comment):newConcatSource(comment,"\n",old);returnsource;}});}}}}}}ä»ä»£ç ä¸åºç°äºcompileråcompilationï¼é£å®ä»¬å°åºæ¯ä»ä¹å¢ï¼
compilercompiler模åæ¯Webpackææ ¸å¿ç模åãæ¯æ¬¡æ§è¡Webpackæ建çæ¶åï¼å¨Webpackå é¨ï¼ä¼é¦å å®ä¾åä¸ä¸ªCompiler对象ï¼ç¶åè°ç¨å®çrunæ¹æ³æ¥å¼å§ä¸æ¬¡å®æ´çç¼è¯è¿ç¨ãcompiler对象代表äºå®æ´çwebpackç¯å¢é ç½®ï¼æ件å¯ä»¥éè¿å®è·åå°webpackçé 置信æ¯ï¼å¦entryãoutputãmoduleçé ç½®ã
compileré©åcompileræå¾å¤é©åï¼ä¸é¢åªä»ç»å¸¸ç¨çå 个:é©åå?|Tapableç±»å|触åæ¶æº|ä¼ å ¥callbackçåæ°?||--------------------|-----------------|------------------------------------------|---------------------------------||entryOption?|SyncBailHook|å¨webpackä¸çentryé ç½®å¤çè¿ä¹å|contextï¼entry||afterPlugins|SyncHook?|åå§åå®å ç½®æ件ä¹å|compiler||environment?|SyncHook?|åå¤ç¼è¯ç¯å¢ï¼webpackpluginsé ç½®åå§åå®æä¹å?|compiler||beforeRun?|AsyncSeriesHook?|å¼å§æ£å¼ç¼è¯ä¹å?|compiler||run|AsyncSeriesHook?|å¼å§ç¼è¯ä¹åï¼è¯»årecordsä¹åï¼|compiler||compile|SyncHook?|ä¸æ¬¡compilationç¼è¯å建ä¹å|compilationParams||compilation?|SyncHook?|compilationå建æåä¹å?|compilationï¼compilationParams||emit|AsyncSeriesHook?|çæèµæºå°outputç®å½ä¹å?|compilation?||done|AsyncSeriesHook?|compilationå®æä¹å?|stats?||failed|SyncHook?|compilation失败|
æ´ä¸ªCompilerå®æ´å°å±ç°äºWebpackçæ建æµç¨ï¼
åå¤é¶æ®µï¼makeä¹ååçäºæ é½å±äºåå¤é¶æ®µï¼è¿é¶æ®µçcalbackå ¥å以compiler为主ï¼
ç¼è¯é¶æ®µï¼è¿é¶æ®µä»¥compilationçé©å为主ï¼calbackå ¥å以compilation为主ï¼
产åºé¶æ®µï¼è¿é¶æ®µä»compilationå¼å§ï¼æååå°Compileré©åä¸ï¼calbackä¼ å ¥åæ°æ¯è·ç»æç¸å ³çæ°æ®ï¼å æ¬statsãerrorã
compilationå¨compilationé¶æ®µï¼æ¨¡åä¼è¢«å è½½(loaded)ãå°å(sealed)ãä¼å(optimized)ãåå(chunked)ãåå¸(hashed)åéæ°å建(restored)ï¼Compilation对象å å«äºå½åç模åèµæºãç¼è¯çæèµæºãååçæ件çãCompilation对象ä¹æä¾äºå¾å¤äºä»¶åè°ä¾æ件åæ©å±ï¼éè¿Compilationä¹è½è¯»åå°Compiler对象ã
Compilationé©å
å¨Compilationä¸å¤çç对象åå«æ¯moduleãchunkãassetsï¼ç±modulesç»æchunksï¼ç±chunksçæassetsï¼å¤ç顺åºæ¯ï¼moduleâmodulesâchunksâassetsï¼å ä»å个moduleå¼å§å¤çï¼æ¥æ¾ä¾èµå ³ç³»ï¼æåå®æå个moduleå¤çï¼å®æå ¨é¨modulesä¹åï¼å¼å§chunksé¶æ®µå¤çï¼æåå¨æ ¹æ®ä¼åé ç½®ï¼æéçæassetsã
æ以æ´ä¸ªCompilationççå½å¨æé©åè½ç¶æ¯è¾å¤ï¼ä½æ¯å¤§è§å¾ä¸æ¯å´ç»è¿ä¸ªé¡ºåºè¿è¡çï¼å ·ä½çé©åå¯ä»¥æ¥çwebpackå®ç½ã
Stats对象å¨Webpackçåè°å½æ°ä¸ä¼å¾å°stats对象ãè¿ä¸ªå¯¹è±¡å®é æ¥èªäºCompilation.getStats()ï¼è¿åçæ¯ä¸»è¦å«æmodulesãchunksåassetsä¸ä¸ªå±æ§å¼ç对象ã
modulesï¼è®°å½äºææ解æåç模åï¼
chunksï¼è®°å½äºææchunkï¼
assetsï¼è®°å½äºææè¦çæçæ件ã
æäºå¯¹compilercompilationçç解ï¼é£ç°å¨æ¥ççBannerPluginçå®ç°ï¼è¿ä¸ªæ件çåè½æ¯å¨æåçæçæ件ç头é¨å ä¸ä¸æ®µæ们èªå®ä¹ç注éï¼é£ä¹å®çæ§è¡æ¶æºè¯å®æ¯å¨ç¼è¯å®æä¹åï¼çææå æ件ä¹é´ï¼ä¹å°±æ¯å¨compiler.hooks.compilationè¿ä¸ªå¤§é©åä¸é¢çprocessAssetsé©åéé¢æ§è¡æ们çé»è¾ã
ç¼åpluginplugin:å¨æ件尾é¨æå ¥ä¸æ®µæ³¨éé¦å å建ä¸ä¸ªplugins/FootPlugin.jsï¼ä»£ç å¦ä¸:
const{ ConcatSource}=require('webpack-sources')classFootPlugin{ constructor(options){ this.options=options}apply(compiler){ compiler.hooks.compilation.tap('FootPlugin',compilation=>{ compilation.hooks.processAssets.tap('FootPlugin',()=>{ for(constchunkofcompilation.chunks){ for(constfileofchunk.files){ console.log('file--',file)//bundle.js//å®ä¹æ³¨éçå 容constcomment=`/*${ this.options.banner}*/`compilation.updateAsset(file,old=>{ //æ注éåæ§ä»£ç è¿è¡æ¼æ¥returnnewConcatSource(old,'\n',comment)})}}})})}}module.exports=FootPluginwebpack.config.js
constFootPlugin=require('./plugins/FootPlugin')module.exports={ plugins:[newwebpack.BannerPlugin({ banner:'欢è¿å¦ä¹ '}),newFootPlugin({ banner:'ç»æå¦ä¹ '})]}å¯ä»¥çå°å¨bundle.jsçå¼å¤´åç»å°¾é½æ对åºç注éã
plugin:æä»¶è¶ è¿ä¸å®å¤§å°æ¶ç»åºè¦åconst{ resolve}=require('path')constfs=require('fs')classBundleSizeWebpackPlugin{ constructor(options){ this.options=options}apply(compiler){ const{ sizeLimit}=this.optionsconsole.log('bundlesizeplugin')//å¨ç¼è¯å®æåï¼æ§è¡åè°ï¼æ¿å°æå åæ件路å¾ï¼ç¶å读åæ件信æ¯è·åæ件大å°ï¼ç¶åå®ä¹ä¸äºé»è¾compiler.hooks.done.tap('BundleSizePlugin',stats=>{ const{ path,filename}=stats.compilation.outputOptionsconstbundlePath=resolve(path,filename)const{ size}=fs.statSync(bundlePath)constbundleSize=size/if(bundleSize<sizeLimit){ console.log('safe:bundle-size',bundleSize,'\nsizelimit:',sizeLimit)}else{ console.warn('unsafe:bundle-size',bundleSize,'\nsizelimit:',sizeLimit)}})}}module.exports=BundleSizeWebpackPluginæ¬ç« å°è¿éå°±ç»æäºï¼æ们å¼å§ä»ç»äºwebpackçæ ¸å¿æ¦å¿µï¼æäºå¯¹webpackçåºæ¬é ç½®çäºè§£ï¼æ¥çå©ç¨css-loaderåstyle-loader对webpackçloaderæºå¶è¿è¡äºè¯¦ç»åæï¼æåï¼å¯¹webpackpluginçå·¥ä½æºå¶åæµç¨è¿è¡äºæ¢³çï¼å¹¶æåäºä¸¤ä¸ªpluginï¼è®©ä½ 对pluginä¸åè§å¾é¥ä¸å¯åãæäºè¿äºåç½®ç¥è¯ï¼å°±å¯ä»¥å¯¹æ们åç项ç®è¿è¡å·¥ç¨åçæ¹é äºãæå¾ ä½ çå¦ä¹ ã
åæï¼/post/2024-11-26 21:22
2024-11-26 20:15
2024-11-26 20:02
2024-11-26 19:31
2024-11-26 19:22