Chrome 扩展开发攻略(以 keepUp 为例)

996 阅读3分钟

前言

当你看到这篇文章说明你已经很好奇 Chrome 的各种扩展(插件)是如何开发创造出来的,你或许也在思考自己该从何下手才能掌握浏览器扩展的开发。那么,这篇文章将用一个 Demo 尝试满足你的好奇并解答你的疑惑。

基本知识

  • HTML 标签的基本认识
  • CSS 选择器和基本布局样式
  • JavaScript 基本语法知识以及 Dom 操作

前端基础十分简单,如果你暂时没有前端开发的基本知识可以到 MDN 进行学习。

扩展 Demo

详细代码请至 GitHub keepUp 仓浏览

浏览器扩展 WebExtensions

扩展的能力

  1. 提升或补充网站的功能,比如稀土掘金为各位开发者提供的工具插件,提供包括记笔记等功能。
  2. 操控网页内容,诸如去除页面广告类扩展。
  3. 添加开发工具,有 Vue 和 React 的 devTools,以及前端较为广泛使用的 FeHelper
  4. 为网页注入脚本,有用户脚本管理工具 Tampermonkey。

扩展中的文件

.
├── icon
│   ├── icon_120x120.png // 不同大小的图片,用于在工具栏等处显示
│   ├── icon_48x48.png
│   └── icon_80x80.png
├── manifest.json // 扩展必须包含的 json 文件,可以使用“//”写行注释
├── popup.html // 弹出页面
├── scripts
│   ├── background.js // 后台运行的脚本文件
│   └── popup.js // 用于操作 Dom
└── styles
    └── inline.css // 样式

manifest.json 这是唯一一个在每个扩展里面必须存在的文件。它包含了关于这个扩展插件基本的元数据(metadata),比如它的名字、版本和所需权限。并且,它也对扩展中其他文件进行了链接。

{
  "manifest_version": 2, // 指定扩展使用的 manifest.json 的版本
  "name": "文章浏览量增加器", // 扩展的名称
  "short_name": "upCount",
  "description": "自动刷新文章的浏览量", // 扩展的描述信息
  "version": "0.1.0", //版本号
  "background": { // 引入一个或者多个后台脚本文件,以及一个可选的后台页面文件
    "scripts": [
      "./scripts/background.js" // 引入的路径
    ],
    "persistent": true // 是否持续运行
  },
  "browser_action": {
    "default_icon": "./icon/icon_120x120.png",
    "default_title": "文章浏览量增加器",
    "default_popup": "./popup.html"
  },
  "icons": {
    "120": "./icon/icon_120x120.png",
    "80": "./icon/icon_80x80.png",
    "48": "./icon/icon_48x48.png"
  },

  "permissions": [
    "activeTab",
    "tabs",
    "http://*/*",
    "https://*/*"
  ]
}

popup.html 弹出的页面
inline.css 引入的样式文件

效果图

popup.js 用于操作 Dom 和与 background.js 通信

// 获取 Dom 以便对其操作
const saveEl = document.querySelector('#save');
const tbodyEl = document.querySelector('tbody');

function createTbodyContent(sites) {
  tbody.innerHTML = '';
  for (let item of sites) {
    const trEl = document.createElement('tr');
    trEl.innerHTML = `
      <td class='urlCol' title='${item}'>${item}</td>
      <td class='operationCol'>
        <div class='del' id='${item}' title='删除${item}'>删除</div>
      </td>`
    tbody.prepend(trEl)
  }
}
// 根据本地获取的 url 生成表格 
createTbodyContent(getSites());

// 全局点击事件绑定
document.addEventListener("click", (e) => {
  switch (e.target.id) {
    case 'save': {
      if (urlField.value.trim() === '') {
        alert('请输入 URL');
        return;
      }
      let result = checkURL(urlField.value.trim())
      if (result) {
      // 发送数据至 background.js,第二个参数是回调函数,接收 background.js 的返回值
        chrome.extension.sendMessage({
          type: 'save',
          data: urlField.value.trim()
        }, (res) => {
          callbackSites(res);
          urlField.value = '';
        });
      } else {
        alert('你输入的 URL 不正确')
      }
      break;
    }
  }
})

background.js 进行逻辑处理

// background.js 接受来自 popup.js 数据,并可通过 callback 给 popup.js 返回处理过的数据
chrome.extension.onMessage.addListener((resquest, sender, callback) => {
  let sites = getSites();
  let newSites;
  switch (resquest.type) {
    case 'save':
      newSites = [resquest.data, ...sites];
      callback && callback(`{"sites": ${JSON.stringify(newSites)} }`);
      break;
  }
});
let index = 0;
let handle;
function start(tabId, windowId, sites) {
  if (tabId && windowId) {
    handle = setInterval(() => {
      run(tabId, sites)
    }, 6000)
  } else {
    clearInterval(handle);
  }
}

function run(tabId, sites) {
  let url = sites[index % sites.length];
   // 在同一 tab 下调取 url 页面
  chrome.tabs.update(tabId, {
    url
  });
  index++;
}

遇到的问题

在给 table 新增行时,由于存在操作列,原本直接在删除操作标签上写点击事件 onclick,但是扩展开发认为这不安全,所以点击事件未能生效。后面改用全局点击事件绑定才得以成功。

推荐

关于扩展开发更为全面的内容推荐学习 【干货】Chrome插件(扩展)开发全攻略