Files
rrweb/browser-extension-development-plan.md
xugp 71438691b3
Some checks failed
Tests / Tests (push) Has been cancelled
ESLint Check / ESLint Check and Report Upload (push) Has been cancelled
Prettier Check / Format Check (push) Has been cancelled
Prettier Check / Format Code (push) Has been cancelled
ESLint Check / Build Base for Bundle Size Comparison (push) Has been cancelled
feat: enhance web extension with export functionality and utility improvements
- Add export functionality to SessionList and Player pages
- Add new utility modules: dataOperations, format, path, settings
- Update manifest with export and download permissions
- Enhance storage utility with new data operations
- Add various test scripts and documentation files
2026-04-16 10:44:50 +08:00

715 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 `
<!DOCTYPE html>
<html>
<head>
<title>rrweb 回放</title>
<script src="https://unpkg.com/rrweb@latest/dist/rrweb.umd.cjs"></script>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
#replayer { width: 100%; height: 600px; border: 1px solid #ccc; }
</style>
</head>
<body>
<h1>rrweb 录制回放</h1>
<div id="replayer"></div>
<script>
const replayer = new rrweb.Replayer(${JSON.stringify(events)});
replayer.play();
</script>
</body>
</html>`;
}
}
```
### 3. UI 组件设计
#### Popup Main Component
```html
<!-- popup/index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>rrweb 录制插件</title>
<script src="popup.js" type="module"></script>
<link rel="stylesheet" href="popup.css">
</head>
<body>
<div class="app">
<!-- 状态栏 -->
<div class="status-bar" id="statusBar">
<div class="status-indicator" id="statusIndicator"></div>
<span id="statusText">停止录制</span>
<span id="statsText"></span>
</div>
<!-- 预览区域 -->
<div class="preview-section" id="previewSection">
<div class="preview-container">
<iframe id="previewFrame" sandbox="allow-same-origin"></iframe>
</div>
</div>
<!-- 控制按钮 -->
<div class="controls">
<button id="recordBtn" class="btn btn-primary">
<span class="btn-icon">🎬</span>
<span class="btn-text">开始录制</span>
</button>
<button id="playBtn" class="btn btn-secondary" disabled>
<span class="btn-icon"></span>
<span class="btn-text">播放</span>
</button>
<button id="exportBtn" class="btn btn-secondary" disabled>
<span class="btn-icon">📤</span>
<span class="btn-text">导出</span>
</button>
<button id="deleteBtn" class="btn btn-danger" disabled>
<span class="btn-icon">🗑️</span>
<span class="btn-text">删除</span>
</button>
</div>
<!-- 底部导航 -->
<div class="nav-bar">
<button class="nav-item active" data-page="main">
<span class="nav-icon">📹</span>
<span class="nav-text">主页</span>
</button>
<button class="nav-item" data-page="history">
<span class="nav-icon">📄</span>
<span class="nav-text">历史</span>
</button>
<button class="nav-item" data-page="settings">
<span class="nav-icon">⚙️</span>
<span class="nav-text">设置</span>
</button>
<button class="nav-item" data-page="help">
<span class="nav-icon"></span>
<span class="nav-text">帮助</span>
</button>
</div>
</div>
</body>
</html>
```
#### 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 设计,用户体验良好
- ✅ 技术方案成熟,可扩展性强
- ✅ 分阶段实施,风险可控
**预期成果:**
一个功能完善、界面美观、易于使用的浏览器录制插件。