明知山没虎

一个游手好闲的人

Chrome浏览器插件开发教程

2025-12-24

插件是个啥

Chrome插件就是给浏览器加功能的小程序。比如广告拦截、翻译、密码管理器这些都是插件。你写个插件可以:

  • 改网页的内容和样式

  • 给浏览器加新功能

  • 调用外部接口

  • 管理书签和标签页

  • 抓取网页数据

基本上想给浏览器加什么功能都行。

准备工作

必须有的

  • Chrome浏览器(最好是最新版)

  • 代码编辑器(VSCode挺好用)

  • 会写HTML、CSS、JavaScript

可选的

  • Node.js(用来装一些开发工具)

  • Git(管理代码版本)

插件的文件结构

一个插件大概长这样:

我的插件/
├── manifest.json          # 配置文件,必须有
├── popup.html            # 点击插件图标弹出的页面
├── popup.js              # 弹窗的JS代码
├── background.js         # 后台运行的代码
├── content.js            # 注入到网页的代码
├── options.html          # 设置页面
├── options.js            # 设置页面的代码
└── icons/               # 图标文件夹
    ├── icon16.png
    ├── icon48.png
    └── icon128.png

第一个插件

咱们先做个简单的"Hello World"插件。

第一步:新建文件夹

随便找个地方新建一个文件夹,比如叫 hello-world

第二步:写配置文件manifest.json

{
  "manifest_version": 3,
  "name": "Hello World 插件",
  "version": "1.0",
  "description": "我的第一个插件",
  "permissions": [
    "activeTab"
  ],
  "action": {
    "default_popup": "popup.html",
    "default_title": "Hello World",
    "default_icon": {
      "16": "icons/icon16.png",
      "32": "icons/icon32.png",
      "48": "icons/icon48.png",
      "128": "icons/icon128.png"
    }
  },
  "icons": {
    "16": "icons/icon16.png",
    "32": "icons/icon32.png",
    "48": "icons/icon48.png",
    "128": "icons/icon128.png"
  }
}

第三步:弹窗页面popup.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <style>
    body {
      width: 300px;
      height: 200px;
      padding: 20px;
      font-family: "微软雅黑", sans-serif;
    }
    button {
      background: #007bff;
      color: white;
      padding: 12px 24px;
      border: none;
      border-radius: 6px;
      cursor: pointer;
      font-size: 14px;
    }
    button:hover {
      background: #0056b3;
    }
    #result {
      margin-top: 15px;
      color: #28a745;
      font-weight: bold;
    }
  </style>
</head>
<body>
  <h2>你好世界!</h2>
  <p>这是我做的第一个Chrome插件</p>
  <button id="clickMe">点我试试</button>
  <p id="result"></p>
  <script src="popup.js"></script>
</body>
</html>

第四步:弹窗逻辑popup.js

document.addEventListener('DOMContentLoaded', function() {
  const button = document.getElementById('clickMe');
  const result = document.getElementById('result');
  
  button.addEventListener('click', function() {
    result.textContent = '恭喜!插件运行成功了!';
  });
});

第五步:准备图标

icons 文件夹里放几个不同尺寸的图标,16、32、48、128像素的PNG文件。

第六步:装载插件

  1. 打开Chrome,地址栏输入 chrome://extensions/

  2. 右上角打开"开发者模式"

  3. 点"加载已解压的扩展程序"

  4. 选择你的插件文件夹

装好后工具栏就会出现你的插件图标,点击试试看!

核心文件说明

manifest.json配置详解

这个文件就是插件的身份证,告诉浏览器这个插件是干嘛的:

{
  "manifest_version": 3,        // 版本号,现在都用3
  "name": "插件名字",
  "version": "1.0.0",          // 你的插件版本
  "description": "插件介绍",
  
  // 申请的权限
  "permissions": [
    "storage",                 // 能存数据
    "activeTab",              // 能操作当前标签页
    "tabs"                    // 能操作所有标签页
  ],
  
  // 能访问的网站(V3新加的)
  "host_permissions": [
    "https://*.baidu.com/*"
  ],
  
  // 后台脚本
  "background": {
    "service_worker": "background.js"
  },
  
  // 网页注入脚本
  "content_scripts": [{
    "matches": ["<all_urls>"],
    "js": ["content.js"],
    "css": ["content.css"]
  }],
  
  // 工具栏图标
  "action": {
    "default_popup": "popup.html",
    "default_title": "鼠标悬停显示的文字",
    "default_icon": "icon.png"
  },
  
  // 选项页面
  "options_page": "options.html",
  
  // 各种尺寸图标
  "icons": {
    "16": "icon16.png",
    "48": "icon48.png",
    "128": "icon128.png"
  }
}

后台脚本background.js

这个脚本在后台一直运行,处理各种事件:

// background.js
// 插件装好后执行
chrome.runtime.onInstalled.addListener(() => {
  console.log('插件装好了');
});

// 监听标签页变化
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
  if (changeInfo.status === 'complete' && tab.url) {
    console.log('页面加载完了:', tab.url);
  }
});

// 点击插件图标时执行
chrome.action.onClicked.addListener((tab) => {
  // 给当前页面发消息
  chrome.tabs.sendMessage(tab.id, {action: "doSomething"});
});

内容脚本content.js

这个脚本会注入到网页里,可以操作页面元素:

// content.js
// 给页面加个按钮
function addMyButton() {
  const button = document.createElement('button');
  button.textContent = '我是插件按钮';
  button.style.cssText = `
    position: fixed;
    top: 20px;
    right: 20px;
    z-index: 9999;
    padding: 10px 15px;
    background: #ff6b6b;
    color: white;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    font-size: 14px;
  `;
  
  button.onclick = function() {
    alert('你好,我是从插件来的!');
  };
  
  document.body.appendChild(button);
}

// 收后台脚本的消息
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === "doSomething") {
    addMyButton();
  }
});

// 页面加载完就执行
if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', addMyButton);
} else {
  addMyButton();
}

权限设置

Chrome插件要申请权限才能做事,这是为了保护用户隐私:

常用权限列表

{
  "permissions": [
    "storage",              // 存储数据
    "activeTab",            // 操作当前标签页
    "tabs",                 // 操作所有标签页
    "bookmarks",            // 操作书签
    "history",              // 查看浏览历史
    "cookies",              // 读写Cookie
    "notifications",        // 发通知
    "contextMenus",         // 右键菜单
    "background"            // 后台运行
  ],
  "host_permissions": [
    "https://*.taobao.com/*",   // 访问淘宝
    "https://*.jd.com/*",       // 访问京东
    "<all_urls>"                // 访问所有网站(慎用)
  ]
}

权限要的越少越好,用户安装时顾虑也少。

不同脚本间的通信

插件里popup、background、content script要互相传消息:

Popup和Background通信

// popup.js - 发消息
chrome.runtime.sendMessage({type: "getData", params: "something"}, (response) => {
  console.log('收到回复:', response);
});

// background.js - 收消息
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.type === "getData") {
    // 处理请求
    const result = processData(request.params);
    sendResponse({success: true, data: result});
  }
});

Content Script和Background通信

// content.js - 给后台发消息
chrome.runtime.sendMessage({
  type: "pageAnalyzed", 
  url: window.location.href,
  title: document.title
});

// background.js - 给content script发消息
chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
  chrome.tabs.sendMessage(tabs[0].id, {
    type: "highlightText",
    keyword: "关键词"
  });
});

数据存储

插件可以用几种方式存数据:

Chrome Storage API(推荐)

// 存数据
chrome.storage.sync.set({
  username: "张三",
  settings: {theme: "dark", lang: "zh"}
}, () => {
  console.log('保存成功');
});

// 取数据
chrome.storage.sync.get(['username', 'settings'], (result) => {
  console.log('用户名:', result.username);
  console.log('设置:', result.settings);
});

// 监听数据变化
chrome.storage.onChanged.addListener((changes, namespace) => {
  for (let key in changes) {
    console.log(`${key} 从 ${changes[key].oldValue} 改成了 ${changes[key].newValue}`);
  }
});

// 删除数据
chrome.storage.sync.remove('username', () => {
  console.log('删除成功');
});

localStorage(只能在content script里用)

// 在content script中
localStorage.setItem('myData', JSON.stringify({name: '数据', value: 123}));
const data = JSON.parse(localStorage.getItem('myData'));

调试方法

各种调试方式

  1. 调试popup:右键插件图标 → "检查弹出内容"

  2. 调试background:扩展管理页面点"检查视图"

  3. 调试content script:网页F12,在Sources里找扩展脚本

调试技巧

// 打日志
console.log('这里是调试信息:', someData);
console.error('出错了:', error);

// 断点调试
debugger; // 程序会在这里停下

// 检查是否有错
if (chrome.runtime.lastError) {
  console.error('Chrome API出错:', chrome.runtime.lastError);
}

// 捕获异常
try {
  // 可能出错的代码
  doSomethingRisky();
} catch (error) {
  console.error('捕获到异常:', error);
}

上架发布

发布前准备

  1. 完善插件信息:描述要详细,图标要好看

  2. 全面测试:各种浏览器版本都试试

  3. 准备素材:截图、宣传图、详细说明

发布到Chrome应用商店

  1. 注册Chrome开发者账号(要交5美元注册费)

  2. 打开 Chrome开发者后台

  3. 点"新增项目"

  4. 上传插件ZIP包

  5. 填写详细信息

  6. 提交审核(通常几天到一周)

打包插件

# 把插件文件夹压缩成ZIP
zip -r 我的插件.zip 插件文件夹/

记住别把开发文件(.git、node_modules等)打包进去。

高级功能

右键菜单

// background.js
chrome.runtime.onInstalled.addListener(() => {
  // 创建右键菜单项
  chrome.contextMenus.create({
    id: "translateText",
    title: "翻译选中文字",
    contexts: ["selection"]  // 只有选中文字时才显示
  });
});

// 处理右键菜单点击
chrome.contextMenus.onClicked.addListener((info, tab) => {
  if (info.menuItemId === "translateText") {
    // 翻译选中的文字
    const selectedText = info.selectionText;
    translateText(selectedText);
  }
});

桌面通知

// 发送通知
chrome.notifications.create('notification-id', {
  type: 'basic',
  iconUrl: 'icon48.png',
  title: '插件通知',
  message: '这是来自插件的消息!'
}, (notificationId) => {
  console.log('通知已发送:', notificationId);
});

// 监听通知点击
chrome.notifications.onClicked.addListener((notificationId) => {
  console.log('用户点击了通知:', notificationId);
});

标签页操作

// 新建标签页
chrome.tabs.create({
  url: 'https://www.baidu.com',
  active: true  // 立即切换到新标签页
});

// 关闭当前标签页
chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
  chrome.tabs.remove(tabs[0].id);
});

// 刷新页面
chrome.tabs.reload();

// 获取所有标签页
chrome.tabs.query({}, (tabs) => {
  tabs.forEach(tab => {
    console.log(tab.title, tab.url);
  });
});

开发经验

性能优化

// 避免频繁操作DOM
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
  const div = document.createElement('div');
  div.textContent = `第${i}个元素`;
  fragment.appendChild(div);
}
document.body.appendChild(fragment); // 一次性添加

// 及时移除事件监听
let tabUpdateListener = (tabId, changeInfo, tab) => {
  // 处理标签页更新
};

// 开始监听
chrome.tabs.onUpdated.addListener(tabUpdateListener);

// 不需要时移除监听
chrome.tabs.onUpdated.removeListener(tabUpdateListener);

安全建议

// 验证URL是否安全
function isValidUrl(url) {
  try {
    const urlObj = new URL(url);
    return urlObj.protocol === 'https:' || urlObj.protocol === 'http:';
  } catch {
    return false;
  }
}

// 过滤用户输入
function sanitizeInput(input) {
  return input.replace(/[<>'"]/g, ''); // 简单过滤
}

// 验证消息来源
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  // 确保消息来自插件内部
  if (!sender.tab || sender.tab.url.startsWith('chrome-extension://')) {
    // 处理消息
  }
});

用户体验

// 显示加载状态
function showLoading() {
  document.getElementById('loading').style.display = 'block';
}

function hideLoading() {
  document.getElementById('loading').style.display = 'none';
}

// 友好的错误提示
function showError(message) {
  const errorDiv = document.createElement('div');
  errorDiv.className = 'error-message';
  errorDiv.textContent = `出错了:${message}`;
  document.body.appendChild(errorDiv);
  
  // 3秒后自动消失
  setTimeout(() => {
    errorDiv.remove();
  }, 3000);
}

// 处理异步操作
async function fetchUserData() {
  try {
    showLoading();
    const response = await fetch('https://api.example.com/user');
    if (!response.ok) {
      throw new Error(`服务器返回${response.status}错误`);
    }
    const data = await response.json();
    return data;
  } catch (error) {
    showError('获取用户数据失败,请稍后重试');
    console.error('API调用失败:', error);
  } finally {
    hideLoading();
  }
}

实战项目:网页截图工具

我们做个实用的插件,能截图当前网页:

manifest.json

{
  "manifest_version": 3,
  "name": "网页截图助手",
  "version": "1.0",
  "description": "快速截取网页截图",
  "permissions": [
    "activeTab",
    "downloads"
  ],
  "action": {
    "default_popup": "popup.html",
    "default_title": "网页截图"
  },
  "background": {
    "service_worker": "background.js"
  }
}

popup.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <style>
    body {
      width: 280px;
      padding: 20px;
      font-family: "微软雅黑", Arial, sans-serif;
    }
    button {
      width: 100%;
      padding: 12px;
      margin: 8px 0;
      border: none;
      border-radius: 6px;
      cursor: pointer;
      font-size: 14px;
      transition: background-color 0.3s;
    }
    .primary-btn {
      background: #007bff;
      color: white;
    }
    .primary-btn:hover {
      background: #0056b3;
    }
    .secondary-btn {
      background: #6c757d;
      color: white;
    }
    .secondary-btn:hover {
      background: #545b62;
    }
    #status {
      margin-top: 15px;
      padding: 10px;
      border-radius: 4px;
      text-align: center;
      display: none;
    }
    .success {
      background: #d4edda;
      color: #155724;
    }
    .error {
      background: #f8d7da;
      color: #721c24;
    }
  </style>
</head>
<body>
  <h3>网页截图工具</h3>
  <button id="captureVisible" class="primary-btn">截取可见区域</button>
  <button id="captureFullPage" class="secondary-btn">截取整个页面</button>
  <div id="status"></div>
  <script src="popup.js"></script>
</body>
</html>

popup.js

document.addEventListener('DOMContentLoaded', function() {
  const captureVisibleBtn = document.getElementById('captureVisible');
  const captureFullPageBtn = document.getElementById('captureFullPage');
  const status = document.getElementById('status');
  
  function showStatus(message, type = 'success') {
    status.textContent = message;
    status.className = type;
    status.style.display = 'block';
    
    setTimeout(() => {
      status.style.display = 'none';
    }, 3000);
  }
  
  captureVisibleBtn.addEventListener('click', () => {
    chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
      chrome.runtime.sendMessage({
        action: 'captureVisible',
        tab: tabs[0]
      }, (response) => {
        if (response.success) {
          showStatus('截图成功!');
        } else {
          showStatus('截图失败:' + response.error, 'error');
        }
      });
    });
  });
  
  captureFullPageBtn.addEventListener('click', () => {
    showStatus('全页截图功能开发中...', 'error');
  });
});

background.js

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === 'captureVisible') {
    captureVisibleTab(request.tab)
      .then(dataUrl => {
        downloadImage(dataUrl, request.tab.title);
        sendResponse({success: true});
      })
      .catch(error => {
        console.error('截图失败:', error);
        sendResponse({success: false, error: error.message});
      });
    
    return true; // 异步响应
  }
});

async function captureVisibleTab(tab) {
  try {
    const dataUrl = await chrome.tabs.captureVisibleTab(tab.windowId, {
      format: 'png',
      quality: 100
    });
    return dataUrl;
  } catch (error) {
    throw new Error('无法截取当前页面');
  }
}

function downloadImage(dataUrl, pageTitle) {
  const now = new Date();
  const timestamp = now.toISOString().replace(/[:.]/g, '-');
  const filename = `截图_${pageTitle || 'unnamed'}_${timestamp}.png`;
  
  chrome.downloads.download({
    url: dataUrl,
    filename: filename,
    saveAs: false
  });
}

这个插件就能截图了!点击按钮会自动下载当前页面的截图。

总结

Chrome插件开发其实不难,主要是:

  1. 理解结构:manifest.json + 各种脚本文件

  2. 掌握通信:popup、background、content script之间怎么传消息

  3. 熟练API:Chrome提供的各种接口

  4. 多练习:从简单项目开始,慢慢做复杂功能

继续学习

  • 看Chrome官方文档了解更多API

  • 学习现代前端框架(React、Vue)开发插件

  • 了解Webpack等构建工具

  • 学习自动化测试

有用链接

记住一点:最好的学习方法就是动手做。想到什么功能就试着实现,遇到问题就查文档,这样进步最快。