
在本部分中,我们将使用在中学到的 API 创建一个 Pomodoro 计时器扩展。样式设计将使用 CSS 完成。本项目的完整代码可找到。
大家现在都知道,在构建扩展时,创建的之一个文件是 manifest 文件。
创建一个新的 manifest.json 文件。
Chrome-Extension-Series ┣ icon.png ┣ manifest.json
{
"manifest_version": 3,
"name": "Pomodoro Timer",
"version": "1.0",
"description": "Assists you to focus and get things done",
"icons": {
"16": "icon.png",
"48": "icon.png",
"128": "icon.png"
},
"action": {
"default_icon": "icon.png",
"default_title": "Pomodoro Timer",
"default_popup": "popup/popup.html"
}
}
现在我们已经设置好扩展名,让我们创建弹出页面。popup.html 文件放在 popup 文件夹中,为我们的项目添加结构。
Chrome-Extension-Series ┣ icon.png ┣ manifest.json ┣ popup ┃ ┣ 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"> <link rel="stylesheet" href="./popup.css"> <title>Pomodoro Timer</title> </head> <body> <div class="2345HAOa58caab8c16e880d header"> <img src="../icon.png"> </div> <h1>00:00</h1> <button>Start Timer</button> <button>Add Task</button> <div> <input type="text"> <input type="button" value="X"> </div> </body> <script src="popup.js"></script> </html>
在前面的代码中,我们定义了弹出页面的结构。它与 pop.css 文件相连,用于设计样式,与 pop.js 文件相连,用于交互。
让我们添加一些样式,并创建与 popup.html 链接的 popup.css 文件。
Chrome-Extension-Series ┣ icon.png ┣ manifest.json ┣ popup ┣ popup.css
body {
height: 400px;
width: 300px;
}
.header {
display: flex;
justify-content: center;
height: 40px;
}
在浏览器上重新载入扩展页面并点击弹出窗口,弹出窗口会显示 popup.html。

通过任务列表功能,我们可以添加和删除任务。
在 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"> <link rel="stylesheet" href="./popup.css"> <title>Pomodoro Timer</title> </head> <body> <div class="2345HAO300f95493ae49754 header"> <img src="../icon.png"> </div> <h1>00:00</h1> <button>Start Timer</button> <button id="add-task-btn">Add Task</button> + <div id="task-container"> <input type="text"> <input type="button" value="X"> + </div> </body> <script src="popup.js"></script> </html>
在 popup.js 中
Chrome-Extension-Series ┣ icon.png ┣ manifest.json ┣ popup ┣ popup.css ┣ popup.js
const addTaskBtn = document.getElementById('add-task-btn')
addTaskBtn.addEventListener('click', () => addTask())
function addTask() {
const taskRow = document.createElement('div')
// Create text input
const text = document.createElement('input')
text.type = 'text'
text.placeholder = 'Enter a task..'
// Create delete button
const deleteBtn = document.createElement('input')
deleteBtn.type = 'button'
deleteBtn.value = 'X'
// append input elements to taskRow
taskRow.appendChild(text)
taskRow.appendChild(deleteBtn)
// append taskRow to taskContainer
const taskContainer = document.getElementById('task-container')
taskContainer.appendChild(taskRow)
}
在前面的代码中,我们通过 Add Task 按钮的 id 选择了该按钮,添加了一个 click 事件监听器和一个回调函数,该函数用于在用户界面中添加一个新任务。

在 popup.js 中
- const addTaskBtn = document.getElementById('add-task-btn')
addTaskBtn.addEventListener('click', () => addTask())
- function addTask() {
const taskRow = document.createElement('div')
// Create text input
- const text = document.createElement('input')
text.type = 'text'
text.placeholder = 'Enter a task..'
// Create delete button
- const deleteBtn = document.createElement('input')
deleteBtn.type = 'button'
deleteBtn.value = 'X'
// append input elements to taskRow
taskRow.appendChild(text)
taskRow.appendChild(deleteBtn)
// append taskRow to taskContainer
- const taskContainer = document.getElementById('task-container')
taskContainer.appendChild(taskRow)
}
// array to store tasks
let tasks = []
const addTaskBtn = document.getElementById('add-task-btn')
addTaskBtn.addEventListener('click', () => addTask())
// render tasks
function renderTask(taskNum) {
const taskRow = document.createElement('div')
// Create text input
const text = document.createElement('input')
text.type = 'text'
text.placeholder = 'Enter a task..'
//Set and track input values of tasks in the array
text.value = tasks[taskNum]
text.addEventListener('change', () => {
tasks[tasksNum] = text.value
})
// Create delete button
const deleteBtn = document.createElement('input')
deleteBtn.type = 'button'
deleteBtn.value = 'X'
// delete task
deleteBtn.addEventListener('click', () => {
deleteTask(taskNum)
})
// append input elements to taskRow
taskRow.appendChild(text)
taskRow.appendChild(deleteBtn)
// append taskRow to taskContainer
const taskContainer = document.getElementById('task-container')
taskContainer.appendChild(taskRow)
}
function addTask() {
const tasksNum = tasks.length
// add tasks to array
tasks.push('')
renderTask(tasksNum)
}
// delete and re-render tasks after mutation
function deleteTask(tasksNum) {
tasks.splice(tasksNum, 1)
renderTasks()
}
function renderTasks() {
const taskContainer = document.getElementById('task-container')
taskContainer.textContent = ''
tasks.forEach((taskText, tasksNum) => {
renderTask(tasksNum)
})
}
在前面的代码中,我们对 popup.js 文件进行了重大修改。让我们来了解一下发生了什么:
tasks )来存储任务Add Task 按钮时,rendTask() 函数会创建一个新任务并将其呈现在 DOM(文档对象模型)上。addTask() 函数是添加任务按钮的事件处理程序X )时, deleteTask() 函数会删除任务。renderTasks() 函数都会更新任务数组,即重新渲染用户界面。现在,如果我们检查扩展,就可以添加和删除任务,但数据不是持久的,我们需要实现存储。

首先,我们在 manifest.json 中设置了使用存储 API 所需的权限。
{
"manifest_version": 3,
"name": "Pomodoro Timer",
"version": "1.0",
"description": "Assists you to focus and get things done",
"icons": {
"16": "icon.png",
"48": "icon.png",
"128": "icon.png"
},
"action": {
"default_icon": "icon.png",
"default_title": "Pomodoro Timer",
"default_popup": "popup/popup.html"
},
+ "permissions": ["storage"]
}
在 popup.js 中
// array to store tasks
let tasks = []
const addTaskBtn = document.getElementById('add-task-btn')
addTaskBtn.addEventListener('click', () => addTask())
// set default storage value for the tasks
chrome.storage.sync.get(['tasks'], (res) => {
tasks = res.tasks ? res.tasks : []
renderTasks()
})
// save tasks
function saveTasks() {
chrome.storage.sync.set({
tasks: tasks,
})
}
// render tasks
function renderTask(taskNum) {
const taskRow = document.createElement('div')
// Create text input
const text = document.createElement('input')
text.type = 'text'
text.placeholder = 'Enter a task..'
//Set and track input values of tasks in the array
text.value = tasks[taskNum]
text.addEventListener('change', () => {
tasks[taskNum] = text.value
// call saveTask whenever a value changes
saveTasks()
})
....
function addTask() {
const tasksNum = tasks.length
// add tasks to array
tasks.push('')
renderTask(tasksNum)
saveTasks()
}
// delete and re-render tasks after mutation
function deleteTask(tasksNum) {
tasks.splice(tasksNum, 1)
renderTasks()
saveTasks()
}
在前面的代码中,我们使用 Chrome 浏览器的存储 API 来存储扩展数据。
saveTasks() 函数将我们的任务数组存储在存储 API 中。renderTask() 中,每当添加或删除一个任务时,都会通过 saveTasks() 进行保存, addTask() 和 deleteTask() 也是如此。任务功能已完成;我们可以删除、添加和存储任务。

计时器功能要求我们创建一个后台脚本,并使用警报和通知在计时器时间到时通知用户。
让我们在 manifest.json 中设置所需的权限。
{
"manifest_version": 3,
"name": "Pomodoro Timer",
"version": "1.0",
"description": "Assists you to focus and get things done",
"icons": {
"16": "icon.png",
"48": "icon.png",
"128": "icon.png"
},
"action": {
"default_icon": "icon.png",
"default_title": "Pomodoro Timer",
"default_popup": "popup/popup.html"
},
+ "permissions": ["storage", "alarms", "notifications"],
+ "background": {
"service_worker": "background.js"
}
}
为我们的背景脚本创建 background.js 文件。
Chrome-Extension-Series ┣ icon.png ┣ manifest.json ┣ popup ┣ popup.css ┣ popup.js ┣ background.js
// create an alarm to notify user when time is up
chrome.alarms.create("pomodoroTimer", {
periodInMinutes: 1 / 60
})
// alarm listener
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === "pomodoroTimer") {
chrome.storage.local.get(["timer", "isRunning"], (res) => {
if (res.isRunning) {
let timer = res.timer + 1
console.log(timer)
chrome.storage.local.set({
timer,
})
}
})
}
})
// storage to set and track timer variables on load
chrome.storage.local.get(["timer", "isRunning"], (res) => {
chrome.storage.local.set({
timer: "timer" in res ? res.timer : 0,
isRunning: "isRunning" in res ? res.isRunning : false,
})
})
在 popup.js 中
// array to store tasks
let tasks = []
// Start Timer Button
+ const startTimerBtn = document.getElementById("start-timer-btn");
startTimerBtn.addEventListener("click", () => {
chrome.storage.local.get(["isRunning"], (res) => {
chrome.storage.local.set({
isRunning: !res.isRunning,
}, () => {
startTimerBtn.textContent = !res.isRunning ? "Pause Timer" : "Start Timer"
})
})
})
在前面的代码中:
timer 和 isRunning 变量用于跟踪时间和计时器的状态,它们作为应用程序初始数据存储在存储器中。onAlarm.addListener )警报,并在 isRunning 为 true 时递增 timer ,然后将 timer 记录到控制台。popup.js 中,我们监听 Start Timer 按钮的 click 事件,并获取当前的 isRunning 值。如果当前值为 true,则设置为 false,计时器暂停;如果当前值为 false,则设置为 true,重置计时器。
现在,让我们来开发重置计时器功能。创建重置按钮 popup.html 的标记。
<body> <div class="2345HAO8c2ed098ee72c11d header"> <img src="../icon.png"> </div> <h1>00:00</h1> <button id="start-timer-btn">Start Timer</button> + <button id="reset-timer-btn">Reset Timer</button> <button id="add-task-btn">Add Task</button> <div id="task-container"> <input type="text"> <input type="button" value="X"> </div> </body>
在 popup.js 中
// Reset Timer Button
const resetTimerBtn = document.getElementById("reset-timer-btn")
resetTimerBtn.addEventListener("click", () => {
chrome.storage.local.set({
// reset variables
timer: 0,
isRunning: false
}, () => {
// reset start button text-content
startTimerBtn.textContent = "Start Timer"
})
})
在前面的代码中,我们执行了以下操作:
id 选择 DOM 上的 Reset Timer 按钮。click 事件监听器,并使用回调函数重置 timer 和存储中的 isRunning 变量。Start Timer 按钮文本设置为字符串 “Start Timer”。
到目前为止,我们一直在控制台上记录计时器的值。让我们在弹出页面上显示时间。
在 popup.html 中
<body> <div class="2345HAOee72c11df6ba66e4 header"> <img src="../icon.png"> </div> + <h1 id="time">00:00</h1> <button id="start-timer-btn">Start Timer</button> <button id="reset-timer-btn">Reset Timer</button> <button id="add-task-btn">Add Task</button> <div id="task-container"> <input type="text"> <input type="button" value="X"> </div> </body>
在 popup.js 中
// array to store tasks
let tasks = [];
const time = document.getElementById("time");
// Update time every 1sec
function updateTime() {
chrome.storage.local.get(["timer"], (res) => {
const time = document.getElementById("time")
// get no. of minutes & secs
const minutes = `${25 - Math.ceil(res.timer / 60)}`.padStart(2, "0");
let seconds = "00";
if (res.timer % 60 != 0) {
seconds = `${60 -res.timer % 60}`.padStart(2, "0");
}
// show minutes & secs on UI
time.textContent = `${minutes}:${seconds}`
})
}
updateTime()
setInterval(updateTime, 1000)
// Start Timer Button
在前面的代码中,我们在弹出页面上显示时间,并在点击任何影响计时器的按钮时更新时间。让我们更好地理解这段代码:
updateTime() 函数中进行了一些数学运算;计时器的值是从存储空间中获取的。minute 变量中。 25 - res.timer / 60 – 例如,如果我们的计时器值(res.timer)是 120 秒,120 / 60 = 2,那么 25 - 2 = 23,即时钟将剩下 23 分钟。res.timer )除以 60。minutes 和 seconds 的值通过 time.textContent 显示在用户界面上。updateTime() 会在弹出窗口加载时自动调用,并通过 setInterval 每 1sec 调用一次。现在可以在弹出窗口中看到时间了。

现在,让我们设置通知,以便在时间到时通知用户。
在 background.js 中
// alarm listener
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === "pomodoroTimer") {
chrome.storage.local.get(["timer", "isRunning"], (res) => {
if (res.isRunning) {
let timer = res.timer + 1
let isRunning = true
if(timer === 25) {
this.registration.showNotification('Pomodoro Timer', {
body: "25 minutes has passed",
icon: "icon.png"
})
timer = 0
isRunning = false
}
chrome.storage.local.set({
timer,
isRunning,
})
}
})
}
})
在前面的代码中,我们使用 if 语句检查计时器是否达到 25 分钟,然后注册一个通知。计时器到期后,timer 值重置为 0 ,isRunning 为 false,从而暂停计时器。要测试此功能,请将计时器的默认值设为 10secs(记住,这只是为了测试目的,所以我们不会等待 25 分钟)。在上述代码的 if 语句中,将计时器值改为 10secs– if(timer === 10)。现在重新启动计时器,10secs 后,您将看到一条通知。

现在,我们已经具备了该扩展的基本功能:我们可以启动计时器、暂停计时器、重置计时器以及删除和添加任务。现在,让我们使扩展功能更具定制性,这样用户就可以根据自己的需求进行定制–有些用户可能希望专注于更长或更短的时间段。我们必须创建一个选项页面,以便用户配置扩展。用户可以将会话的最长时间设置为 1 小时(60 分钟),最短时间设置为 1 分钟。
在 manifest.json 中添加 options_page 文件。
{
"manifest_version": 3,
"name": "Pomodoro Timer",
"version": "1.0",
"description": "Assists you to focus and get things done",
"icons": {
"16": "icon.png",
"48": "icon.png",
"128": "icon.png"
},
"action": {
"default_icon": "icon.png",
"default_title": "Pomodoro Timer",
"default_popup": "popup/popup.html"
},
"permissions": ["storage", "alarms", "notifications"],
"background": {
"service_worker": "background.js"
},
"options_page": "options/options.html"
}
我们将 options.html 文件放在一个文件夹中,以便为项目添加结构。
创建一个 options 文件夹,并在其中添加 options.html 和 options.css 文件。
Chrome-Extension-Series ┣ icon.png ┣ manifest.json ┣ popup ┣ options ┃ ┣ options.css ┃ ┣ 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"> `<link rel="stylesheet" href="options.css"> <title>Pomodoro Timer Extension Options</title> </head> <body> <h1>Pomodoro Timer Options</h1> <input id="time-option" type="number" min="1" max="60" value="25"> <button id="save-btn">Save Options</button> </body> <script src="options.js"></script> </html>
在 HTML 中,我们有一个最小值为 1 (1 分钟)、更大值为 60 (60 分钟)的数字 input 输入框。value 属性包含 25(25 分钟)的默认计时器值。Save options 按钮可以让我们保存选项。

在 options.js 中
// Add validation
const timeOption = document.getElementById('time-option')
timeOption.addEventListener('change', (event) => {
const val = event.target.value
if (val < 1 || val > 60) {
timeOption.value = 25
}
})
// Save Option
const saveBtn = document.getElementById('save-btn')
saveBtn.addEventListener('click', () => {
chrome.storage.local.set({
timeOption: timeOption.value,
timer: 0,
isRunning: false,
})
})
// Load Saved Option
chrome.storage.local.get(['timeOption'], (res) => {
timeOption.value = res.timeOption
})
在前面的 option.js 代码中:
1 或大于 60。isRunning 参数设置为 false。现在让我们通过后台脚本读取已保存的选项,并将其显示在弹出页面上。
在 background.js 中
// create an alarm to notify user when time is up
chrome.alarms.create("pomodoroTimer", {
periodInMinutes: 1 / 60
})
// alarm listener
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === "pomodoroTimer") {
chrome.storage.local.get(["timer", "isRunning", "timeOption"], (res) => {
if (res.isRunning) {
let timer = res.timer + 1
let isRunning = true
// console.log(timer)
if(timer === 60 * res.timeOption) {
this.registration.showNotification('Pomodoro Timer', {
- body: "25 minutes has passed",
+ body: `${res.timeOption} minutes has passed!`,
icon: "icon.png"
})
timer = 0
isRunning = false
}
chrome.storage.local.set({
timer,
isRunning,
})
}
})
}
})
// storage to set and track timer variables
chrome.storage.local.get(["timer", "isRunning", "timeOption"], (res) => {
chrome.storage.local.set({
timer: "timer" in res ? res.timer : 0,
timeOption: "timeOption" in res ? res.timeOption : 25,
isRunning: "isRunning" in res ? res.isRunning : false,
})
})
在 popup.js 中
// array to store tasks
let tasks = [];
const time = document.getElementById("time");
// Update time every 1sec
function updateTime() {
chrome.storage.local.get(["timer", "timeOption"], (res) => {
const time = document.getElementById("time")
// get no. of minutes & secs
const minutes = `${ res.timeOption - Math.ceil(res.timer / 60)}`.padStart(2, "0");
let seconds = "00";
if (res.timer % 60 != 0) {
seconds = `${60 -res.timer % 60}`.padStart(2, "0");
}
// show minutes & secs on UI
time.textContent = `${minutes}:${seconds}`
})
}
如果通过设置选项来测试扩展,就会在 popup 页面上看到新值。

最后,让我们来设计扩展的样式。复制并粘贴下面的代码。
在 popup.css 中
body {
height: 400px;
width: 350px;
background: hsla(238, 100%, 71%, 1);
background: linear-gradient(
90deg,
hsla(238, 100%, 71%, 1) 0%,
hsla(295, 100%, 84%, 1) 100%
);
background: -moz-linear-gradient(
90deg,
hsla(238, 100%, 71%, 1) 0%,
hsla(295, 100%, 84%, 1) 100%
);
background: -webkit-linear-gradient(
90deg,
hsla(238, 100%, 71%, 1) 0%,
hsla(295, 100%, 84%, 1) 100%
);
filter: progid: DXImageTransform.Microsoft.gradient( startColorstr="#696EFF", endColorstr="#F8ACFF", GradientType=1 );
}
.header {
display: flex;
justify-content: center;
height: 40px;
background-color: white *** oke;
margin: -8px;
padding: 5px;
}
#time {
text-align: center;
font-size: 50px;
margin: 10px;
font-weight: normal;
color: white *** oke;
}
#btn-container {
display: flex;
justify-content: space-evenly;
}
#btn-container > button {
color: black;
background-color: white *** oke;
border: none;
outline: none;
border-radius: 5px;
padding: 8px;
font-weight: bold;
width: 100px;
cursor: pointer;
}
#task-container {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
}
.task-input {
outline: none;
border: none;
border-radius: 4px;
margin: 5px;
padding: 5px 10px 5px 10px;
width: 250px;
}
.task-delete {
outline: none;
border: none;
height: 25px;
width: 25px;
border-radius: 4px;
color: indianred;
cursor: pointer;
font-weight: 700;
}
在 options.css 中
body {
background: hsla(238, 100%, 71%, 1) no-repeat;
background: linear-gradient(
90deg,
hsla(238, 100%, 71%, 1) 0%,
hsla(295, 100%, 84%, 1) 100%
) no-repeat;
background: -moz-linear-gradient(
90deg,
hsla(238, 100%, 71%, 1) 0%,
hsla(295, 100%, 84%, 1) 100%
) no-repeat;
background: -webkit-linear-gradient(
90deg,
hsla(238, 100%, 71%, 1) 0%,
hsla(295, 100%, 84%, 1) 100%
) no-repeat;
filter: progid: DXImageTransform.Microsoft.gradient( startColorstr="#696EFF", endColorstr="#F8ACFF", GradientType=1 );
}
h1 {
color: white *** oke;
text-align: center;
font-size: 50px;
margin: 10px;
font-weight: normal;
}
h2 {
font-weight: normal;
color: white *** oke;
}
#time-option {
outline: none;
border: none;
width: 300px;
border-radius: 4px;
padding: 10px;
}
#save-btn {
display: block;
margin-top: 40px;
border: none;
outline: none;
border-radius: 4px;
padding: 10px;
color: black;
font-weight: bold;
cursor: pointer;
}

Chrome 浏览器扩展新手入门系列到此结束。希望它能帮助你了解构建 Chrome 扩展的基本概念。我鼓励你尝试构建自己的项目,甚至在此项目的基础上添加更多功能。祝你学习顺利。
宝塔的数据库管理,是基于phpmyadmin管理和新建数据库。其最大的便利性就是类似,通过面板可以快速访问进行管理操作,无需单独访问phpmyadmin的主页。 此外,在宝塔面板进行数据库管理,你也对数据库名、用户名及密码等信息一目了然,及可以对数据库执行快速备份或者导入。 添加数据...
由于市场上有各种可用的数据库,用户经常会就MongoDB与MySQL进行辩论,以找出更好的选择。 使用MySQL等关系数据库的组织在根据不断变化的需求管理和存储数据时可能会面临一定的困难。同时,新公司想知道选择什么数据库,这样他们就不会在开发过程中遇到问题。 同时,构建金融应用程序的开发人员...
Laravel多年来一直是PHP应用程序开发的摇滚明星,这是有充分理由的。庞大的生态系统、活跃的社区、强大的就业市场、成功的初创公司——它拥有一切让采用新技术变得值得的东西。 如果你想学习Laravel,你不需要更进一步。通过浏览本指南,您可以找到最适合您的Laravel教程,与您的知识水平和...
美国劳工统计局估计在美有超过软件开发人员。根据2021年的数据,其中,这是世界上使用最广泛的五种编程语言之一。 自然,学习Java并成为Java开发人员对于任何对软件开发感兴趣的人来说都是明智的职业选择。 在本综合指南中,您将学习成为Java开发人员所需的一切知识。我们将分解您需要的技能、工...
薪水可能是某些人的禁忌话题,但如果您是WordPress开发人员,或者正在考虑从事WordPress开发工作,那么了解人们的收入对于最大化您自己的赚钱能力至关重要。 所以让我们谈谈这个秘密——让我们谈谈WordPress开发人员的平均工资。这样,您将确切地知道自己的价值(以及如何赚取更多)。...
PHP 8.2预计将于今年11月发布,最新的稳定版本是PHP 8.1.5。虽然现在还为时过早,但对更新的接受程度参差不齐。 但是,知道会发生什么可以帮助您。通过了解新功能和不推荐使用的功能,您可以了解更新可能如何影响开发。这些知识还可以帮助您为最终发布做好准备。 在这篇文章中,我们将回顾最新...