Chrome 插件开发到发布完整指南:从零开始打造 TTS 朗读助手

Chrome 插件开发到发布完整指南:从零开始打造 TTS 朗读助手

本文将从零开始,完整介绍如何开发一个 Chrome 浏览器插件,并发布到 GitHub 供用户下载使用。以 TTS 朗读助手为例,涵盖开发、调试、打包、发布的完整流程。

📋 目录

🎯 项目概述

项目简介

开发一个基于阿里云 TTS 服务的浏览器朗读助手,支持选中文本朗读、播放控制、右键菜单等功能。

功能特性

  • 🎯 智能朗读:选中网页文本,一键朗读
  • 🎨 多种音色:支持多种语音音色选择
  • 🎵 播放控制:支持播放、暂停、停止功能
  • 🎪 视觉反馈:播放时图标动态显示
  • 🖱️ 右键菜单:右键选中文本即可朗读
  • 🔒 安全可靠:基于阿里云 TTS 服务

技术栈

  • 前端:HTML + CSS + JavaScript
  • TTS 服务:阿里云通义千问 TTS
  • 浏览器 API:Chrome Extension API
  • 版本控制:Git + GitHub

🛠️ 开发环境准备

必需工具

  1. Chrome 浏览器:88.0 或更高版本
  2. 代码编辑器:VS Code、Sublime Text 等
  3. Git:版本控制
  4. GitHub 账号:代码托管和发布

目录结构

python 复制代码
xuri-tts-assistant/
├── extension/                    # 插件源码目录
│   ├── manifest.json            # 插件配置文件
│   ├── background.js            # 后台脚本
│   ├── content.js               # 内容脚本
│   ├── popup.html               # 弹窗页面
│   ├── popup.js                 # 弹窗脚本
│   ├── popup.css                # 弹窗样式
│   ├── icon.png                 # 插件图标
│   └── icon-playing.png         # 播放状态图标
├── README.md                    # 项目说明
├── LICENSE                      # 开源许可证
└── xuri-tts-assistant.zip       # 打包文件

🏗️ 插件架构设计

Chrome 插件架构

Chrome 插件由以下几个核心部分组成:

  1. Manifest.json:插件配置文件,定义权限、脚本等
  2. Background Script:后台脚本,处理全局逻辑
  3. Content Script:内容脚本,与网页交互
  4. Popup:弹窗界面,用户操作入口
  5. Icons:插件图标,视觉标识

数据流向

css 复制代码
用户操作 → Popup → Background Script → TTS API → Content Script → 音频播放

💻 核心功能开发

1. 创建 Manifest.json

json 复制代码
{
  "manifest_version": 3,
  "name": "Xuri TTS Assistant",
  "version": "1.0",
  "description": "浏览器朗读助手,支持智能文本朗读",
  "permissions": [
    "storage",
    "contextMenus",
    "notifications",
    "scripting",
    "tabs",
    "activeTab"
  ],
  "host_permissions": ["<all_urls>"],
  "background": {
    "service_worker": "background.js"
  },
  "action": {
    "default_popup": "popup.html"
  },
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content.js"]
    }
  ]
}

关键配置说明

  • manifest_version: 3:使用最新的 Manifest V3
  • permissions:声明需要的权限
  • host_permissions:允许访问的网站
  • content_scripts:注入到网页的脚本

2. 开发 Background Script

javascript 复制代码
// background.js
let isPlaying = false;

// 创建右键菜单
chrome.runtime.onInstalled.addListener(() => {
  chrome.contextMenus.create({
    id: "tts-read-selection",
    title: "朗读选中文本",
    contexts: ["selection"],
  });
});

// 监听右键菜单点击
chrome.contextMenus.onClicked.addListener(async (info, tab) => {
  if (info.menuItemId === "tts-read-selection" && info.selectionText) {
    const text = info.selectionText.trim();
    if (text) {
      try {
        const result = await qwenTTS(text, "Cherry", false);
        if (result.output && result.output.audio) {
          playAudio(result.output.audio.url);
        }
      } catch (error) {
        showNotification("TTS 服务调用失败: " + error.message);
      }
    }
  }
});

// TTS API 调用
async function qwenTTS(text, voice = "Cherry", stream = false) {
  const apiUrl =
    "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation";
  const apiKey = "your-api-key"; // 替换为你的 API Key

  const response = await fetch(apiUrl, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${apiKey}`,
    },
    body: JSON.stringify({
      model: "qwen-tts",
      input: { text, voice, stream },
    }),
  });

  if (!response.ok) {
    throw new Error(`HTTP ${response.status}: ${response.statusText}`);
  }

  return await response.json();
}

// 播放音频
function playAudio(url) {
  chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
    if (tabs.length > 0) {
      chrome.tabs.sendMessage(tabs[0].id, { action: "play-audio", url });
    }
  });
}

// 更新图标状态
function updateIcon(playing) {
  const iconPath = playing ? "icon-playing.png" : "icon.png";
  chrome.action.setIcon({
    path: { 16: iconPath, 32: iconPath, 48: iconPath, 128: iconPath },
  });
  chrome.action.setTitle({
    title: playing ? "朗读助手 - 正在播放" : "朗读助手",
  });
}

3. 开发 Content Script

javascript 复制代码
// content.js
let audio = null;

// 监听来自 background script 的消息
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
  if (msg.action === "play-audio" && msg.url) {
    playAudio(msg.url);
  } else if (msg.action === "pause-audio") {
    pauseAudio();
  } else if (msg.action === "stop-audio") {
    stopAudio();
  }
});

// 播放音频
function playAudio(url) {
  if (audio) {
    audio.pause();
    audio = null;
  }

  audio = new Audio(url);

  // 音频事件监听
  audio.onloadstart = () => {
    chrome.runtime.sendMessage({ action: "audio-started" });
  };

  audio.onplay = () => {
    chrome.runtime.sendMessage({ action: "audio-started" });
  };

  audio.onended = () => {
    chrome.runtime.sendMessage({ action: "audio-stopped" });
  };

  audio.onerror = (e) => {
    chrome.runtime.sendMessage({ action: "audio-stopped" });
    console.error("音频播放失败:", e);
  };

  audio.play().catch((e) => {
    chrome.runtime.sendMessage({ action: "audio-stopped" });
    console.error("音频播放失败:", e);
  });
}

// 暂停音频
function pauseAudio() {
  if (audio && !audio.paused) {
    audio.pause();
  }
}

// 停止音频
function stopAudio() {
  if (audio) {
    audio.pause();
    audio.currentTime = 0;
    chrome.runtime.sendMessage({ action: "audio-stopped" });
  }
}
html 复制代码
<!-- popup.html -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <link rel="stylesheet" href="popup.css" />
  </head>
  <body>
    <div class="container">
      <h2>朗读助手</h2>

      <div class="voice-selector">
        <label for="voice">选择音色:</label>
        <select id="voice">
          <option value="Cherry">Cherry (女声)</option>
          <option value="Allen">Allen (男声)</option>
          <option value="Zhiyan">Zhiyan (知言)</option>
        </select>
      </div>

      <div class="controls">
        <button id="startBtn" class="btn primary">开始朗读</button>
        <button id="pauseBtn" class="btn secondary" disabled>暂停</button>
        <button id="stopBtn" class="btn secondary" disabled>停止</button>
      </div>

      <div class="status" id="status"></div>
    </div>

    <script src="popup.js"></script>
  </body>
</html>
css 复制代码
/* popup.css */
body {
  width: 300px;
  padding: 20px;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}

.container {
  text-align: center;
}

h2 {
  color: #333;
  margin-bottom: 20px;
}

.voice-selector {
  margin-bottom: 20px;
}

.voice-selector label {
  display: block;
  margin-bottom: 8px;
  color: #666;
}

.voice-selector select {
  width: 100%;
  padding: 8px;
  border: 1px solid #ddd;
  border-radius: 4px;
}

.controls {
  display: flex;
  gap: 10px;
  margin-bottom: 20px;
}

.btn {
  flex: 1;
  padding: 10px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
  transition: background-color 0.2s;
}

.btn.primary {
  background-color: #4caf50;
  color: white;
}

.btn.primary:hover {
  background-color: #45a049;
}

.btn.secondary {
  background-color: #f0f0f0;
  color: #333;
}

.btn.secondary:hover {
  background-color: #e0e0e0;
}

.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.status {
  color: #666;
  font-size: 12px;
  min-height: 16px;
}
javascript 复制代码
// popup.js
document.addEventListener("DOMContentLoaded", () => {
  const startBtn = document.getElementById("startBtn");
  const pauseBtn = document.getElementById("pauseBtn");
  const stopBtn = document.getElementById("stopBtn");
  const voiceSelect = document.getElementById("voice");
  const status = document.getElementById("status");

  // 开始朗读
  startBtn.addEventListener("click", () => {
    const voice = voiceSelect.value;
    chrome.runtime.sendMessage({ action: "start", voice });
    updateStatus("正在获取选中文本...");
  });

  // 暂停
  pauseBtn.addEventListener("click", () => {
    chrome.runtime.sendMessage({ action: "pause" });
    updateStatus("已暂停");
  });

  // 停止
  stopBtn.addEventListener("click", () => {
    chrome.runtime.sendMessage({ action: "stop" });
    updateStatus("已停止");
  });

  // 更新状态
  function updateStatus(message) {
    status.textContent = message;
  }

  // 监听播放状态
  chrome.runtime.onMessage.addListener((msg) => {
    if (msg.action === "audio-started") {
      startBtn.disabled = true;
      pauseBtn.disabled = false;
      stopBtn.disabled = false;
      updateStatus("正在播放...");
    } else if (msg.action === "audio-stopped") {
      startBtn.disabled = false;
      pauseBtn.disabled = true;
      stopBtn.disabled = true;
      updateStatus("播放完成");
    }
  });
});

🐛 调试与测试

1. 本地调试

  1. 加载插件

    • 打开 Chrome,访问 chrome://extensions/
    • 开启"开发者模式"
    • 点击"加载已解压的扩展程序"
    • 选择 extension 文件夹
  2. 调试 Background Script

    • 在扩展页面点击"检查视图"
    • 使用 Console 和 Sources 面板调试
  3. 调试 Content Script

    • 在目标网页按 F12 打开开发者工具
    • 在 Console 中查看日志
  4. 调试 Popup

    • 右键点击插件图标
    • 选择"检查弹出内容"

2. 常见调试技巧

javascript 复制代码
// 添加调试日志
console.log("Debug:", { text, voice, result });

// 错误处理
try {
  const result = await qwenTTS(text, voice);
  console.log("TTS Result:", result);
} catch (error) {
  console.error("TTS Error:", error);
  showNotification("TTS 服务调用失败: " + error.message);
}

// 检查权限
chrome.permissions.contains(
  {
    permissions: ["activeTab"],
    origins: ["<all_urls>"],
  },
  (result) => {
    console.log("Permissions check:", result);
  }
);

3. 测试清单

  • 插件能正常加载
  • 右键菜单功能正常
  • 弹窗界面显示正确
  • TTS API 调用成功
  • 音频播放功能正常
  • 播放状态图标更新
  • 错误处理机制有效
  • 在不同网站测试兼容性

📦 打包与发布

1. 准备发布文件

bash 复制代码
# 创建打包文件
zip -r xuri-tts-assistant.zip extension/

# 检查文件大小
ls -la xuri-tts-assistant.zip

2. 创建发布文档

README.md
markdown 复制代码
# Xuri TTS Assistant - 浏览器朗读助手

## 功能特性

- 🎯 智能朗读:选中网页文本,一键朗读
- 🎨 多种音色:支持多种语音音色选择
- 🎵 播放控制:支持播放、暂停、停止功能
- 🎪 视觉反馈:播放时图标动态显示
- 🖱️ 右键菜单:右键选中文本即可朗读

## 安装方法

1. 下载 `xuri-tts-assistant.zip`
2. 解压文件
3. 打开 Chrome,进入 `chrome://extensions/`
4. 开启"开发者模式"
5. 点击"加载已解压的扩展程序"
6. 选择解压后的 `extension` 文件夹

## 使用方法

1. 在网页上选中要朗读的文本
2. 点击插件图标,选择音色后点击"开始朗读"
3. 或右键选中文本,选择"朗读选中文本"
RELEASE_NOTES.md
markdown 复制代码
# Xuri TTS Assistant v1.0 发布说明

## 🎉 首个正式版本发布

### ✨ 主要功能

- 智能文本朗读
- 多种音色支持
- 播放控制功能
- 右键菜单集成
- 播放状态显示

### 📦 安装方法

1. 下载 `xuri-tts-assistant.zip`
2. 按 README.md 中的步骤安装

### 🔧 技术特性

- TTS 引擎:阿里云通义千问 TTS
- 浏览器支持:Chrome 88+
- 权限要求:最小化权限设计

3. GitHub 发布流程

bash 复制代码
# 1. 提交代码
git add .
git commit -m "feat: 完成插件开发并准备发布 v1.0"
git push origin main

# 2. 创建版本标签
git tag -a v1.0 -m "Release v1.0: 首个正式版本"
git push origin v1.0

# 3. 在 GitHub 创建 Release
# - 访问仓库的 Releases 页面
# - 点击 "Create a new release"
# - 选择标签 v1.0
# - 填写发布说明
# - 上传 xuri-tts-assistant.zip
# - 点击 "Publish release"

4. 发布检查清单

  • 所有功能测试通过
  • 代码注释完整
  • 错误处理完善
  • 文档齐全(README、LICENSE、隐私政策)
  • 打包文件创建
  • GitHub Release 发布
  • 安装说明清晰

🔧 常见问题解决

1. 权限错误

yaml 复制代码
Unchecked runtime.lastError: Cannot access contents of url

解决方案

  • manifest.json 中添加 host_permissions
  • 确保权限声明正确

2. 连接错误

arduino 复制代码
Uncaught (in promise) Error: Could not establish connection

解决方案

  • 检查 content script 是否正确注入
  • 添加错误处理和重试机制
  • 排除特殊页面(chrome:// 等)

3. API 调用失败

复制代码
TTS 服务调用失败

解决方案

  • 检查 API Key 是否正确
  • 验证网络连接
  • 确认 API 配额充足

4. 音频播放问题

复制代码
音频播放失败

解决方案

  • 检查音频 URL 是否有效
  • 确认浏览器支持音频格式
  • 添加音频加载错误处理

📈 性能优化

1. 代码优化

javascript 复制代码
// 使用防抖处理频繁操作
function debounce(func, wait) {
  let timeout;
  return function executedFunction(...args) {
    const later = () => {
      clearTimeout(timeout);
      func(...args);
    };
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
  };
}

// 缓存 API 结果
const audioCache = new Map();
function getCachedAudio(text, voice) {
  const key = `${text}-${voice}`;
  if (audioCache.has(key)) {
    return audioCache.get(key);
  }
  // 调用 API 并缓存结果
}

2. 资源优化

  • 压缩图片文件
  • 合并 CSS/JS 文件
  • 使用 CDN 加速
  • 启用 Gzip 压缩

3. 用户体验优化

  • 添加加载动画
  • 优化错误提示
  • 支持快捷键操作
  • 记住用户偏好设置

🚀 扩展功能

1. 功能扩展

  • 支持更多 TTS 服务
  • 添加语音设置选项
  • 支持批量文本朗读
  • 添加朗读历史记录
  • 支持更多浏览器

2. 技术升级

  • 使用 WebAssembly 优化性能
  • 添加离线 TTS 支持
  • 实现语音识别功能
  • 支持多语言界面

3. 商业化方向

  • 发布到 Chrome Web Store
  • 提供付费高级功能
  • 企业版本定制
  • API 服务商业化

📚 学习资源

官方文档

开发工具

社区资源

🎯 总结与展望

开发总结

通过本教程,我们完成了:

  1. 完整的开发流程:从需求分析到功能实现
  2. 规范的代码结构:模块化设计,易于维护
  3. 完善的错误处理:提升用户体验
  4. 详细的文档说明:便于用户使用和开发者贡献

技术收获

  • Chrome Extension API 的使用
  • Manifest V3 的新特性
  • 前后端分离的架构设计
  • 异步编程和错误处理
  • 用户体验优化技巧

未来展望

Chrome 插件开发是一个充满可能性的领域:

  1. 技术发展:随着 Web 技术的进步,插件功能将更加强大
  2. 应用场景:从工具类到娱乐类,应用场景不断扩展
  3. 商业模式:从免费开源到商业化运营,变现方式多样
  4. 生态建设:开发者社区活跃,资源共享丰富

建议

  1. 持续学习:关注 Chrome 插件技术的最新发展
  2. 实践项目:多开发不同类型的插件项目
  3. 开源贡献:参与开源项目,提升技术水平
  4. 社区交流:加入开发者社区,分享经验和资源

感谢阅读! 🎉

如果这个教程对你有帮助,请给个 ⭐ Star 支持一下!


本文首发于掘金,转载请注明出处。

相关推荐
崔庆才丨静觅9 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60619 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了10 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅10 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅10 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅10 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment11 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅11 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊11 小时前
jwt介绍
前端
爱敲代码的小鱼11 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax