一步步构建 Firefox 扩展
最近我一直在使用dev.to上的阅读列表。它是个不错的工具,但我习惯于将文章保存到Pocket中以便以后阅读。
在本文中,我们将创建一个 Firefox 扩展,以便自动将文章同时添加到您的 Dev.to 阅读列表和 Pocket 帐户中。
它看起来是这样的(扩展文件可在本文末尾找到):
该扩展程序要求您已经在浏览器中连接到 Pocket 帐户(因此我们不必处理 API 身份验证)。
什么是浏览器扩展?
浏览器扩展是 Firefox 浏览特定页面时执行的一组脚本。这些脚本可以修改页面的 HTML、CSS 和 JavaScript,并可以访问特定的JavaScript API(例如书签、身份等)。
脚本分为两种类型:内容脚本和后台脚本。内容脚本在页面内执行,而后台脚本执行长期操作并维护长期状态。后台脚本也可以访问所有 WebExtension API。
以下是该项目的最终文件结构:
- manifest.json (配置文件)
- background.js (我们的背景脚本)
- devtopocket.js (在 dev.to 页面上执行的内容脚本)
- 图片/
内容和背景脚本
我们的项目中有两个脚本:一个处理后台工作(发送 Ajax 请求),另一个(内容脚本)在“阅读列表”Dev.to 按钮上注册点击事件:
内容脚本
内容脚本(devtopocket.js)注册点击并将请求发送到我们的后台脚本。
devtopocket.js
document.getElementById("reaction-butt-readinglist").addEventListener("click", function() {
if(window.confirm("Do you want to save this article in Pocket?")) {
sendBackgroundToPocket();
}
});
该sendBackgroundToPocket
方法需要与后台脚本进行通信并要求其发送Ajax请求。
browser.runtime
为我们的扩展脚本之间提供了一个双向通信通道。browser.runtime.sendMessage
在该通道上发送一条消息,并返回一个等待另一端响应的 Promise。一旦我们收到响应(意味着 Ajax 请求已完成),就会向用户显示一条消息(参见上面的 gif):
devtopocket.js
function sendBackgroundToPocket(){
browser.runtime.sendMessage({"url": window.location.href}).then(function(){
document.getElementById("article-reaction-actions").insertAdjacentHTML("afterend", "<div id='devtopocket_notification' style='text-align: center;padding: 10px 0px 28px;'>This article has been saved to Pocket!</div>")
setTimeout(function(){
document.getElementById("devtopocket_notification").remove()
}, 2000)
});
}
背景脚本
后台脚本用于编写不依赖于打开特定网页的耗时操作。
这些脚本随扩展程序一起加载,并一直执行,直到扩展程序被禁用或卸载。
我们的后台脚本(background.js)有两个作用:
- 发送 Ajax 请求
- 通过 History API 对 URL 更改做出反应
在扩展配置(下面的manifest.json)中,我们将说“在与url模式匹配的页面上加载devtopocket.js”,当我们直接浏览到文章页面时它就会起作用。
dev.to 网站的“问题”在于它使用 HTML5 History API 来浏览页面(每个单页 Web 应用都如此)。如果页面未完全重新加载,Firefox 不会监听 URL 变化,因此不会执行我们的内容脚本。因此,我们需要一个后台脚本通过 History API 监听 URL 变化,并在需要时手动执行前端脚本。
我们通过使用webNavigation API来监听 url 的变化:
background.js
browser.webNavigation.onHistoryStateUpdated.addListener(function(details) {
browser.tabs.executeScript(null,{file:"devtopocket.js"});
}, {
url: [{originAndPathMatches: "^.+://dev.to/.+/.+$"}]
});
{originAndPathMatches: "^.+://dev.to/.+/.+$"}
将监听器限制为特定的目标 URL 模式(与我们将在 中定义的模式相同manifest.json
)。
该browser.tabs.executeScript
方法在当前选项卡中加载内容脚本。
后台脚本需要从我们的内容脚本中获取一条消息(当单击“阅读列表”按钮时):
background.js
function handleMessage(message, sender, sendResponse) {
if(message.url) {
sendToPocket(message.url, sendResponse)
return true;
}
}
browser.runtime.onMessage.addListener(handleMessage)
sendToPocket
接收消息时会调用该方法。
为了将 URL 保存到 Pocket,我们将调用 Pocket 提供的现有保存页面 ( https://getpocket.com/save )。一个经典的 Ajax 请求即可完成此操作:
function sendToPocket(url, sendResponse) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
sendResponse();
}
};
xhr.open("GET", "https://getpocket.com/save?url="+url, true);
xhr.send();
}
您可能会看到跨域请求问题,我们稍后将使用扩展权限解决该问题。
清单
manifest.json
是我们的扩展配置文件。它类似于package.json
JavaScript Web 应用中的配置文件或 Android 应用中的 AndroidManifest.xml 文件。您可以定义项目的版本和名称、所需的权限以及组成扩展程序的 JavaScript 源文件。
首先我们编写应用程序定义:
{
"manifest_version": 2,
"name": "DevToPocket",
"version": "1.0.0",
"description": "Send your DEV.to reading list to Pocket",
"icons": {
"48": "icons/devtopocket-48.png"
},
...
}
至少提供一个 48x48 的图标,如果您提供更多尺寸,Firefox 会根据您的屏幕分辨率尝试使用最佳的图标尺寸。我们将使用这个图标:
然后我们定义我们的权限:
{
...
"permissions": [
"storage",
"cookies",
"webNavigation",
"tabs",
"*://dev.to/*/*",
"*://getpocket.com/*"
]
}
您可以在Mozilla 文档中找到权限列表。
权限中的 URL 赋予了我们的扩展程序扩展权限。在我们的例子中,它允许我们从 dev.to 访问 getpocket.com,且不受跨域限制。我们可以通过 dev.to 注入脚本,tabs.executeScript
并且能够访问 getpocket.com 的 Cookie,从而对 Ajax 请求进行身份验证。完整的主机权限列表可在此处查看。
完整manifest.json
文件:
{
"manifest_version": 2,
"name": "DevToPocket",
"version": "1.0.0",
"description": "Send your DEV.to reading list to Pocket",
"icons": {
"48": "icons/devtopocket-48.png"
},
"content_scripts": [
{
"matches": ["*://dev.to/*/*"],
"js": ["devtopocket.js"]
}
],
"background": {
"scripts": ["background.js"]
},
"permissions": [
"storage",
"cookies",
"webNavigation",
"tabs",
"*://dev.to/*/*",
"*://getpocket.com/*"
]
}
运行扩展
为了运行您的扩展,我们将使用web-ext命令行:https://github.com/mozilla/web-ext
这是一个帮助构建、运行和测试 WebExtensions 的命令行工具。
npm install --global web-ext
然后在终端中,在项目文件夹中运行以下命令:
web-ext run
它将启动一个浏览器,并临时加载你的扩展程序。当你进行一些更改时,扩展程序会自动重新加载。
签署扩展
要在其他人的浏览器中安装您的扩展程序,您需要打包并签署该扩展程序。
首先在Mozilla 开发者中心创建一个开发者帐户,然后在此处检索您的 API 凭据:https://addons.mozilla.org/en-US/developers/addon/api/key/
运行web-ext sign 命令:
web-ext sign --api-key=user:XXX --api-secret=YYY
您的扩展文件随后将在web-ext-artifacts/devtopocket-XXX-an+fx.xpi中可用。在 Firefox 中打开该文件进行安装。
完整的源代码可以在 GitHub 上找到:https://github.com/scleriot/devtopocket
您可以下载并安装最新版本:https://github.com/scleriot/devtopocket/releases/latest
此扩展程序也适用于 Android 版 Firefox!
鏂囩珷鏉ユ簮锛�https://dev.to/scleriot/build-a-firefox-extension-step-by-step-5dbl