【前端必会】不知道webpack插件? webpack插件源码分析BannerPlugin
2022-09-29 09:10:12 作者:admin
背景
- 不知道webpack插件是怎么回事,除了官方的文档外,还有一个很直观的方式,就是看源码。
- 看源码是一个挖宝的行动,也是一次冒险,我们可以找一些代码量不是很大的源码
- 比如webpack插件,我们就可以通过BannerPlugin源码,来看下官方是如何实现一个插件的
- 希望对各位同学有所帮助,必要时可以通过源码进行一门技术的学习,加深理解
闲言少叙,直接上代码
https://github.com/webpack/webpack/blob/main/lib/BannerPlugin.js
配合文档api
https://webpack.docschina.org/api/compilation-object/#updateasset
代码分析已添加中文注释
/*MIT License http://www.opensource.org/licenses/mit-license.phpAuthor Tobias Koppers @sokra*/"use strict";const { ConcatSource } = require("webpack-sources");const Compilation = require("./Compilation");const ModuleFilenameHelpers = require("./ModuleFilenameHelpers");const Template = require("./Template");const createSchemaValidation = require("./util/create-schema-validation");/** @typedef {import("../declarations/plugins/BannerPlugin").BannerPluginArgument} BannerPluginArgument *//** @typedef {import("../declarations/plugins/BannerPlugin").BannerPluginOptions} BannerPluginOptions *//** @typedef {import("./Compiler")} Compiler */// 创建一个验证const validate = createSchemaValidation( require("../schemas/plugins/BannerPlugin.check.js"), () => require("../schemas/plugins/BannerPlugin.json"), { name: "Banner Plugin", baseDataPath: "options", });//包装Banner文字const wrapComment = (str) => { if (!str.includes("\n")) { return Template.toComment(str); } return `/*!\n * ${str .replace(/\*\//g, "* /") .split("\n") .join("\n * ") .replace(/\s+\n/g, "\n") .trimRight()}\n */`;};//插件类class BannerPlugin { /** * @param {BannerPluginArgument} options options object * 初始化插件配置 */ constructor(options) { if (typeof options === "string" || typeof options === "function") { options = { banner: options, }; } validate(options); this.options = options; const bannerOption = options.banner; if (typeof bannerOption === "function") { const getBanner = bannerOption; this.banner = this.options.raw ? getBanner : (data) => wrapComment(getBanner(data)); } else { const banner = this.options.raw ? bannerOption : wrapComment(bannerOption); this.banner = () => banner; } } /** * Apply the plugin * @param {Compiler} compiler the compiler instance * @returns {void} * 插件主方法 */ apply(compiler) { const options = this.options; const banner = this.banner; const matchObject = ModuleFilenameHelpers.matchObject.bind( undefined, options ); //创建一个Map,处理如果添加过的文件,不在添加 const cache = new WeakMap(); compiler.hooks.compilation.tap("BannerPlugin", (compilation) => { //处理Assets的hook compilation.hooks.processAssets.tap( { name: "BannerPlugin", //PROCESS_ASSETS_STAGE_ADDITIONS — 为现有的 asset 添加额外的内容,例如 banner 或初始代码。 stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS, }, () => { //遍历当前编译对象的chunks for (const chunk of compilation.chunks) { //如果配置标识只处理入口,但是当前chunk不是入口,直接进入下一次循环 if (options.entryOnly && !chunk.canBeInitial()) { continue; } //否则,遍历chunk下的文件 for (const file of chunk.files) { //根据配置匹配文件是否满足要求,如果不满足,直接进入下一次循环,处理下一个文件 if (!matchObject(file)) { continue; } //否则, const data = { chunk, filename: file, }; //获取插值路径?https://webpack.docschina.org/api/compilation-object/#getpath const comment = compilation.getPath(banner, data); //修改Asset,https://webpack.docschina.org/api/compilation-object/#updateasset compilation.updateAsset(file, (old) => { //从缓存中获取 let cached = cache.get(old); //如果缓存不存在 或者缓存的comment 不等于当前的comment if (!cached || cached.comment !== comment) { //源文件追加到头部或者尾部 const source = options.footer ? new ConcatSource(old, "\n", comment) : new ConcatSource(comment, "\n", old); //创建对象加到缓存 cache.set(old, { source, comment }); //返回修改后的源 return source; } //返回缓存中的源 return cached.source; }); } } } ); }); }}module.exports = BannerPlugin;
总结
- 查看源码,查看源码,查看源码
- WeakMap可以深入了解下,应该是避免对象不释放导致内存问题。
- 插件里用到的很多工具方法可以继续深入,一遍自己开发插件时可以参考
猜你喜欢
联络方式:
400-123-789
邮箱:xiachao@163.com
Q Q:12345678
微信公众号
微信二维码