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;
}
相关推荐
m0_7482356110 分钟前
从零开始学前端之HTML(三)
前端·html
一个处女座的程序猿O(∩_∩)O2 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
hackeroink5 小时前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
迷雾漫步者7 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-7 小时前
验证码机制
前端·后端
燃先生._.8 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖9 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_748235249 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_7482402510 小时前
前端如何检测用户登录状态是否过期
前端