# rrweb 浏览器插件开发计划 ## 📋 项目概述 基于 rrweb 开发一个功能完善的浏览器插件,支持录制用户操作、本地回放、多格式导出等功能。 --- ## 🎯 开发阶段规划 ### 阶段一:基础录制功能(1-2周) #### 核心功能清单 - ✅ **录制开始/停止** - ✅ **基本数据收集**(事件、时间戳) - ✅ **简单 popup UI**(状态显示、控制按钮) - ✅ **文件保存到默认路径**(自动 JSON 导出) - ✅ **快捷键支持**(Ctrl+Shift+R 开始/停止) #### UI 设计(简化版) ``` ┌─────────────────────────────────────┐ │ 📹 rrweb 录制插件 │ ├─────────────────────────────────────┤ │ 状态: [❌] 停止录制 │ │ │ │ [🎬 开始录制] [⚙️ 设置] │ │ │ │ 快捷键: Ctrl+Shift+R │ │ 帮助 📄 │ └─────────────────────────────────────┘ ``` #### 技术实现 - Background Service 管理录制状态 - Content Script 注入录制脚本 - 基础的数据收集和存储 - 简单的 popup 界面 --- ### 阶段二:UI 增强和回放(2-3周) #### 功能增强 - 🎨 **现代化 UI 设计** - 使用 Tailwind CSS - 添加图标和动画 - 响应式布局 - 🎬 **本地回放功能** - 在 popup 中预览录制 - 基本播放控制(播放/暂停/进度条) - 📊 **录制状态显示** - 实时事件计数 - 录制时长显示 - 文件大小显示 - 🔧 **设置页面** - 基础配置选项 - 隐私控制设置 #### UI 设计(增强版) ``` ┌─────────────────────────────────────┐ │ 📹 rrweb 录制插件 │ ├─────────────────────────────────────┤ │ ● 正在录制 [00:15:23] │ │ 事件数: 1,234 | 文件: 2.3MB │ │ │ │ ┌─────────────────────────────┐ │ │ │ [🎬 实时预览] │ │ │ │ 👆 点击、输入、滚动等操作 │ │ │ └─────────────────────────────┘ │ │ │ │ ┌─────────────────────────────┐ │ │ │ [▶ 播放录制] [⏸ 暂停] │ │ │ │ [📤 导出] [🗑️ 删除] │ │ │ └─────────────────────────────┘ │ │ │ │ 设置 ⚙️ 历史 📄 快捷键 ⌨️ │ └─────────────────────────────────────┘ ``` #### 技术实现 - 现代化 UI 组件设计 - 录制数据实时预览 - 基础回放功能实现 - 设置页面配置管理 --- ### 阶段三:数据管理和导出(1-2周) #### 核心功能 - 💾 **自定义保存路径** - 用户选择文件夹 - 支持自定义文件名格式 - 路径持久化存储 - 📤 **多格式导出** - JSON 原始数据 - HTML 回放页面 - ZIP 压缩包 - 📄 **录制历史管理** - 录制列表显示 - 详情预览 - 批量操作 - 🗑️ **数据清理** - 手动删除 - 自动清理选项 #### 历史管理界面 ``` ┌─────────────────────────────────────┐ 📄 录制历史 │ ├─────────────────────────────────────┤ │ 🔍 搜索: [_________________] │ │ │ │ ┌─────────────────────────────┐ │ │ │ 🎬 product-demo.web.app │ │ │ │ ● 02:35 | 342事件 | 1.2MB │ │ │ │ 2024-01-15 14:30 │ │ │ │ ┌─────────────────────────┐ │ │ │ │ │ [▶ 播放] [📤 导出] [🗑️] │ │ │ │ │ └─────────────────────────┘ │ │ │ │ │ │ │ │ 🎬 login-test.com │ │ │ │ ● 01:15 | 156事件 | 0.8MB │ │ │ │ 2024-01-15 10:20 │ │ │ └─────────────────────────────┘ │ │ │ │ [←] [1/5] [→] 清空全部 🗑️ │ └─────────────────────────────────────┘ ``` #### 技术实现 - 文件路径管理系统 - 多格式导出工具 - 录制历史数据管理 - 用户配置持久化 --- ## 🛠️ 技术架构设计 ### 1. 项目结构 ``` web-extension/ ├── manifest.json # 扩展配置 ├── popup/ # 弹出窗口 │ ├── index.html # popup HTML │ ├── popup.css # 样式文件 │ ├── popup.js # popup 脚本 │ └── components/ # UI 组件 │ ├── StatusIndicator.js │ ├── RecordingPreview.js │ └── ControlButtons.js ├── options/ # 设置页面 │ ├── index.html │ ├── options.css │ ├── options.js │ └── settings.html ├── background/ # 后台服务 │ ├── service-worker.js │ └── recording-manager.js ├── content/ # 内容脚本 │ ├── inject.js # 注入脚本 │ └── enhanced-recorder.js # 增强录制器 ├── utils/ # 工具函数 │ ├── file-manager.js │ ├── export-manager.js │ └── shortcut-manager.js ├── assets/ # 资源文件 │ ├── icons/ │ └── styles/ └── lib/ # 第三方库 └── rrweb-dist/ ``` ### 2. 核心模块实现 #### Background Service Worker ```javascript // background/service-worker.js class RecordingManager { constructor() { this.recordings = new Map(); this.currentRecording = null; } async startRecording(tabId) { try { const recorder = new EnhancedRecorder({ tabId, onData: (events) => this.saveEvents(tabId, events), options: await this.getRecordingOptions() }); this.currentRecording = { tabId, recorder, startTime: Date.now(), events: [], status: 'recording' }; await this.saveToStorage(); // 更新 popup 状态 chrome.runtime.sendMessage({ type: 'RECORDING_STARTED', data: { tabId } }); } catch (error) { console.error('Start recording failed:', error); } } async stopRecording(tabId) { if (!this.currentRecording) return; const recording = this.currentRecording; recording.recorder.stop(); recording.status = 'stopped'; recording.endTime = Date.now(); // 保存文件 await this.saveRecordingFile(recording); this.currentRecording = null; await this.saveToStorage(); chrome.runtime.sendMessage({ type: 'RECORDING_STOPPED', data: { tabId } }); } } ``` #### Enhanced Recorder ```javascript // content/enhanced-recorder.js class EnhancedRecorder { constructor({ tabId, onData, options }) { this.tabId = tabId; this.onData = onData; this.options = options; this.events = []; this.recorder = null; this.init(); } init() { this.recorder = record({ emit: (event) => { this.events.push(event); this.onData(this.events); }, recordCrossOriginIframes: true, blockClass: 'rr-block', maskTextClass: 'rr-mask', ...this.options }); } stop() { if (this.recorder) { this.recorder(); this.recorder = null; } } } ``` #### File Manager ```javascript // utils/file-manager.js class FileManager { constructor() { this.settings = this.loadSettings(); } async saveRecording(data) { const filename = this.generateFilename(); const filepath = await this.getFilePath(filename); // 转换为 JSON const json = JSON.stringify({ version: '1.0', timestamp: data.timestamp, events: data.events, metadata: { duration: data.duration, eventCount: data.events.length, url: data.url } }, null, 2); // 保存文件 await this.writeFile(filepath, json); return { filename, filepath, size: json.length }; } generateFilename() { const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); return `rrweb-${timestamp}.json`; } async getFilePath(filename) { const savePath = this.settings.savePath || this.getDefaultPath(); const fullPath = `${savePath}/${filename}`; // 确保目录存在 await this.ensureDirectory(savePath); return fullPath; } } ``` #### Export Manager ```javascript // utils/export-manager.js class ExportManager { async exportJSON(events, filename) { const data = this.prepareEventData(events); const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }); return this.downloadFile(blob, `${filename}.json`); } async exportHTML(events, filename) { const html = this.generateReplayHTML(events); const blob = new Blob([html], { type: 'text/html' }); return this.downloadFile(blob, `${filename}.html`); } async exportZIP(events, filename) { const JSZip = await import('jszip'); const zip = new JSZip(); // 添加 JSON 数据 const data = this.prepareEventData(events); zip.file('recording.json', JSON.stringify(data, null, 2)); // 添加回放 HTML zip.file('replay.html', this.generateReplayHTML(events)); // 添加 README zip.file('README.md', this.generateReadme(events)); const content = await zip.generateAsync({ type: 'blob' }); return this.downloadFile(content, `${filename}.zip`); } generateReplayHTML(events) { return ` rrweb 回放

rrweb 录制回放

`; } } ``` ### 3. UI 组件设计 #### Popup Main Component ```html rrweb 录制插件
停止录制
``` #### CSS 样式 ```css /* popup.css */ @import 'https://cdn.tailwindcss.com'; :root { --primary-color: #3B82F6; --success-color: #10B981; --danger-color: #EF4444; } .app { width: 400px; height: 600px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; } .status-bar { display: flex; align-items: center; padding: 12px; background: #F3F4F6; border-bottom: 1px solid #E5E7EB; } .status-indicator { width: 8px; height: 8px; border-radius: 50%; margin-right: 8px; background: #9CA3AF; } .status-indicator.recording { background: #EF4444; animation: pulse 2s infinite; } @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } .btn { display: flex; align-items: center; gap: 8px; padding: 12px 20px; border: none; border-radius: 8px; font-weight: 500; cursor: pointer; transition: all 0.2s; } .btn-primary { background: var(--primary-color); color: white; } .btn-primary:hover { background: #2563EB; transform: translateY(-1px); } .btn-secondary { background: #6B7280; color: white; } .btn-secondary:hover { background: #4B5563; } .btn-danger { background: var(--danger-color); color: white; } .btn-danger:hover { background: #DC2626; } .nav-bar { display: flex; border-top: 1px solid #E5E7EB; } .nav-item { flex: 1; display: flex; flex-direction: column; align-items: center; padding: 12px; background: white; border: none; cursor: pointer; transition: background 0.2s; } .nav-item.active { background: #F9FAFB; color: var(--primary-color); } .nav-item:hover { background: #F3F4F6; } ``` --- ## 📊 开发时间估算 | 阶段 | 主要任务 | 预估时间 | 优先级 | |------|---------|---------|-------| | **阶段一** | 基础录制功能 | 1-2周 | 🏆 高 | | | 录制核心逻辑 | 3天 | | | | 基础 UI | 2天 | | | | 快捷键 | 1天 | | | | 文件保存 | 2天 | | | **阶段二** | UI 增强和回放 | 2-3周 | 🥈 中 | | | UI 设计优化 | 4天 | | | | 本地回放 | 5天 | | | | 状态管理 | 3天 | | | | 设置页面 | 3天 | | | **阶段三** | 数据管理和导出 | 1-2周 | 🥉 中 | | | 自定义路径 | 3天 | | | | 多格式导出 | 4天 | | | | 历史管理 | 3天 | | | | 数据清理 | 2天 | | --- ## 🎯 功能特性清单 ### 基础录制功能 - [ ] 开始/停止录制 - [ ] 实时状态显示 - [ ] 事件计数 - [ ] 录制时长 - [ ] 快捷键支持 ### UI 增强功能 - [ ] 现代化界面设计 - [ ] 实时预览窗口 - [ ] 录制控制按钮 - [ ] 状态指示器 - [ ] 响应式布局 ### 回放功能 - [ ] 本地回放 - [ ] 播放/暂停控制 - [ ] 进度条 - [ ] 速度调节 ### 数据管理 - [ ] 自定义保存路径 - [ ] 文件命名规则 - [ ] 录制历史 - [ ] 搜索功能 - [ ] 批量操作 ### 导出功能 - [ ] JSON 格式导出 - [ ] HTML 回放页面 - [ ] ZIP 压缩包 - [ ] 批量导出 - [ ] 云端同步(后续版本) ### 隐私保护 - [ ] 敏感信息遮罩 - [ ] 自定义屏蔽规则 - [ ] 网站白名单 - [ ] 数据加密(后续版本) --- ## 🛠️ 技术栈 | 组件 | 技术选择 | 理由 | |------|---------|------| | UI Framework | Tailwind CSS + Alpine.js | 快速开发,美观 | | State Management | Zustand/Redux | 轻量级状态管理 | | File System | File System API | 原生文件访问 | | Storage | IndexedDB + Chrome Storage | 大数据存储 | | Icons | Lucide Icons | 现代化图标库 | | Charts | Chart.js | 数据可视化 | --- ## 🚀 实施计划 ### 第一步:评估现有扩展 - [ ] 测试当前 rrweb web-extension 功能 - [ ] 确认录制质量是否满足需求 - [ ] 分析需要改进的地方 ### 第二步:阶段一实施 - [ ] 实现基础录制功能 - [ ] 创建简单的 popup UI - [ ] 添加文件保存功能 - [ ] 实现快捷键支持 ### 第三步:阶段二实施 - [ ] UI 设计优化 - [ ] 实现本地回放 - [ ] 添加设置页面 - [ ] 完善状态管理 ### 第四步:阶段三实施 - [ ] 实现自定义保存路径 - [ ] 开发多格式导出 - [ ] 创建历史管理界面 - [ ] 添加数据清理功能 --- ## 📝 注意事项 1. **浏览器兼容性** - 支持 Chrome 88+ - 支持 Firefox 75+ - 避免使用不兼容的 API 2. **性能考虑** - 大文件处理优化 - 内存管理 - 录制性能监控 3. **用户隐私** - 明确的录制提示 - 敏感信息保护 - 数据安全存储 4. **用户体验** - 流畅的界面交互 - 清晰的状态反馈 - 易用的操作流程 --- ## 🎉 总结 本计划提供了 rrweb 浏览器插件开发的完整路线图,从基础录制功能到高级的数据管理和导出功能。通过分阶段实施,可以确保每个阶段都有明确的交付成果,同时保持开发的灵活性。 **主要特点:** - ✅ 功能完整,覆盖录制、回放、管理、导出 - ✅ 现代化 UI 设计,用户体验良好 - ✅ 技术方案成熟,可扩展性强 - ✅ 分阶段实施,风险可控 **预期成果:** 一个功能完善、界面美观、易于使用的浏览器录制插件。