PageSpy 对无界子应用内容录制的实现,即 iframe实现思路

前言

本文只记录了 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 这个插件实现的数据信息收集

所以我们需要在无界子应用内部将pageSpydata-harbor 重新初始化一遍

实现一个 wujie 插件

这里子应用只做了一层嵌套,即: 主应用 > 子应用;

不考虑多层嵌套的情况,即:主应用 > 子应用1 > 子应用2
iframe 的录制思路相同,在 iframe 内部页面加载之前进行脚本注入即可

  1. 在无界子应用的 jsBeforeLoaders 钩子中将pageSpy插件注入
  2. 自定义 harboronDownload 方法,即:使用主应用的统一下载回调
  3. 将子应用的 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,
});
相关推荐
我是若尘4 分钟前
我的需求代码被主干 revert 了,接下来我该怎么操作?
前端·后端·代码规范
魁首25 分钟前
Claude Code 源码泄露的背后,到底与Codex,Gemini 有啥不一样?
前端·openai·claude
攀登的牵牛花27 分钟前
程序员失业论,被 SWE-CI 一组数据打醒:真正先被替代的是低质量交付
前端·github
BumBle1 小时前
Vue项目中实现路由守卫自动取消Pending请求
前端
gCode Teacher 格码致知1 小时前
Javascript提高:get和post等请求,对于汉字和空格信息进行编码的原则-由Deepseek产生
开发语言·前端·javascript·node.js·jquery
竹林8181 小时前
从ethers.js迁移到Viem:我在一个DeFi项目前端重构中踩过的坑
前端·javascript
像我这样帅的人丶你还1 小时前
从交稿到甩锅预防:AI 前端流水线
前端·ai编程
想想弹幕会怎么做1 小时前
如何构建一颗可交互的ui树?
前端
程序员陆业聪2 小时前
我见过的最反直觉的 Android 架构问题:UseCase 越多,项目越烂
前端
Arya_aa2 小时前
网络:前端向后端发送网络请求渲染在页面上,将EasyMock中的信息用前端vue框架编写代码,最终展示在浏览器
前端·vue.js