欢迎阅读 Chrome 浏览器扩展开发新手入门!本系列的主要目的是介绍 Chrome 扩展的基本概念以及如何创建扩展。在本系列结束时,你将使用 Javascript 制作一个 Pomodoro 计时器扩展。
要创建任何 Chrome 扩展,我们都必须依赖 Chrome 开发人员的文档。无论是 Chrome API 还是在 Chrome 网站上发布,这都是与扩展相关的唯一真相来源。Chrome API 是特殊的 Javascript 函数或特殊的 manifest 文件(稍后详述)字段,允许我们通过 Chrome 扩展与 Chrome 浏览器进行交互。它们由 chrome
关键字和 API 名称 chrome.[API name]
表示。
Manifest 文件
manifest 文件是每个扩展的核心。通过它,你可以告诉网络浏览器你的 Chrome 浏览器扩展应该做什么,以及扩展由哪些 Javascript、Html 和 Css 文件组成。
根据 Chrome 浏览器开发人员的文档:
扩展的 manifest 是唯一必需的文件,它必须有一个特定的文件名: manifest.json
。它还必须位于扩展的根目录中。清单记录了重要的元数据、定义了资源、声明了权限,并确定了要在后台和页面上运行的文件。
这意味着 manifest 文件是一个 JSON(Javascript 对象符号)格式的文件。
让我们来看一些代码示例:
从本文的 GitHub 代码库中下载代码,在代码编辑器中打开 chrome extension basics
文件夹。它应该是这样的
📦 Chrome-Extension-Series ┣ 🎨 icon.png
创建新的 manifest.json
文件
📦 chrome extension basics ┣ 🎨 icon.png ┣ 📄 **manifest.json**
添加新的 manifest.json
文件后,在其中添加以下代码。
{ "manifest_version": 3, "name": "First Extension", "version": "1.0.0", "description": "My first extension", "icons": { "16": "icon.png", "48": "icon.png", "128": "icon.png" } }
以上是 manifest 文件的基本定义。 manifest_version
告诉浏览器使用哪个版本(3 是最新版本);如何定义 manifest 文件取决于 manifest 版本。最佳做法是使用最新版本,因为旧版本已经过时。名称字段只是我们扩展的名称。version
字段代表 Chrome 浏览器扩展的版本。description
字段只是 Chrome 扩展的描述。icons
字段是一个对象,包含扩展图标在需要时可缩放的不同尺寸。
manifest 文件准备就绪后,就可以在浏览器上加载 Chrome 扩展了。
- 打开 Chrome 浏览器
- 点击最右侧的汉堡包图标(如上图所示)
- 点击扩展程序 > 管理扩展程序
- 然后打开开发者模式
- 点击 “加载已解压的扩展程序”
- 最后,选择包含
manifest.json
文件的chrome extension basics
文件夹
按照上述步骤操作后,你的 Chrome 浏览器扩展应该已经加载到浏览器中了。如果我们查看 Chrome 浏览器的扩展页面,就会看到已加载的扩展。
点击此处了解有关 manifest 文件的更多信息
弹出窗口和浏览器操作
既然我们已经加载了基本扩展,那么就来添加一些交互功能吧。popup 是每个扩展都有的交互元素;当你点击扩展图标时,它就会出现在工具栏上。
根据文档:
使用 chrome.action API 控制 Google Chrome 浏览器工具栏中的扩展图标。
在上面的演示中,你可以看到我们的扩展图标(钉住它)是灰色的,点击它时什么也不会显示,这是因为弹出页面尚未创建。要创建弹出页面,我们必须在 manifest 文件中执行操作设置(action setting)。
{ "manifest_version": 3, "name": "First Extension", "version": "1.0.0", "description": "My first extension", "icons": { "16": "icon.png", "48": "icon.png", "128": "icon.png" }, + "action": { + "default_icon": { + "16": "icon.png", + "24": "icon.png", + "32": "icon.png" + }, + "default_title": "My Extension Action Title", + "default_popup": "popup.html" + } }
传递给 default_popup
字段的值就是点击扩展时将加载的 HTML 文件。现在创建一个名为 popup.html
的新文件。
📦 chrome extension basics ┣ 🎨 icon.png ┣ 📄 manifest.json ┣ 📄 **popup.html**
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>First Extension</title> </head> <body> <h1>My First Extension</h1> </body> </html>
现在我们可以在浏览器中查看弹出页面了。注:在更改 manifest 文件时,必须始终重新加载扩展,如下面的演示所示。
现在,我们已经在弹出页面上加载了 Html 文件,可以使用 Javascript 使其互动,并使用 css 为其设计样式。创建 popup.css
文件,并将 popup.html
连接到该文件。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>First Extension</title> + <link rel="stylesheet" href="popup.css"> ...... </html>
📦 chrome extension basics ┣ 🎨 icon.png ┣ 📄 manifest.json ┣ 📄 popup.html ┣ 📄 **popup.css**
body { width: 400px; height: 400px; } h1 { color: blue; }
用前面的代码设置了 popup.html
的样式后,弹出文字现在是蓝色的了。
现在,让我们使用 Javascript 显示当前时间,使弹出窗口更具互动性。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>First Extension</title> <link rel="stylesheet" href="popup.css"> </head> <body> <h1>My First Extension</h1> <h2 id="time"></h2> </body> <script src="popup.js"></script> </html>
现在创建 popup.js
文件。
📦 chrome extension basics ┣ 🎨 icon.png ┣ 📄 manifest.json ┣ 📄 popup.html ┣ 📄 popup.css ┣ 📄 **popup.js**
const timeElement = document.getElementById('time') const currentTime = new Date().toLocaleTimeString() timeElement.textContent = `The time is: ${currentTime}`
现在,当你点击弹出窗口时,扩展程序就会显示当前时间。
让我们使用 chrome.action
API 方法之一来增强扩展的交互性。让我们在显示文本 TIME
的弹出窗口上设置一个徽章。我们将使用 setBadgeText
方法。
在 popup.js
中添加以下代码。
const timeElement = document.getElementById('time') const currentTime = new Date().toLocaleTimeString() timeElement.textContent = `The time is: ${currentTime}` chrome.action.setBadgeText({ text: "TIME", }, () => { console.log('Finished setting badge text') })
现在,我们的扩展有了一个显示文本 TIME
的 icon,控制台也会显示该信息。这样就能正常工作了。
Options 页面
选项页面是用户与 Chrome 浏览器扩展互动的另一种方式,因此让我们为扩展创建一个选项页面。如果右键单击扩展图标,就会显示 options
,但由于尚未创建选项页面,所以选项是禁用的。
要创建选项页面,我们必须在 manifest.json
中添加 options
字段。
{ "manifest_version": 3, "name": "First Extension", "version": "1.0.0", "description": "My first extension", "icons": { "16": "icon.png", "48": "icon.png", "128": "icon.png" }, "action": { "default_icon": { "16": "icon.png", "24": "icon.png", "32": "icon.png" }, "default_title": "My Extension Action Title", "default_popup": "popup.html" }, + "options_page": "options.html" }
现在创建一个 options.html
文件,这将是我们的扩展选项页面文件。
📦 chrome extension basics ┣ 🎨 icon.png ┣ 📄 manifest.json ┣ 📄 popup.html ┣ 📄 popup.css ┣ 📄 popup.js ┣ 📄 options.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>My Extension Options</title> </head> <body> <h1>My Extension Options</h1> </body> </html>
现在,我们可以右键单击图标,选择 “options
” 选项,查看扩展的选项页面。
让我们为 options 页面设计风格并添加互动性。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="options.css"> <title>My Extension Options</title> </head> <body> <h1>My Extension Options</h1> <input id="name-input" type="text" placeholder="Enter your name!" /> <button id="save-btn">Save Options</button> </body> + <script src="options.js"></script> </html>
我们的项目目录结构如下:
📦 chrome extension basics ┣ 🎨 icon.png ┣ 📄 manifest.json ┣ 📄 popup.html ┣ 📄 popup.css ┣ 📄 popup.js ┣ 📄 options.html ┣ 📄 options.css ┣ 📄 options.js
在 options.css
中添加以下代码。
h1 { color: green; }
在 options.js
中添加以下代码。
const nameInput = document.getElementById("name-input") const saveBtn = document.getElementById("save-btn") saveBtn.addEventListener("click", () => { console.log(nameInput.value) })
当点击 save Options
按钮时,options.js
中的代码会获取输入框中的文本并登录到控制台。
Chrome 存储 API
我们可以与用户输入选项页面的数据进行交互,但目前还没有办法在选项页面上存储任何数据。我们需要一个存储系统来存储选项页面上的数据,并在弹出页面上访问这些数据。为此,我们使用了 chrome 存储 API。
根据文档:
使用 chrome.storage
API 来存储、检索和跟踪用户数据的更改。
存储 API 有一些注意事项,但您可以认为它的工作原理与任何使用 Javascript 的基本本地存储 API 类似。
在使用存储 API 之前,有两件事需要了解:
- 使用存储 API 需要用户许可(添加到 manifest 文件中)。
- 主要有两种存储 API
chrome.storage.sync
APIchrome.storage.local
API两者的主要区别在于,chrome.storage.sync
API 会在不同的浏览器会话中同步所有 Chrome 浏览器存储数据,而chrome.storage.local
则只针对单个会话。不过,在处理用户选项时,使用chrome.storage.sync
是个不错的做法,因为您希望数据能在不同的浏览器实例中保存。
在使用存储 API 之前,我们必须在 manifest.json
文件的 permissions
字段中添加存储 API。
{ "manifest_version": 3, "name": "First Extension", "version": "1.0.0", "description": "My first extension", "icons": { "16": "icon.png", "48": "icon.png", "128": "icon.png" }, "action": { "default_icon": { "16": "icon.png", "24": "icon.png", "32": "icon.png" }, "default_title": "My Extension Action Title", "default_popup": "popup.html" }, "options_page": "options.html", + "permissions": ["storage"] }
然后在 options.js
中添加以下代码。
const nameInput = document.getElementById("name-input") const saveBtn = document.getElementById("save-btn") saveBtn.addEventListener("click", () => { const name = nameInput.value; chrome.storage.sync.set({ name, }, () => { console.log(`Name is set to ${name}`) }) })
在前面的代码中,我们通过 set
方法使用 Chrome 存储 API 来存储用户输入,该方法接受要存储的值和回调。
现在,如果你刷新扩展,进入选项页面并检查控制台,存储的值就会被记录下来。
如果我们刷新页面,你会发现即使我们在存储中设置了输入值,也没有将 name
输入的内部值更新为保存的值。
为此,我们将使用 get
方法来获取值(set
用于存储值,get
用于获取值)。
const nameInput = document.getElementById('name-input') const saveBtn = document.getElementById('save-btn') ..... chrome.storage.sync.get(['name'], (res) => { nameInput.value = res.name ?? "???" })
get
方法需要以下参数:
- 一个包含要检索的键值(
[name]
)的数组(键值应与set
方法中的键值一致)。 - 回调函数,其参数代表一个对象,其中包含最初用
set
方法存储的值的键值绑定。
例如:假设存储在 name
键中的值是 john
chrome.storage.sync.get(['name'], (res) => { console.log(res) }) //logs {name: 'john'}
我们已经成功地在选项页面上存储并获取了用户输入。让我们在弹出页面上显示用户输入。在 popup.html
中添加。
...... <body> <h1>My First Extension</h1> <h2 id="time"></h2> + <h2 id="name"></h2> </body> <script src="popup.js"></script> </html>
在 popup.js
中添加。
const timeElement = document.getElementById('time') const nameElement = document.getElementById("name"); const currentTime = new Date().toLocaleTimeString() timeElement.textContent = `The time is: ${currentTime}` chrome.action.setBadgeText({ text: "TIME", }, () => { console.log('Finished setting badge text') }) chrome.storage.sync.get(["name"], (res) => { const name = res.name ?? "???" nameElement.textContent = `Your name is: ${res.name}` })
在前面的代码中,我们选择了显示存储值的元素( nameElement
),从存储中获取值,并将其设置为所选元素的 textContent
。
现在,我们可以在弹出页面的选项页中查看输入的值。
后台脚本和 Service Workers
根据文件:
扩展是基于事件的程序,用于修改或增强 Chrome 浏览器的浏览体验。事件是浏览器触发器,例如导航到新页面、删除书签或关闭标签页。扩展程序会在后台脚本中监控这些事件,然后根据指定指令做出反应。
后台脚本是一个 Javascript 文件,在安装 Chrome 浏览器扩展时在后台运行。服务工作者在运行一段时间后总是处于空闲状态;点击此处了解有关服务工作者的更多信息。
让我们为扩展实现后台脚本:
在我们的 manifest.json
文件中添加 ” background
” 字段。
{ "manifest_version": 3, "name": "First Extension", "version": "1.0.0", "description": "My first extension", "icons": { "16": "icon.png", "48": "icon.png", "128": "icon.png" }, "action": { "default_icon": { "16": "icon.png", "24": "icon.png", "32": "icon.png" }, "default_title": "My Extension Action Title", "default_popup": "popup.html" }, "options_page": "options.html", "permissions": ["storage"], "background": { "service_worker": "lbackground.js" } }
创建 background.js
文件
📦 chrome extension basics ┣ 🎨 icon.png ┣ 📄 manifest.json ┣ 📄 popup.html ┣ 📄 popup.css ┣ 📄 popup.js ┣ 📄 options.html ┣ 📄 options.css ┣ 📄 options.js ┣ 📄 background.js
consol.log("Hello from the background script!");
上面的演示显示,添加后台脚本后,当你进入扩展页面并刷新扩展时,就会看到 inspect views:service worker 选项。点击 inspect 时,我们会看到它显示了从 background.js
记录到控制台的信息。
关于后台脚本,需要注意以下几点:
- 点击
service worker
选项时显示的 devtools 环境与检查弹出式页面时看到的 devtools 环境并无不同,只是与我们的弹出式页面相比,我们没有Elements
标签,因为后台脚本全部是 Javascript(只有 Javascript 相关的 devtools 可用)。 -
this
关键字并不指向后台脚本中的window
对象,而是指向ServiceWorkerGlobalScope
对象(这意味着它是一个服务工作者)。因此请注意,服务工作者的功能与 HTML 文档中的普通 Javascript 文件不同。
/// background.js console.log(this) // logs > ServiceWorkerGlobalScope
现在,让我们在后台脚本中设置一个计时器功能。
/// background.js let time = 0 setInterval(() => { time += 1 console.log(time) }, 1000)
前面的代码通过 setInterval()
函数每秒增加一个 time
变量。
Chrome Alarms API
根据文档
使用 chrome.alarms
API 可安排代码定期或在未来某个指定时间运行。
这意味着,即使服务 Worker 处于休眠状态, chrome.alarms
API 也允许代码在后台脚本中运行。
让我们在 manifest 文件中启用此 API 的权限。
/// manifest.json ...... "options_page": "options.html", "permissions": ["storage", "alarms"] "background": { "service_worker": "background.js" } .....
现在,让我们在 background.js
中使用 chrome.alarms
API 重新实现计时器功能。
chrome.alarms.create({ periodInMinutes: 1 / 60, }) chrome.alarms.onAlarm.addListener((alarm) => { chrome.storage.local.get(["timer"], (res) => { const time = res.timer ?? 0 chrome.storage.local.set({ timer: time + 1 }) console.log(time) }) })
前面的代码使用 chrome.alarms.create
方法通过 periodInMinutes
属性创建了一个每秒触发一次的警报。然后使用 onAlarm
方法监听并响应警报。最后,我们使用 chrome.storage
设置和增加计时器,并将其记录到控制台。
Chrome Notifications API
根据文档
使用 chrome.notifications
API 使用模板创建丰富的通知,并在系统托盘中向用户显示这些通知。
这意味着 chrome.notifications
API 可用于为我们的扩展创建桌面通知。不过,需要注意的是,在 manifest V3 中,后台脚本是一个服务工作者。在 service Worker 中,有一个名为 ServiceWorkerRegistration 的对象。它有一个内置的显示通知函数( ServiceWorkerRegistration.showNotification()
),可以在桌面上向用户显示通知。
现在,让我们使用 API 在特定时间段过去后通知用户。让我们在 manifest 文件中启用此 API 的权限。
/// manifest.json ...... "options_page": "options.html", + "permissions": ["storage", "alarms", "notifications"] "background": { "service_worker": "background.js" } .....
在 background.js
chrome.alarms.create({ periodInMinutes: 1 / 60, }) chrome.alarms.onAlarm.addListener((alarm) => { chrome.storage.local.get(["timer"], (res) => { const time = res.timer ?? 0 chrome.storage.local.set({ timer: time + 1 }) if(time % 5 == 0) { this.registration.showNotification('My first Extension', { body: '1 second has passed', icon: 'icon.png', }) } }) })
在前面的代码中,我们通过 serviceWorker 对象( this
)访问 showNotification()
函数。该函数接受两个参数–一个表示通知标题的字符串和一个包含不同通知配置选项的对象。在我们的例子中,通知标题是 “My first Extension“;配置对象包含 body
属性(在通知正文中显示的文本)和 icon
(通知图标)。我们的通知被包裹在一个 if
语句中,该语句每 5 秒触发一次通知。
现在刷新扩展页面并等待 5 秒钟,你就会在桌面上看到通知。
待续…
这就是本两部分系列的第一部分。在下一部分中,我们将构建一个 Pomodoro 计时器扩展。
你可以从 GitHub 代码库中下载本文的示例代码文件。
同时,如果你想更深入地了解本文中的概念,请查看下面的 “参考阅读” 部分。
评论留言