前言
当你看到这篇文章说明你已经很好奇 Chrome 的各种扩展(插件)是如何开发创造出来的,你或许也在思考自己该从何下手才能掌握浏览器扩展的开发。那么,这篇文章将用一个 Demo 尝试满足你的好奇并解答你的疑惑。
基本知识
- HTML 标签的基本认识
- CSS 选择器和基本布局样式
- JavaScript 基本语法知识以及 Dom 操作
前端基础十分简单,如果你暂时没有前端开发的基本知识可以到 MDN 进行学习。
扩展 Demo
详细代码请至 GitHub keepUp 仓浏览
浏览器扩展 WebExtensions
扩展的能力
- 提升或补充网站的功能,比如稀土掘金为各位开发者提供的工具插件,提供包括记笔记等功能。
- 操控网页内容,诸如去除页面广告类扩展。
- 添加开发工具,有 Vue 和 React 的 devTools,以及前端较为广泛使用的 FeHelper。
- 为网页注入脚本,有用户脚本管理工具 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插件(扩展)开发全攻略