Chrome扩展开发实战:打造你的GPT助手
在当今AI快速发展的时代,GPT等大语言模型已经成为开发者的得力助手。如何将这些强大的AI能力无缝集成到我们的日常工作流程中?本文将介绍如何使用现代前端技术栈开发一个Chrome扩展,让GPT的能力触手可及。
GPT Helper是一个Chrome扩展,它允许用户在浏览网页时随时调用GPT进行交互,用户可以选择文本并让GPT进行解释、翻译或代码分析等操作,并集成必应搜索功能。
点击查看GitHub源码
功能示意图如下
-
鼠标左键划词:
-
解释选中文案
-
侧边栏可集成任意网址
技术栈概览
前端技术
- 框架:React + TypeScript
- 构建工具:Vite
- 样式预处理器:Less
- 浏览器API:Chrome Extension MV3
后端服务
- AI引擎:GPT-3.5 Turbo API
- 搜索集成:Bing Search API
- 通信协议:WebSocket
项目结构解析
bash
.
├── src/
│ ├── content-scripts/ # 内容脚本核心逻辑
│ │ ├── components/ # React组件
│ │ │ ├── GptModal/ # GPT交互弹窗
│ │ │ └── Menu/ # 右键菜单组件
│ │ ├── App.tsx # 主应用入口
│ │ └── content.css # 全局样式
│ ├── assets/ # 静态资源
│ ├── manifest.json # 扩展清单配置
│ └── popup.html # 扩展弹出页
├── vite-plugin/ # 自定义Vite插件
│ └── src/
│ ├── client/ # WebSocket客户端
│ └── index.ts # 插件主逻辑
├── package.json
└── vite.config.ts
开发Chrome扩展的关键点
1. Manifest V3配置
Chrome扩展的核心是manifest.json
文件,它定义了扩展的权限、资源和行为。在Manifest V3中,安全性和性能得到了加强,但也带来了一些开发上的变化。
2. 内容脚本与背景脚本
- 内容脚本(Content Scripts):直接在网页上下文中运行,可以读取和修改DOM
- 背景脚本(Background Scripts):在扩展的后台持续运行,管理全局状态和事件
3. 使用Vite构建Chrome扩展
传统的Chrome扩展开发可能使用webpack或原生JS,但使用Vite可以带来更好的开发体验:
- 更快的热重载
- 现代化的模块打包
- TypeScript和React的无缝支持
我们使用了自定义的vite-plugin-crx-mv3
插件来简化Chrome扩展的构建过程。
javascript
// vite.config.ts示例
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { crx } from 'vite-plugin-crx-mv3';
export default defineConfig({
plugins: [
react(),
crx({ manifest: './src/manifest.json' })
]
});
实现GPT交互功能
1. 划词菜单实现
当用户选中文本并右键点击时,我们需要显示自定义菜单选项:
typescript
// 简化的菜单组件示例
const Menu = ({ position, onSelect }) => {
const options = [
{ key: 'explain', label: '解释选中内容' },
{ key: 'translate', label: '翻译选中内容' },
{ key: 'code', label: '分析代码' }
];
return (
<div className="gpt-menu" style={{ top: position.y, left: position.x }}>
{options.map(option => (
<div
key={option.key}
className="menu-item"
onClick={() => onSelect(option.key)}
>
{option.label}
</div>
))}
</div>
);
};
2. GPT API调用
选择操作后,我们需要调用GPT API并展示结果:
typescript
// 简化的API调用示例
const callGptApi = async (content, type) => {
try {
//需要填写自己的gpt接口
const response = await fetch('you gpt url', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_KEY}` //需要填写自己的key
},
body: JSON.stringify({
model: 'gpt-3.5-turbo',
messages: [
{ role: 'system', content: getPromptByType(type) },
{ role: 'user', content }
]
})
});
const data = await response.json();
return data.choices[0].message.content;
} catch (error) {
console.error('GPT API调用失败:', error);
return '抱歉,请求失败,请稍后再试。';
}
};
3. 结果展示
使用可拖拽和可调整大小的模态框展示GPT的回答:
typescript
// App.tsx中的状态管理示例
const [gptContentVisible, setGptContentVisible] = useState(false);
const [gptContent, setGptContent] = useState('');
const [curGptType, setCurGptType] = useState('');
const [bingAnswerList, setBingAnswerList] = useState([]);
const updateGptModal = (visible: boolean) => {
setGptContentVisible(visible);
}
const updateGptConten = (content: string) => {
setGptContent(content);
}
const updateGptType = (type: string) => {
setCurGptType(type);
}
const updateBing = (list: any) => {
setBingAnswerList(list);
}
开发过程中的技术挑战与解决方案
1. 内容脚本与扩展通信
Chrome扩展的不同部分(内容脚本、背景脚本、弹出页面)之间的通信是一个常见挑战。我们使用Chrome提供的消息传递API来解决:
typescript
// 发送消息
chrome.runtime.sendMessage({ type: 'GPT_QUERY', data: { text, queryType } });
// 接收消息
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'GPT_RESPONSE') {
// 处理GPT响应
updateGptConten(message.data.content);
setGptContentVisible(true);
}
return true;
});
2. 开发环境的热重载
Chrome扩展开发中,每次修改代码后都需要重新加载扩展,这大大降低了开发效率。我们使用WebSocket实现了热重载功能:
typescript
// vite-plugin-crx-mv3中的热重载实现
export async function emitDevScript(
context: PluginContext,
port: number,
manifestContext,
reloadPage: boolean
): Promise<Record<string, any>> {
// ...
let code = `var PORT=${port},MENIFEST_NAME='${manifest.name}',RELOADPAGE=${reloadPage};`
let contentScriptDevPath = normalizePathResolve(
__dirname,
'client/content.js'
)
let content = await getContentFromCache(
context.cache,
contentScriptDevPath,
'utf8'
)
context.emitFile({
type: 'asset',
source: code + content,
fileName: CONTENT_SCRIPT_DEV_PATH
})
// ...
}
3. Markdown渲染
GPT返回的内容通常包含Markdown格式,我们需要将其渲染为HTML:
typescript
// 使用marked和highlight.js渲染Markdown
import { marked } from 'marked';
import hljs from 'highlight.js';
marked.setOptions({
highlight: function(code, lang) {
if (lang && hljs.getLanguage(lang)) {
return hljs.highlight(code, { language: lang }).value;
}
return hljs.highlightAuto(code).value;
}
});
const renderMarkdown = (content) => {
return { __html: marked(content) };
};
// 在组件中使用
<div
className="markdown-content"
dangerouslySetInnerHTML={renderMarkdown(gptContent)}
/>
部署与发布
Chrome扩展的发布流程相对简单:
- 使用生产模式构建扩展:
npm run build
- 打包生成的
dist
目录为zip文件 - 在Chrome Web Store开发者控制台上传并提交审核
- 审核通过后,扩展将在Chrome Web Store上线