Chrome扩展录屏:如何在页面导航/刷新后保持录制及注入脚本存活?
实现页面导航/刷新时保持屏幕录制及悬浮按钮存活的方案
核心思路
将录制逻辑脱离内容脚本,迁移到后台Service Worker(扩展的后台持久进程),同时通过全局注册内容脚本实现悬浮按钮在所有页面自动注入,再借助扩展的消息机制同步录制状态。
1. 重构录制逻辑到后台Service Worker
内容脚本会随页面卸载销毁,因此必须把录制的核心逻辑放在后台:
1.1 后台初始化与状态维护
在background.js(Service Worker)中维护全局录制状态:
let mediaRecorder = null; let recordedChunks = []; let currentStream = null; // 监听来自内容脚本的消息 chrome.runtime.onMessage.addListener(async (message, sender, sendResponse) => { switch (message.type) { case 'START_RECORDING': await startRecording(sender.tab.id); sendResponse({ status: 'started' }); break; case 'STOP_RECORDING': const blob = await stopRecording(); sendResponse({ status: 'stopped', blobUrl: URL.createObjectURL(blob) }); break; case 'GET_RECORD_STATUS': sendResponse({ isRecording: !!mediaRecorder?.state === 'recording' }); break; } return true; // 保持消息通道开放,支持异步响应 });
1.2 实现录制启停逻辑
使用chrome.tabCapture捕获当前标签页流(或chrome.desktopCapture捕获屏幕/窗口,适合跨页面不间断录制):
async function startRecording(tabId) { // 停止已有录制 if (mediaRecorder?.state === 'recording') { await stopRecording(); } // 捕获当前标签页的媒体流(若要支持跨页面不间断录制,建议改用desktopCapture捕获窗口/屏幕) currentStream = await chrome.tabCapture.capture({ audio: true, video: true, videoConstraints: { mandatory: { chromeMediaSource: 'tab', maxWidth: window.screen.width, maxHeight: window.screen.height } } }); // 初始化MediaRecorder mediaRecorder = new MediaRecorder(currentStream); recordedChunks = []; mediaRecorder.ondataavailable = (e) => { if (e.data.size > 0) recordedChunks.push(e.data); }; mediaRecorder.onstop = () => { currentStream.getTracks().forEach(track => track.stop()); currentStream = null; }; mediaRecorder.start(); } async function stopRecording() { return new Promise((resolve) => { mediaRecorder.onstop = () => { const blob = new Blob(recordedChunks, { type: 'video/webm' }); resolve(blob); }; mediaRecorder.stop(); }); }
1.3 处理页面导航时的流续接(可选)
如果需要录制标签页的导航过程(而非固定屏幕/窗口),可监听标签页更新事件,在页面加载完成后重新捕获流:
chrome.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => { // 仅当当前正在录制,且页面导航完成时重新捕获流 if (mediaRecorder?.state === 'recording' && changeInfo.status === 'complete') { // 停止旧流 currentStream?.getTracks().forEach(track => track.stop()); // 重新捕获新页面的流 currentStream = await chrome.tabCapture.capture({ audio: true, video: true }); // 替换MediaRecorder的流(注意:部分浏览器不支持动态换流,可考虑多段录制后合并) mediaRecorder.stream = currentStream; } });
2. 实现全局悬浮按钮的持久化注入
通过chrome.scripting.registerContentScripts注册全局内容脚本,确保每个页面加载时自动注入按钮:
2.1 在后台注册内容脚本
在background.js的初始化逻辑中注册:
// 注册全局内容脚本 chrome.runtime.onInstalled.addListener(async () => { await chrome.scripting.registerContentScripts([{ id: 'recorder-button', matches: ['<all_urls>'], // 匹配所有页面 js: ['content.js'], runAt: 'document_start', // 页面加载初期注入 persistAcrossSessions: true }]); });
2.2 内容脚本实现悬浮按钮
在content.js中创建悬浮按钮,并与后台同步状态:
// 创建悬浮按钮 function createRecorderButton() { const button = document.createElement('button'); button.id = 'ext-recorder-btn'; button.style.cssText = ` position: fixed; bottom: 20px; right: 20px; padding: 12px 24px; background: #ff4444; color: white; border: none; border-radius: 24px; font-size: 14px; cursor: pointer; z-index: 999999; box-shadow: 0 2px 8px rgba(0,0,0,0.2); `; // 初始获取录制状态,更新按钮文本 updateButtonStatus(button); // 点击按钮触发录制启停 button.addEventListener('click', async () => { const status = await chrome.runtime.sendMessage({ type: 'GET_RECORD_STATUS' }); if (status.isRecording) { const result = await chrome.runtime.sendMessage({ type: 'STOP_RECORDING' }); // 下载录制文件 const a = document.createElement('a'); a.href = result.blobUrl; a.download = `recording-${Date.now()}.webm`; a.click(); URL.revokeObjectURL(result.blobUrl); } else { await chrome.runtime.sendMessage({ type: 'START_RECORDING' }); } updateButtonStatus(button); }); document.body.appendChild(button); } // 更新按钮文本与样式 async function updateButtonStatus(button) { const status = await chrome.runtime.sendMessage({ type: 'GET_RECORD_STATUS' }); if (status.isRecording) { button.textContent = '停止录制'; button.style.background = '#cc0000'; } else { button.textContent = '开始录制'; button.style.background = '#ff4444'; } } // 页面加载完成后创建按钮(避免document.body未就绪) if (document.readyState === 'complete') { createRecorderButton(); } else { window.addEventListener('load', createRecorderButton); }
3. 配置manifest.json权限
确保扩展声明所需权限:
{ "manifest_version": 3, "name": "屏幕录制工具", "version": "1.0", "background": { "service_worker": "background.js" }, "permissions": [ "scripting", "tabs", "tabCapture" ], "action": { "default_popup": "popup.html" } }
关键说明
- 录制逻辑后台化:Service Worker不受页面生命周期影响,确保录制过程不中断。
- 全局内容脚本注册:通过
registerContentScripts实现悬浮按钮在所有页面自动注入,无需手动触发。 - 状态同步:借助
chrome.runtime.sendMessage实现内容脚本与后台的实时状态交互,保证按钮显示与实际录制状态一致。 - 跨页面录制可选方案:若需要完全不间断的录制(如跨标签页导航),建议改用
chrome.desktopCapture让用户选择录制整个屏幕或浏览器窗口,而非单个标签页。
内容的提问来源于stack exchange,提问作者Aviato
相关产品推荐
相关产品推荐

