Chrome 扩展开发攻略

前言

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

基本知识

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

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

浏览器扩展 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 startEl = document.querySelector('#start');
const pauseEl = document.querySelector('#pause');
const urlField = document.querySelector('#urlField');
const tbody = document.querySelector('#urlTableData');
const resetEl = document.querySelector('#reset');
const tfootEl = document.querySelector('tfoot');

// 获取本地存储数据
function getSites() {
  const str = window.localStorage.getItem('sites') || '[]';
  return JSON.parse(str);
}

// URL 校验方法
function checkURL(url) {
  const urlReg = /http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/;
  return urlReg.test(url);
}

function buttonDisable(sites) {
  if (sites.length) {
    tfootEl.classList.add('hidden');
    resetEl.removeAttribute('disabled');
    startEl.removeAttribute('disabled');
    pauseEl.removeAttribute('disabled');
  } else {
    tfootEl.classList.remove('hidden');
    resetEl.setAttribute('disabled', 'disabled');
    startEl.setAttribute('disabled', 'disabled');
    pauseEl.setAttribute('disabled', 'disabled');
  }
}

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)
  }
}

// 接受 background 返回值并存储,同时确定按钮是否 disabled
function callbackSites(res) {
  const sites = JSON.parse(res).sites;
  createTbodyContent(sites);
  window.localStorage.setItem('sites', JSON.stringify(sites));
  buttonDisable(sites)
}

// 根据本地获取的 url 生成表格 
createTbodyContent(getSites());
buttonDisable(getSites());

// 全局点击事件绑定
document.addEventListener("click", (e) => {
  if (checkURL(e.target.id)) {
    chrome.runtime.sendMessage({
      type: 'del',
      data: e.target.id
    }, callbackSites);
  }
  switch (e.target.id) {
    case 'save': {
      if (urlField.value.trim() === '') {
        alert('请输入 URL');
        return;
      }
      let result = checkURL(urlField.value.trim())
      if (result) {
        chrome.extension.sendMessage({
          type: 'save',
          data: urlField.value.trim()
        }, (res) => {
          callbackSites(res);
          urlField.value = '';
        });
      } else {
        alert('你输入的 URL 不正确')
      }
      break;
    }
    case 'start': {
      chrome.tabs.query({
        active: true,
        currentWindow: true
      }, (tabs) => {
        chrome.extension.sendMessage({
          type: 'start',
          windowId: tabs[0].windowId,
          tabId: tabs[0].id,
        }, (res) => {
          window.localStorage.setItem('status', JSON.stringify(JSON.parse(res).status));
          console.log(JSON.parse(res).status);
        })
      })
      break;
    }
    case "pause": {
      chrome.extension.sendMessage({
        type: 'pause'
      })
      break;
    }
    case "reset": {
      chrome.extension.sendMessage({
        type: 'reset'
      }, callbackSites)
    }
  }
})
复制代码

猜你喜欢

转载自juejin.im/post/7128824846133248014