前言
本文只记录了 PageSpy 离线日志 的实现方法,在线调试未做记录! PageSpy官网
无法录制的原因
pageSpy是使用的 rrweb 进行的页面 dom 信息录制,rrweb 需要开启 recordIframe
解决页面录制问题
开启 rrweb 的 recordIframe 属性即可
js
window.$rrweb = new window.RRWebPlugin({
recordIframe: true, // 启用 iframe 录制
checkoutEveryNms: 2 * 1000, // 每2秒创建检查点,确保 iframe 状态被记录
});
window.PageSpy.registerPlugin(window.$rrweb);
页面信息有了,但是数据没有被记录
pageSpy是使用的
data-harbor
这个插件实现的数据信息收集
所以我们需要在无界子应用内部将pageSpy
和data-harbor
重新初始化一遍
实现一个 wujie 插件
这里子应用只做了一层嵌套,即: 主应用 > 子应用;
不考虑多层嵌套的情况,即:主应用 > 子应用1 > 子应用2
iframe 的录制思路相同,在 iframe 内部页面加载之前进行脚本注入即可
- 在无界子应用的
jsBeforeLoaders
钩子中将pageSpy
插件注入 - 自定义
harbor
的onDownload
方法,即:使用主应用的统一下载回调 - 将子应用的
window
实例挂载到主应用中
js
export function injectPageSpy() {
// 这里根据 url 中是否携带 DEBUGGER 参数决定是否开启 pageSpy
const isOpenDebugger = location.href.includes('DEBUGGER');
if (!isOpenDebugger) return {};
return {
jsBeforeLoaders: [
{ src: 'https://pagespy.jikejishu.com/page-spy/index.min.js' },
{ src: 'https://pagespy.jikejishu.com/plugin/data-harbor/index.min.js' },
{
callback: (appWindow) => {
appWindow.$harbor = new appWindow.DataHarborPlugin({
onDownload: appWindow.parent.__PageSpy_Log_Download__,
});
appWindow.PageSpy.registerPlugin(appWindow.$harbor);
appWindow.$pageSpy = new appWindow.PageSpy({
title: 'Bug 调试',
project: `PageSpy-Wujie-${appWindow.name}`,
autoRender: false,
serializeData: true,
offline: true,
});
appWindow.parent.__WUJIE_CHILD_APP__.push(appWindow);
},
},
],
};
}
主应用中 data-harbor
插件改造
js
// 所有日志信息收集
const PAGE_SPY_USER_DATA_LOG = [];
// wujie 子应用 window 实例集合
window.__WUJIE_CHILD_APP__ = [];
// 全局统一日志下载回调方法
window.__PageSpy_Log_Download__ = (data) => {
PAGE_SPY_USER_DATA_LOG.push(...data);
};
window.$harbor = new window.DataHarborPlugin({
onDownload: async (data) => {
const list = window.__WUJIE_CHILD_APP__.map((instance) => instance.$harbor.download());
try {
await Promise.all(list);
PAGE_SPY_USER_DATA_LOG.push(...data);
// TODO: 下载 PAGE_SPY_USER_DATA_LOG 日志数据
// Dowload PAGE_SPY_USER_DATA_LOG
} catch (err) {
console.log(err);
}
},
});
window.PageSpy.registerPlugin(window.$harbor);
主应用完整代码实现
js
function useScript(src) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.async = true;
script.defer = true;
script.src = src;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
function downloadPageSpyData(data) {
const jsonData = data.sort((a, b) => a.timestamp - b.timestamp);
const blob = new Blob([JSON.stringify(jsonData)], { type: 'application/json', encoding: 'utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `debug-data-${Date.now()}.json`;
a.click();
URL.revokeObjectURL(url);
}
async function openDebugger() {
try {
await useScript('https://pagespy.jikejishu.com/page-spy/index.min.js');
await useScript('https://pagespy.jikejishu.com/plugin/data-harbor/index.min.js');
await useScript('https://pagespy.jikejishu.com/plugin/rrweb/index.min.js');
const PAGE_SPY_USER_DATA_LOG = [];
window.__WUJIE_CHILD_APP__ = [];
window.__PageSpy_Log_Download__ = (data) => {
PAGE_SPY_USER_DATA_LOG.push(...data);
};
window.$harbor = new window.DataHarborPlugin({
onDownload: async (data) => {
const list = window.__WUJIE_CHILD_APP__.map((instance) => instance.$harbor.download());
try {
await Promise.all(list);
PAGE_SPY_USER_DATA_LOG.push(...data);
downloadPageSpyData(PAGE_SPY_USER_DATA_LOG);
} catch (err) {
console.log(err);
}
},
});
window.PageSpy.registerPlugin(window.$harbor);
window.$rrweb = new window.RRWebPlugin({
recordIframe: true, // 启用 iframe 录制
checkoutEveryNms: 2 * 1000, // 每3秒创建检查点,确保 iframe 状态被记录
});
window.PageSpy.registerPlugin(window.$rrweb);
window.$pageSpy = new window.PageSpy({
title: 'Bug 调试',
project: 'YIYE',
autoRender: true,
serializeData: true,
offline: true,
modal: {
logo: '',
title: 'YIYE调试器',
},
});
} catch (err) {
console.error('PageSpy init failed:');
console.error(err);
}
}
const isOpenDebugger = location.href.includes('OPEN_DEBUGGER');
if (isOpenDebugger) {
const style = document.createElement('style');
style.innerHTML = `#upload-periods,#page-spy-copy-link { display: none; }`;
document.head.appendChild(style);
openDebugger();
}
无界插件注册
js
import { startApp as rawStartApp } from 'wujie';
const localPlugins = [
/* ...more Plugin */
injectPageSpy()
]
await rawStartApp({
/* ...more wujie config */
plugins: localPlugins,
});