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;
}
相关推荐
石小石Orz2 分钟前
Three.js + AI:AI 算法生成 3D 萤火虫飞舞效果~
javascript·人工智能·算法
小行星1255 分钟前
前端预览pdf文件流
前端·javascript·vue.js
join86 分钟前
解决vue-pdf的签章不显示问题
javascript·vue.js·pdf
小行星12511 分钟前
前端把dom页面转为pdf文件下载和弹窗预览
前端·javascript·vue.js·pdf
Lysun00121 分钟前
[less] Operation on an invalid type
前端·vue·less·sass·scss
土豆湿28 分钟前
拥抱极简主义前端开发:NoCss.js 引领无 CSS 编程潮流
开发语言·javascript·css
J总裁的小芒果36 分钟前
Vue3 el-table 默认选中 传入的数组
前端·javascript·elementui·typescript
Lei_zhen9639 分钟前
记录一次electron-builder报错ENOENT: no such file or directory, rename xxxx的问题
前端·javascript·electron
辣条小哥哥40 分钟前
electron主进程和渲染进程之间的通信
javascript·electron·ecmascript
咖喱鱼蛋41 分钟前
Electron一些概念理解
前端·javascript·electron