rollup 插件架构-装饰器模式增添插件性能分析

文章目录

输入 rollup 配置

  • 初始化计时器,构建完成时输出每个阶段的耗时、内存占用等信息,会 wrapper 相应 hook 方法,添加计时相关功能
java 复制代码
initialiseTimers(inputOptions); 

根据用户配置开启插件性能分析

javascript 复制代码
export function initialiseTimers(inputOptions: NormalizedInputOptions): void {
	if (inputOptions.perf) { // 开启插件性能分析
		timers = new Map();
		timeStart = timeStartImpl;
		timeEnd = timeEndImpl;
		inputOptions.plugins = inputOptions.plugins!.map(getPluginWithTimers);
	} else {
		timeStart = NOOP;
		timeEnd = NOOP;
	}
}

性能分析函数实现

  • 通过 node 相关 Api 获取堆栈信息,process.memoryUsage()
javascript 复制代码
let timers = new Map<string, Timer>();

// 记录开始
function timeStartImpl(label: string, level = 3): void {
	label = getPersistedLabel(label, level);

	const startMemory = process.memoryUsage().heapUsed;
	const startTime = performance.now();

	const timer = timers.get(label);

	if (timer === undefined) { // 初始化创建对应标签
		timers.set(label, {
			memory: 0,
			startMemory,
			startTime,
			time: 0,
			totalMemory: 0
		});
	} else {
		timer.startMemory = startMemory;
		timer.startTime = startTime;
	}
}

// 记录结束
function timeEndImpl(label: string, level = 3): void {
	label = getPersistedLabel(label, level);

	const timer = timers.get(label);

	if (timer !== undefined) {
		const currentMemory = process.memoryUsage().heapUsed;
		timer.memory += currentMemory - timer.startMemory;
		timer.time += performance.now() - timer.startTime;
		timer.totalMemory = Math.max(timer.totalMemory, currentMemory);
	}
}

分级输出结果

  • 根据 level 控制台输出对应格式信息
javascript 复制代码
function getPersistedLabel(label: string, level: number): string {
	switch (level) {
		case 1: {
			return `# ${label}`;
		}
		case 2: {
			return `## ${label}`;
		}
		case 3: {
			return label;
		}
		default: {
			return `${'  '.repeat(level - 4)}- ${label}`;
		}
	}
}

装饰器模式拓展组件

  • 枚举需要性能分析的插件 hookName ,添加timeStart、timeEnd 分析功能
javascript 复制代码
export function initialiseTimers(inputOptions: NormalizedInputOptions): void {
	if (inputOptions.perf) { // 是否开启性能分析
		timers = new Map();
		timeStart = timeStartImpl; // 记录开始
		timeEnd = timeEndImpl; // 记录结束
		inputOptions.plugins = inputOptions.plugins!.map(getPluginWithTimers);
	} else {
		timeStart = NOOP;
		timeEnd = NOOP;
	}
}
javascript 复制代码
const TIMED_PLUGIN_HOOKS: readonly (keyof PluginHooks)[] = [
	'augmentChunkHash',
	'buildEnd',
	'buildStart',
	'...',
	'writeBundle'
];

function getPluginWithTimers(plugin: any, index: number): Plugin {
	// 遍历需要分析的组件
	for (const hook of TIMED_PLUGIN_HOOKS) {
		if (hook in plugin) {
			let timerLabel = `plugin ${index}`;
			if (plugin.name) {
				timerLabel += ` (${plugin.name})`;
			}
			timerLabel += ` - ${hook}`; // 设置对应标签名
			
			// 在调用原插件方法前后增加性能分析
			const handler = function (this: any, ...parameters: readonly unknown[]) {
				timeStart(timerLabel, 4);
				const result = hookFunction.apply(this, parameters);
				timeEnd(timerLabel, 4);
				return result;
			};

			let hookFunction: any;
			
			// 格式化插件配置
			if (typeof plugin[hook].handler === 'function') {
				hookFunction = plugin[hook].handler;
				plugin[hook].handler = handler;
			} else {
				hookFunction = plugin[hook];
				plugin[hook] = handler;
			}
		}
	}
	return plugin;
}
相关推荐
Autumn_ing7 小时前
Design.md生成UI怎么验收?举例运营后台的颜色、状态和数据核对
前端·ui·ai·设计系统·paico·ai生成ui·design.md
远离UE47 小时前
UE5 Substrate Thin Film 源码拆析
开发语言·javascript·ue5
keyipatience8 小时前
JavaScript学习(1)书写位置,变量、数据类型、运算符
开发语言·前端·javascript·css·学习
朱涛的自习室8 小时前
我给 Gemini 4 和 DeepSeek 搞了套「末位淘汰」,AI 干活会更猛吗?(Day 3)
android·前端·后端
java1234_小锋10 小时前
【技术专题】HTML5 网页技术 - HTML5简介 & HTML5 文档结构
前端·html·html5
鶴哥只手遮天11 小时前
Vue 3 核心源码深度剖析系列(v3.5.44)-第 1 章 项目全景:monorepo 架构与工程化体系
前端·vue.js·开源
流星。。方11 小时前
小团队官网SEO落地:前端语义化与静态生成的工程实践
前端·状态模式
用户5304297657911 小时前
盘点危化品搬运诊断的6类工具与自处理边界判断
前端
curtain11 小时前
从古法编程到 Agent:我入行这两年,AI 改变了什么
前端·openai
zyfts11 小时前
10分钟自制Codex钉钉通知Skill:AI干完活自动钉钉提醒你
前端