# Chrome插件使用

Chrome插件就是一个简单HTML静态文件,和正常的网页开发一样,遵循插件的格式和使用对应的API就可以了。并且可以支持向服务端发送请求。插件的几个核心点就是弹窗、内容脚本、Service Worker线程服务,还有这几个插件之间互相发送和接收消息。

Chrome插件文档 (opens new window)

# 创建项目

Chrome插件项目就是普通的HTML页面,就多了一个manifest.json配置文件,项目创建好直接在浏览器中导入就可以用了。

├── background                   # Service Worker文件
│   └── background.js            
├── images                       # 图片
├── popup                        # 插件弹窗文件
│   ├── popup.html               # 弹窗html
│   ├── popup.css                # 弹窗css
│   └── popup.js                 # 弹窗js
├── scripts                      # 内容脚本
│   ├── query.min.js             # 其他js文件
│   └── content.js               # 内容脚本
├── manifest.json                # 配置文件

# 安装

项目创建好后在新标签页中输入 chrome://extensions,转到“扩展程序”页面。右侧打开 开发者模式 会有几个按钮。点击 加载已解压的扩展程序 按钮,选择对应的文件就会把插件加载到浏览器中。

然后把插件固定到右上角的插件栏,点击就可以执行插件了。

# 配置文件

manifest.json文件用来描述插件和配置插件使用哪些功能。相当于入口文件。

  • action 配置插件的图片和弹窗内容

  • content_scripts 配置插件的内容脚本

  • background 配置插件的Service Worker

// manifest.json
{
  "manifest_version": 3,
  "name": "Hello Extensions of the world!",
  "description": "Base Level Extension",
  "version": "1.0",
  "icons": {
    "16": "images/icon-16.png",
    "32": "images/icon-32.png",
    "48": "images/icon-48.png",
    "128": "images/icon-128.png"
  },
  "action": {
    "default_popup": "popup/hello.html"
  },
  "background": {
    "service_worker": "background/background.js"
  },
  "content_scripts": [
    {
      "js": ["scripts/content.js"],
      "matches": [
        "https://developer.chrome.com/*"
      ]
    }
  ]
}

# 弹窗

popup为插件的弹窗内容,弹窗相当于一个单独的浏览器页面,可以在这个窗口进行界面开发,和我们正常的HTML、JS、CSS一样使用。审查该弹窗元素需要打开弹窗,右击点击检查。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="./popup.css">
</head>
<body>
  <div class="index">
    <div class="index-header">
      <h1>1Hello Extensions</h1>
    </div>


    <div class="index-btn">
      <button id="start-btn1">向Content发送消息</button>
    </div>
    <div class="index-btn">
      <button id="start-btn2">向Service Worker发送消息</button>
    </div>
  </div>
  <script src="./popup.js"></script>
</body>
</html>
// popup.js


// 插件弹窗JS内容,右键弹窗审查会审查弹窗的DOM内容,而不是网页的
console.log("This is a popup!");


var startBtn1 = document.getElementById('start-btn1');
var startBtn2 = document.getElementById('start-btn2');


startBtn1.addEventListener('click', function() {
  console.log('点击按钮1');
  sendMessageToContent('startContentMsg', function(response) {
    console.log('向Content发送消息成功', response);
  })
})


startBtn2.addEventListener('click', function() {
  console.log('点击按钮2');
  sendMessageToServiceWorker('startServiceWorkerMsg', function(response) {
    console.log('向ServiceWorker发送消息成功', response);
  })
})




/**
 * @desc 向内容脚本发送消息
 * @param action 消息类型
 * */ 
function sendMessageToContent(action, callback) {
  chrome.tabs.query({active: true}, function(tabs) {
    const tab = tabs[0];
    chrome.tabs.sendMessage(tab.id, { action: action }, callback);
  });
}


/**
 * @desc 向Service Worker 发送消息
 * @param action 消息类型
 * */ 
function sendMessageToServiceWorker(action, callback) {
  chrome.runtime.sendMessage({ action: action }, callback);
}

# 内容脚本

用来处理插件所在网页相关的JS内容,比如获取页面中的DOM元素、页面内容、手动触发按钮、模拟操作DOM等,除了接口无法获取,其他操作 都可以像在网页的js中一样执行。

它无法直接处理popup内容。调试可以直接在所在网页中进行调试。

// content.js


// 执行内容脚本,针对的是使用插件的网页,审查也是使用的页面审查元素


console.log('This is a content!')


// 获取页面的元素
console.log(document)


// 发送消息给service worker
function sendMessageToServiceWorker(action, params, callback) {
  chrome.runtime.sendMessage({ action: action, params: params}, callback);
}


// 修改背景颜色
function changeBodyBg() {
  const body = document.querySelector('body');
  console.log(body);
  body.style.background = 'red';
}


// 向Service Worker发送消息
function getReplyMessage() {
  sendMessageToServiceWorker('getReplyMessage', '我是content消息', function(response) {
    console.log('获取Service Worker的接口数据:', response)
  })
}


// 定义一个对象,用于映射 action 到相应的处理函数
const actionHandlers = {
  startContentMsg: function (message, sendResponse) {
    changeBodyBg();
    getReplyMessage();
    sendResponse('返回消息成功');
  }
}


// 接收弹窗Popup和Service Worker发送来的消息
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
  console.log(message)
  // 检查是否有处理该 action 的函数
  if (actionHandlers[message.action]) {
    // 调用对应的处理函数
    actionHandlers[message.action](message, sendResponse);
  } else {
    console.warn(`未处理的 action: ${message.action}`);
  }


  // 必须返回 true 以异步响应消息
  return true;
})

# Service Worker

只用来处理一些核心线程问题,比如调用接口数据,在其中无法访问DOM元素。

// background.js


console.log("Service worker 已加载");


// 监听插件的安装或更新事件
chrome.runtime.onInstalled.addListener((details) => {
  if (details.reason === chrome.runtime.OnInstalledReason.INSTALL) {
    console.log("插件已安装");
    // 这里可以执行一些初始化操作,比如设置默认配置
    chrome.storage.local.set({ installed: true });
  } else if (details.reason === chrome.runtime.OnInstalledReason.UPDATE) {
    console.log("插件已更新");
    // 这里可以执行一些更新后的操作,比如迁移数据
  } else if (details.reason === chrome.runtime.OnInstalledReason.CHROME_UPDATE) {
    console.log("Chrome 浏览器已更新");
  }
});




// 定义一个对象,用于映射 action 到相应的处理函数
const actionHandlers = {
  
  // 接收Content发送的消息
  getReplyMessage: function(message, sendResponse) {
    console.log("收到消息Content发送的消息", message);
    // 调用API请求
    fetch("https://jsonplaceholder.typicode.com/posts", {
      method: "GET",
    }).then(response => response.json())
    .then(res => {
      console.log(res)
      sendResponse(res)
    })
    .catch(err => console.log('Request Failed', err)); 
  },


  // 接收弹窗发送的消息
  startServiceWorkerMsg: function(message, sendResponse) {
    console.log('接收弹窗发送的消息', message)
    sendResponse('我是ServiceWorker')
  }
};


// 监听来自弹窗和Content的消息
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
  // 检查是否有处理该 action 的函数
  if (actionHandlers[message.action]) {
      // 调用对应的处理函数
      actionHandlers[message.action](message, sendResponse);
  } else {
      console.warn(`未处理的 action: ${message.action}`);
  }
  // 必须返回 true 以异步响应消息
  return true;
});

# 发布

发布插件 (opens new window)需要去注册Chrome插件开发者账号,注册好后就可以去发布了。

# 参考示例

官方示例:https://github.com/GoogleChrome/chrome-extensions-samples (opens new window)

github:https://github.com/changzhengithub/chrome-extension (opens new window)