前言
扩展(Extension),指的是通过调用 Chrome 提供的 Chrome API 来扩展浏览器功能的一种组件,工作在浏览器层面,使用 HTML + Javascript 语言开发[*]。比如著名的 Adblock plus。
- 谷歌官方文档
- Chrome 扩展开发中文文档 – 非官方
下面我会以一个简单的 demo 来讲,该 demo 是一个简单翻译扩展,输入要翻译内容后把翻译结果输出到页面上。
一、目录结构
demo 主要目录结构如下:
| background.js // 后台脚本
| index.html // 浏览器点击本扩展时弹出的页面
| manifest.json // [重点] 扩展的配置文件,类似于 package.json
|
+---css // css 文件
| all.css
| style.css
|
+---img // 图片文件
| favicon.ico
| ico-logo.png
| ico.png
|
\---js // js 文件
translate.js // 翻译脚本
youdao.js // 有道翻译页面脚本
二、配置 manifest.json
manifest.json 是扩展的配置文件,可以在里面配置在那个页面使用哪个 js 文件,以及一些其他配置
在 2021 年 9 月 23 日 发布下图时间线全力推 manifest V3
下面可以看看大概结构
{
"name": "插件名",
"author": "插件作者",
"manifest_version": 3,
"version": "1.0.0",
"description": "插件描述",
// 插件图标
"icons": {
"16": "img/ico-logo.png",
"48": "img/ico-logo.png",
"128": "img/ico-logo.png"
},
// 『重点』action配置项主要用于点击图标弹出框,对于弹出框接受的是html文件
"action": {
"default_popup": "index.html",
"default_title": "鼠标移上去时的提示",
"default_icon": {
"16": "img/ico-logo.png",
"48": "img/ico-logo.png",
"128": "img/ico-logo.png"
}
},
// 允许使用扩展的域名
"host_permissions": ["http://*/", "https://*/"],
// 『包含已知字符串列表中的项目 【只需一次弹框要求允许】
"permissions": [
"contextMenus",
"tabs",
"webRequest",
"storage",
"notifications",
"activeTab"
],
// 『与常规类似permissions,但由扩展的用户在运行时授予,而不是提前授予【安全】
"background": {
"service_worker": "background.js"
},
// 『重点』应用于所有页面上下文的js
"content_scripts": [
{
// 匹配的页面
"matches": ["https://fanyi.youdao.com/*"],
"js": [
// 这个 js 将会在 https://fanyi.youdao.com/* 页面中运行
"js/youdao.js"
]
}
]
}
三、各个文件代码
index.html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<title></title>
<link rel="stylesheet" type="text/css" href="./css/style.css" />
</head>
<body>
<div class="container">
<div class="input-box">
<input
type="text"
id="input"
placeholder="请输入你要翻译的内容"
autofocus
/>
<button id="btn">翻译</button>
</div>
<div class="all">
<a id="all">翻译此页面</a>
</div>
<div class="result-box">
<div class="result-content" id="result"></div>
</div>
</div>
</body>
<footer class="footer">
<div id="autoOption">
本翻译由 <a href="https://fanyi.youdao.com/" target="_blank"
>有道翻译</a
> 提供
</div>
</footer>
<script src="./js/translate.js"></script>
</html>
效果如下:
translate.js
const btn = document.getElementById("btn");
const text = document.getElementById("input");
const result = document.getElementById("result");
const all = document.getElementById("all");
/**
* 翻译
* @param {string} text 要翻译的内容
*/
const translate = (text) => {
request(text, (res) => {
let arr = res?.translateResult[0] || [];
let str = "";
arr.map((v) => {
str += v.tgt;
});
result.textContent = str;
});
};
/**
* 请求
* @param {string} str 要翻译的内容
* @param {function} callback 回调函数
* @returns
*/
const request = (str, callback) => {
let xhr = new XMLHttpRequest();
xhr.open(
"GET",
`https://fanyi.youdao.com/translate?&doctype=json&type=AUTO&i=${str}`
);
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
callback(JSON.parse(xhr.responseText));
}
};
};
/**
* 翻译按钮点击事件
* @returns
*/
btn.addEventListener("click", () => {
try {
translate(text.value);
} catch (err) {
result.textContent = "翻译失败" + err;
}
});
/**
* TODO:翻译整页按钮点击事件
* @returns
*/
all.addEventListener("click", () => {
try {
// TODO: 翻译整页
// chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
// chrome.tabs.sendMessage(
// tabs[0].id,
// { type: "searchAllGoogle" },
// function (response) {
// console.log("接收到消息", response);
// if (response) {
// result.textContent = response.text;
// }
// }
// );
// });
} catch (err) {
console.log("翻译整页失败", err);
}
});
youdao.js
// 获取有道翻译页上的元素
const input = document.getElementById('inputOriginal');
const btn = document.getElementById('transMachine');
// 如果 url 携带有参数,那么解码并获取 url 参数
const getUrlParam = (name) => {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if (r != null) return decodeURI(r[2]); return null;
}
// 自动点击翻译按钮
if (getUrlParam('i')) {
input.value = getUrlParam('i');
btn.click();
} else {
input.focus();
}
后面可以点击测试: https://fanyi.youdao.com/index.html#/?i=杀杀杀
background.js
,这个虽然有,但还没有写任何东西,后面会用到
// TODO: 监听来自content-script的消息
// TODO: 翻译整页是可能需要用到发送消息给content-script
// 监听来自content-script的消息
// chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
// console.log("收到来自content-script的消息:");
// console.log(request, sender, sendResponse);
// sendResponse("我是后台,我已收到你的消息:" + JSON.stringify(request));
// });
// 发送消息给content-script
// chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
// // 通过chrome.tabs.sendMessage给content-script发消息
// chrome.tabs.sendMessage(tabId, { greeting: "hello" }, function (response) {
// console.log("收到来自content-script的回复:" + response);
// });
// });
四、打包
打包的时候需要注意,如果你的扩展需要用到第三方库,那么需要在
manifest.json
中的content_scripts
中的js
中引入,否则打包后会报错
打开谷歌扩展页,上面有几个选项 [加载已解压的扩展程序]、[打包扩展程序]、[更新],可以选择[打包扩展程序],选择源码路径,再点击确定就会生成一个 crx
文件,这个文件就是我们的扩展了。