Chrome Extension Boilerplate React Vite:浏览器自动化控制
Chrome Extension Boilerplate React Vite:浏览器自动化控制
你是否还在为手动操作浏览器重复任务而烦恼?本文将带你使用Chrome Extension Boilerplate React Vite构建高效浏览器自动化工具,通过React组件注入、内容脚本和侧边栏面板实现网页数据抓取、自动表单填写等实用功能。读完本文,你将掌握扩展开发全流程,从环境搭建到自动化脚本编写,让浏览器为你高效工作。
项目概述
该项目是基于React、TypeScript和Vite构建的Chrome/Firefox扩展开发模板,通过Turborepo提升构建速度和开发体验。它支持Manifest V3标准,提供热模块重载(HMR)、国际化(i18n)和跨页面状态管理等核心功能,特别适合开发需要与网页内容交互的自动化工具。
项目核心优势:
- 基于React 18和TypeScript的类型安全开发环境
- Vite驱动的极速热重载,开发效率提升300%
- 模块化架构支持内容脚本、侧边栏和弹出窗口等多界面形态
- 内置存储管理模块实现跨页面数据共享
环境准备与安装
开发环境配置
- 克隆项目仓库:
git clone https://gitcode.com/GitHub_Trending/ch/chrome-extension-boilerplate-react-vite
cd chrome-extension-boilerplate-react-vite
- 安装依赖(需Node.js 16+和pnpm):
npm install -g pnpm
pnpm install
- 启动开发服务器:
pnpm dev # Chrome开发模式
# 或
pnpm dev:firefox # Firefox开发模式
- 加载扩展到浏览器:
- Chrome:访问
chrome://extensions,启用"开发者模式",点击"加载已解压的扩展程序",选择项目中的dist目录 - Firefox:访问
about:debugging#/runtime/this-firefox,点击"临时载入附加组件",选择dist/manifest.json文件
- Chrome:访问
项目结构解析
项目采用Turborepo管理的多包架构,核心代码组织如下:
chrome-extension/ # 扩展核心配置
├── manifest.ts # 扩展清单配置[chrome-extension/manifest.ts](https://link.gitcode.com/i/c10a207e74506274d83d0e771473dbfd)
└── public/ # 静态资源
├── icon-128.png # 扩展图标[chrome-extension/public/icon-128.png](https://link.gitcode.com/i/33bcb709920e9290054b9139c95bc0ab)
└── icon-34.png # 工具栏图标[chrome-extension/public/icon-34.png](https://link.gitcode.com/i/4e8d3a7f27b6e663039cb0fb03978b5a)
pages/ # 扩展界面组件
├── content/ # 内容脚本[pages/content/](https://link.gitcode.com/i/ed1fd0bf201151b7d604b4b7ea25e861)
├── side-panel/ # 侧边栏面板[pages/side-panel/](https://link.gitcode.com/i/77270504ed797302eed48bc327d53005)
└── popup/ # 弹出窗口[pages/popup/](https://link.gitcode.com/i/788fe38b73b3528ea62fb93225093943)
packages/ # 共享功能模块
├── storage/ # 存储管理[packages/storage/](https://link.gitcode.com/i/1a4f3803efa10c4f9e303a8eca6b5727)
├── hmr/ # 热重载插件[packages/hmr/](https://link.gitcode.com/i/d6bc1ff726597ba287d66bf24ffd3166)
└── shared/ # 共享工具函数[packages/shared/](https://link.gitcode.com/i/0ced7388c9c14c1ad6787c34c7087cc6)
自动化控制核心功能实现
内容脚本注入与网页操作
内容脚本是实现网页自动化的核心,它们运行在目标网页上下文中,可直接操作DOM元素。项目中内容脚本组织在pages/content目录,支持按URL匹配注入不同脚本。
基础内容脚本示例
创建pages/content/src/matches/example/index.ts实现自动表单填写:
// 匹配example.com域名的内容脚本
export default function runAutomation() {
// 等待页面加载完成
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initAutomation);
} else {
initAutomation();
}
}
function initAutomation() {
// 自动填写登录表单
const usernameInput = document.querySelector<HTMLInputElement>('input[name="username"]');
const passwordInput = document.querySelector<HTMLInputElement>('input[name="password"]');
const submitButton = document.querySelector<HTMLButtonElement>('button[type="submit"]');
if (usernameInput && passwordInput && submitButton) {
usernameInput.value = 'auto_filled@example.com';
passwordInput.value = 'secure_password_123';
// 3秒后自动提交
setTimeout(() => {
submitButton.click();
console.log('表单已自动提交');
}, 3000);
}
}
配置内容脚本注入规则
修改chrome-extension/manifest.ts配置内容脚本注入规则:
// 在manifest.ts的content_scripts数组中添加
{
matches: ['https://example.com/login*'], // 匹配登录页面
js: ['content/example.iife.js'], // 注入我们的自动化脚本
run_at: 'document_idle' // 文档加载完成后执行
}
侧边栏面板开发
侧边栏提供了与用户交互的界面,可通过它控制自动化流程。项目中pages/side-panel目录包含侧边栏组件,使用React开发。
侧边栏组件示例
修改pages/side-panel/src/SidePanel.tsx添加自动化控制按钮:
import React, { useState } from 'react';
import { useStorage } from '@extension/shared'; // 导入存储钩子
const SidePanel: React.FC = () => {
const [status, setStatus] = useState('idle');
const { getStorage, setStorage } = useStorage();
// 启动自动化任务
const startAutomation = async () => {
setStatus('running');
// 获取当前激活标签页
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
if (tab.id) {
// 向内容脚本发送消息
chrome.tabs.sendMessage(tab.id, {
action: 'start_automation',
config: await getStorage('automation_config')
}, (response) => {
setStatus(response.success ? 'completed' : 'error');
});
}
};
return (
<div className="p-4 w-64">
<h2 className="text-lg font-bold mb-4">网页自动化助手</h2>
<div className="mb-4">
<label className="block mb-2">自动化状态: {status}</label>
<button
onClick={startAutomation}
disabled={status === 'running'}
className="w-full bg-blue-500 text-white py-2 px-4 rounded"
>
{status === 'running' ? '运行中...' : '启动自动化'}
</button>
</div>
{/* 配置保存示例 */}
<div>
<h3 className="font-medium mb-2">自动化配置</h3>
<textarea
className="w-full h-24 p-2 border rounded"
placeholder="输入自动化规则JSON..."
onChange={(e) => {
try {
setStorage('automation_config', JSON.parse(e.target.value));
} catch (err) {
console.error('配置格式错误', err);
}
}}
></textarea>
</div>
</div>
);
};
export default SidePanel;
跨页面通信与状态管理
扩展的不同组件(侧边栏、弹出窗口、背景页)通过Chrome API进行通信,配合存储模块实现状态共享。项目提供的@extension/storage包简化了存储操作:
// 保存数据到本地存储
import { useStorage } from '@extension/shared/lib/hooks/use-storage.tsx';
const { setStorage, getStorage } = useStorage();
// 保存自动化配置
await setStorage('auto_fill_rules', {
usernameSelector: 'input[name="user"]',
passwordSelector: 'input[name="pass"]',
submitSelector: 'button[type="submit"]'
});
// 获取配置
const rules = await getStorage('auto_fill_rules');
背景服务工作线程(chrome-extension/src/background/index.ts)负责协调不同组件间的通信:
// 监听来自内容脚本的消息
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === 'automation_completed') {
// 显示通知
chrome.notifications.create({
type: 'basic',
iconUrl: 'icon-128.png',
title: '自动化完成',
message: `已处理 ${message.count} 条数据`
});
// 记录操作日志
chrome.storage.local.set({
lastAutomation: new Date().toISOString(),
totalActions: message.count
});
sendResponse({ success: true });
}
});
实战案例:网页数据抓取工具
以下是一个完整的自动化数据抓取工具实现,它从电商网站提取产品信息并保存到本地CSV文件。
1. 内容脚本实现数据提取
创建pages/content/src/matches/ecommerce/index.ts:
// 电商网站数据提取脚本
export default function extractProductData() {
// 等待页面加载完成
window.addEventListener('load', () => {
// 检查是否是产品列表页
if (window.location.pathname.includes('/products')) {
extractProductList();
}
// 检查是否是产品详情页
else if (window.location.pathname.includes('/product/')) {
extractProductDetails();
}
});
// 监听来自扩展的消息
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === 'extract_data') {
switch (message.target) {
case 'list':
sendResponse(extractProductList());
break;
case 'details':
sendResponse(extractProductDetails());
break;
default:
sendResponse({ error: '未知目标' });
}
}
});
// 提取产品列表
function extractProductList() {
const products = Array.from(document.querySelectorAll('.product-item')).map(item => ({
title: item.querySelector('.product-title')?.textContent?.trim(),
price: item.querySelector('.product-price')?.textContent?.trim(),
url: item.querySelector('a')?.href,
image: item.querySelector('img')?.src
})).filter(Boolean);
return {
type: 'product_list',
count: products.length,
data: products,
extractedAt: new Date().toISOString()
};
}
// 提取产品详情
function extractProductDetails() {
return {
type: 'product_details',
data: {
title: document.querySelector('h1.product-title')?.textContent?.trim(),
price: document.querySelector('.product-price')?.textContent?.trim(),
description: document.querySelector('.product-description')?.textContent?.trim(),
specifications: Array.from(document.querySelectorAll('.spec-row')).reduce((specs, row) => {
const key = row.querySelector('.spec-name')?.textContent?.trim();
const value = row.querySelector('.spec-value')?.textContent?.trim();
if (key && value) specs[key] = value;
return specs;
}, {}),
images: Array.from(document.querySelectorAll('.product-image img')).map(img => img.src)
},
extractedAt: new Date().toISOString()
};
}
}
2. 侧边栏控制面板
修改pages/side-panel/src/SidePanel.tsx添加数据抓取控制界面:
import React, { useState, useEffect } from 'react';
import { useStorage } from '@extension/shared/lib/hooks/use-storage.tsx';
const SidePanel: React.FC = () => {
const [status, setStatus] = useState('ready');
const [extractedData, setExtractedData] = useState(null);
const [currentTab, setCurrentTab] = useState(null);
const { getStorage, setStorage } = useStorage();
// 获取当前标签页信息
useEffect(() => {
chrome.tabs.query({ active: true, currentWindow: true })
.then(tabs => setCurrentTab(tabs[0]));
}, []);
// 提取列表数据
const extractListData = async () => {
if (!currentTab?.id) return;
setStatus('extracting');
try {
const result = await chrome.tabs.sendMessage(currentTab.id, {
action: 'extract_data',
target: 'list'
});
setExtractedData(result);
setStatus('completed');
// 保存到存储
await setStorage('extracted_data', {
url: currentTab.url,
timestamp: new Date().toISOString(),
data: result
});
// 下载CSV文件
downloadCSV(result.data);
} catch (err) {
setStatus('error');
console.error('提取失败:', err);
}
};
// 导出为CSV文件
const downloadCSV = (products) => {
const headers = ['标题', '价格', 'URL', '图片'];
const rows = [
headers.join(','),
...products.map(p => [
`"${p.title?.replace(/"/g, '""')}"`,
`"${p.price?.replace(/"/g, '""')}"`,
`"${p.url?.replace(/"/g, '""')}"`,
`"${p.image?.replace(/"/g, '""')}"`
].join(','))
];
const csvContent = rows.join('\n');
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `products_${new Date().toISOString().slice(0,10)}.csv`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
};
return (
<div className="p-4 w-72">
<h2 className="text-lg font-bold mb-4">电商数据助手</h2>
<div className="mb-4 p-3 bg-gray-100 rounded">
<p className="text-sm mb-2">当前页面: {currentTab?.title?.slice(0, 30)}...</p>
<p className="text-xs text-gray-500 mb-3">{currentTab?.url?.slice(0, 50)}...</p>
<button
onClick={extractListData}
disabled={status === 'extracting'}
className="w-full bg-green-500 text-white py-2 px-4 rounded disabled:bg-gray-400"
>
{status === 'extracting' ? '提取中...' : '提取产品列表'}
</button>
</div>
{extractedData && (
<div className="mt-4 p-3 bg-green-50 border border-green-200 rounded">
<h3 className="font-medium text-green-700 mb-2">提取结果</h3>
<p className="text-sm">共找到 {extractedData.count} 个产品</p>
<button
onClick={() => downloadCSV(extractedData.data)}
className="mt-2 text-sm text-blue-600"
>
下载CSV文件
</button>
</div>
)}
{status === 'error' && (
<div className="mt-4 p-3 bg-red-50 border border-red-200 rounded text-red-700 text-sm">
提取失败,请确保在支持的电商页面上使用此功能
</div>
)}
</div>
);
};
export default SidePanel;
3. 更新扩展清单
确保chrome-extension/manifest.ts中包含必要的权限和内容脚本配置:
// 在manifest.ts的content_scripts数组中添加
{
matches: ['https://*.example-ecommerce.com/products*'],
js: ['content/ecommerce.iife.js']
}
// 在permissions数组中确保包含
permissions: ['storage', 'scripting', 'tabs', 'downloads', 'notifications'],
host_permissions: ['<all_urls>']
调试与部署
开发调试技巧
-
内容脚本调试:
- Chrome:在开发者工具的"Sources"面板中,展开"Content scripts"找到你的脚本
- 使用
chrome.devtools.inspectedWindow.eval()在页面上下文中执行代码
-
背景页调试:
- Chrome:访问
chrome://extensions,点击扩展的"Service Worker"链接 - Firefox:访问
about:debugging#/runtime/this-firefox,点击"检查"按钮
- Chrome:访问
-
热重载问题排查: 如果热重载失效,尝试重启开发服务器并重新加载扩展:
pnpm dev --force # 强制重新构建
生产构建与发布
- 构建生产版本:
pnpm build # Chrome生产构建
# 或
pnpm build:firefox # Firefox生产构建
- 打包为ZIP文件:
pnpm zip # 生成dist-zip/extension-YYYYMMDD-HHmmss.zip
- 发布到应用商店:
- Chrome:访问Chrome开发者控制台上传ZIP文件
- Firefox:访问Firefox开发者中心提交扩展
高级功能与扩展
自动化任务调度
使用chrome.alarmsAPI实现定时自动化任务:
// 在background/index.ts中添加
chrome.alarms.create('daily-scrape', {
when: Date.now() + 60000, // 1分钟后首次执行
periodInMinutes: 1440 // 每天执行一次
});
chrome.alarms.onAlarm.addListener(alarm => {
if (alarm.name === 'daily-scrape') {
// 打开目标页面并执行自动化
chrome.tabs.create({ url: 'https://example-ecommerce.com/new-products' })
.then(tab => {
// 等待页面加载后注入脚本
setTimeout(() => {
chrome.scripting.executeScript({
target: { tabId: tab.id },
files: ['content/ecommerce.iife.js']
}).then(() => {
chrome.tabs.sendMessage(tab.id, { action: 'auto_scrape_and_save' });
});
}, 5000);
});
}
});
用户配置界面
利用项目提供的Options页面(pages/options/)创建用户配置界面,让用户自定义自动化规则:
// pages/options/src/Options.tsx示例
import React from 'react';
import { useStorage } from '@extension/shared/lib/hooks/use-storage.tsx';
const Options: React.FC = () => {
const { getStorage, setStorage } = useStorage();
const [selectors, setSelectors] = React.useState({
title: 'h1.product-title',
price: '.product-price',
description: '.product-description'
});
React.useEffect(() => {
getStorage('custom_selectors').then(data => {
if (data) setSelectors(data);
});
}, []);
const saveOptions = () => {
setStorage('custom_selectors', selectors);
alert('配置已保存');
};
return (
<div className="p-6 max-w-2xl mx-auto">
<h1 className="text-2xl font-bold mb-6">自动化配置</h1>
<div className="space-y-4">
<div>
<label className="block mb-2">标题选择器</label>
<input
type="text"
value={selectors.title}
onChange={e => setSelectors({...selectors, title: e.target.value})}
className="w-full p-2 border rounded"
/>
</div>
<div>
<label className="block mb-2">价格选择器</label>
<input
type="text"
value={selectors.price}
onChange={e => setSelectors({...selectors, price: e.target.value})}
className="w-full p-2 border rounded"
/>
</div>
<div>
<label className="block mb-2">描述选择器</label>
<input
type="text"
value={selectors.description}
onChange={e => setSelectors({...selectors, description: e.target.value})}
className="w-full p-2 border rounded"
/>
</div>
<button
onClick={saveOptions}
className="bg-blue-500 text-white py-2 px-4 rounded"
>
保存配置
</button>
</div>
</div>
);
};
export default Options;
总结与展望
本文介绍了如何使用Chrome Extension Boilerplate React Vite构建浏览器自动化工具,涵盖了内容脚本开发、跨页面通信、数据存储和用户界面设计等核心技术点。通过这个框架,你可以快速开发出功能强大的浏览器自动化工具,从简单的表单填写到复杂的数据抓取和分析。
项目后续可扩展方向:
- 集成AI功能实现智能页面分析
- 添加多语言支持(利用项目内置的i18n模块packages/i18n/)
- 实现云同步功能,共享自动化规则
掌握这些技术后,你可以开发各种实用工具,如网页监控、自动测试、内容聚合等,极大提升工作效率。立即开始探索,释放浏览器自动化的无限可能!
项目完整文档和更多示例,请参考README.md和各模块详细说明。如有问题,可通过项目的Discord社区获取支持。
更多推荐



所有评论(0)