背景

  1. 不知道webpack插件是怎么回事,除了官方的文档外,还有一个很直观的方式,就是看源码。
  2. 看源码是一个挖宝的行动,也是一次冒险,我们可以找一些代码量不是很大的源码
  3. 比如webpack插件,我们就可以通过BannerPlugin源码,来看下官方是如何实现一个插件的
  4. 希望对各位同学有所帮助,必要时可以通过源码进行一门技术的学习,加深理解

闲言少叙,直接上代码

https://github.com/webpack/webpack/blob/main/lib/BannerPlugin.js

配合文档api

https://webpack.docschina.org/api/compilation-object/#updateasset

代码分析已添加中文注释

  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { ConcatSource } = require("webpack-sources");
  7. const Compilation = require("./Compilation");
  8. const ModuleFilenameHelpers = require("./ModuleFilenameHelpers");
  9. const Template = require("./Template");
  10. const createSchemaValidation = require("./util/create-schema-validation");
  11. /** @typedef {import("../declarations/plugins/BannerPlugin").BannerPluginArgument} BannerPluginArgument */
  12. /** @typedef {import("../declarations/plugins/BannerPlugin").BannerPluginOptions} BannerPluginOptions */
  13. /** @typedef {import("./Compiler")} Compiler */
  14. // 创建一个验证
  15. const validate = createSchemaValidation(
  16. require("../schemas/plugins/BannerPlugin.check.js"),
  17. () => require("../schemas/plugins/BannerPlugin.json"),
  18. {
  19. name: "Banner Plugin",
  20. baseDataPath: "options",
  21. }
  22. );
  23. //包装Banner文字
  24. const wrapComment = (str) => {
  25. if (!str.includes("\n")) {
  26. return Template.toComment(str);
  27. }
  28. return `/*!\n * ${str
  29. .replace(/\*\//g, "* /")
  30. .split("\n")
  31. .join("\n * ")
  32. .replace(/\s+\n/g, "\n")
  33. .trimRight()}\n */`;
  34. };
  35. //插件类
  36. class BannerPlugin {
  37. /**
  38. * @param {BannerPluginArgument} options options object
  39. * 初始化插件配置
  40. */
  41. constructor(options) {
  42. if (typeof options === "string" || typeof options === "function") {
  43. options = {
  44. banner: options,
  45. };
  46. }
  47. validate(options);
  48. this.options = options;
  49. const bannerOption = options.banner;
  50. if (typeof bannerOption === "function") {
  51. const getBanner = bannerOption;
  52. this.banner = this.options.raw
  53. ? getBanner
  54. : (data) => wrapComment(getBanner(data));
  55. } else {
  56. const banner = this.options.raw
  57. ? bannerOption
  58. : wrapComment(bannerOption);
  59. this.banner = () => banner;
  60. }
  61. }
  62. /**
  63. * Apply the plugin
  64. * @param {Compiler} compiler the compiler instance
  65. * @returns {void}
  66. * 插件主方法
  67. */
  68. apply(compiler) {
  69. const options = this.options;
  70. const banner = this.banner;
  71. const matchObject = ModuleFilenameHelpers.matchObject.bind(
  72. undefined,
  73. options
  74. );
  75. //创建一个Map,处理如果添加过的文件,不在添加
  76. const cache = new WeakMap();
  77. compiler.hooks.compilation.tap("BannerPlugin", (compilation) => {
  78. //处理Assets的hook
  79. compilation.hooks.processAssets.tap(
  80. {
  81. name: "BannerPlugin",
  82. //PROCESS_ASSETS_STAGE_ADDITIONS — 为现有的 asset 添加额外的内容,例如 banner 或初始代码。
  83. stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS,
  84. },
  85. () => {
  86. //遍历当前编译对象的chunks
  87. for (const chunk of compilation.chunks) {
  88. //如果配置标识只处理入口,但是当前chunk不是入口,直接进入下一次循环
  89. if (options.entryOnly && !chunk.canBeInitial()) {
  90. continue;
  91. }
  92. //否则,遍历chunk下的文件
  93. for (const file of chunk.files) {
  94. //根据配置匹配文件是否满足要求,如果不满足,直接进入下一次循环,处理下一个文件
  95. if (!matchObject(file)) {
  96. continue;
  97. }
  98. //否则,
  99. const data = {
  100. chunk,
  101. filename: file,
  102. };
  103. //获取插值路径?https://webpack.docschina.org/api/compilation-object/#getpath
  104. const comment = compilation.getPath(banner, data);
  105. //修改Asset,https://webpack.docschina.org/api/compilation-object/#updateasset
  106. compilation.updateAsset(file, (old) => {
  107. //从缓存中获取
  108. let cached = cache.get(old);
  109. //如果缓存不存在 或者缓存的comment 不等于当前的comment
  110. if (!cached || cached.comment !== comment) {
  111. //源文件追加到头部或者尾部
  112. const source = options.footer
  113. ? new ConcatSource(old, "\n", comment)
  114. : new ConcatSource(comment, "\n", old);
  115. //创建对象加到缓存
  116. cache.set(old, { source, comment });
  117. //返回修改后的源
  118. return source;
  119. }
  120. //返回缓存中的源
  121. return cached.source;
  122. });
  123. }
  124. }
  125. }
  126. );
  127. });
  128. }
  129. }
  130. module.exports = BannerPlugin;

总结

  1. 查看源码,查看源码,查看源码
  2. WeakMap可以深入了解下,应该是避免对象不释放导致内存问题。
  3. 插件里用到的很多工具方法可以继续深入,一遍自己开发插件时可以参考

【前端必会】不知道webpack插件? webpack插件源码分析BannerPlugin的更多相关文章

  1. Android Small插件化框架源码分析

    Android Small插件化框架源码分析 目录 概述 Small如何使用 插件加载流程 待改进的地方 一.概述 Small是一个写得非常简洁的插件化框架,工程源码位置:https://github ...

  2. Unity时钟定时器插件——Vision Timer源码分析之二

      Unity时钟定时器插件——Vision Timer源码分析之二 By D.S.Qiu 尊重他人的劳动,支持原创,转载请注明出处:http.dsqiu.iteye.com 前面的已经介绍了vp_T ...

  3. Mybatis 插件使用及源码分析

    Mybatis 插件 Mybatis插件主要是通过JDK动态代理实现的,插件可以针对接口中的方法进行代理增强,在Mybatis中比较重要的接口如下: Executor :sql执行器,包含多个实现类, ...

  4. Unity时钟定时器插件——Vision Timer源码分析之一

    因为项目中,UI的所有模块都没有MonBehaviour类(纯粹的C#类),只有像NGUI的基本组件的类是继承MonoBehaviour.因为没有继承MonoBehaviour,这也不能使用Updat ...

  5. Jenkins插件hyper slaves源码分析

    1.public class HyperSlaves extends Plugin implements Describable<HyperSlaves> (1).init():初始化co ...

  6. 插件开发之360 DroidPlugin源码分析(五)Service预注册占坑

    请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52264977 在了解系统的activity,service,broa ...

  7. Kettle 4.2源码分析第四讲--KettleJob机制与Database插件简介(含讲解PPT)

    1.  Job机制 一个job项代表ETL控制流中的一项逻辑任务.Job项将会顺序执行,每个job项会产生一个结果,能作为别的分支上job项的条件. 图 1 job项示例 1.1. Job类图简介 图 ...

  8. 插件开发之360 DroidPlugin源码分析(四)Activity预注册占坑

    请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52258434 在了解系统的activity,service,broa ...

  9. 插件开发之360 DroidPlugin源码分析(二)Hook机制

    转载请注明出处:http://blog.csdn.net/hejjunlin/article/details/52124397 前言:新插件的开发,可以说是为插件开发者带来了福音,虽然还很多坑要填补, ...

随机推荐

  1. 倍增求RMQ

    RMQ,即区间最值查询,给定一个序列,求区间l-r的最大值.最小值. st表求RMQ,预处理On*logn,查询O1. 预处理: void init_rmq() { for(rll j=1;j< ...

  2. grafana监控配置

    一.配置开启smtp服务 1.编辑grafana配置文件grafana.ini [smtp] enabled = true host = smtp.163.com:25 user = 157xxxx3 ...

  3. 基于WPF重复造轮子,写一款数据库文档管理工具(一)

    项目背景 公司业务历史悠久且复杂,数据库的表更是多而繁杂,每次基于老业务做功能开发都需要去翻以前的表和业务代码.需要理解旧的表的用途以及包含的字段的含义,表少还好说,但是表一多这就很浪费时间,而且留下 ...

  4. ExcelPatternTool: Excel表格-数据库互导工具

    ExcelPatternTool Excel表格-数据库互导工具 介绍: 指定Pattern文件-一个规则描述的json文档,基于此规则实现Excel表格与数据库之间的导入导出,校验等功能. 特点: ...

  5. 基于ABP和Magicodes实现Excel导出操作

      前端使用的vue-element-admin框架,后端使用ABP框架,Excel导出使用的Magicodes.IE.Excel.Abp库.Excel导入和导出操作几乎一样,不再介绍.文本主要介绍E ...

  6. Taurus.MVC WebAPI 入门开发教程8:WebAPI文档与自动化测试。

    系列目录 1.Taurus.MVC WebAPI  入门开发教程1:框架下载环境配置与运行. 2.Taurus.MVC WebAPI 入门开发教程2:添加控制器输出Hello World. 3.Tau ...

  7. MySQL数据库授权的两种方式

    方法一:grant命令创建用户并授权(针对只修改权限) grant命令简单语法如下: grant all privileges on dbname.* to username@localhost id ...

  8. dentry的引用计数不对导致的crash

    [17528853.189372] python invoked oom-killer: gfp_mask=0xd0, order=0, oom_score_adj=-998[17528853.189 ...

  9. QQ高级功能

    本篇文章为微信公众号:酿俗 教学内容请跟着小编一起探索吧! 第一步解锁微信豆影藏内容 随后下载需要的材料,注意!手机可能会提示有病毒!这里使用这些功能报病毒很正常 其实并没有病毒只是手机厂商的安全系统 ...

  10. 第七十篇:Vue组件的使用

    好家伙, 1.vue的组件化开发 1.1.什么是组件? 组件是对UI结构的复用, vue是一个支持组件化开发的前端框架, vue中规定:组件的后缀名是.vue 例如:App.vue文件本质上就是一个v ...