Web-Dev-For-Beginners 浏览器扩展课:背景任务、动态图标与网页性能剖析实战
本篇是 Web-Dev-For-Beginners 课程「浏览器扩展」模块的第三讲(也是该系列最后一讲)技术指南,聚焦两个主题:如何为碳足迹追踪扩展 Carbon Trigger 补上最后一块拼图——基于 chrome.runtime 消息传递的背景任务与动态图标更新;以及如何用浏览器开发者工具的 Performance 分页剖析网页性能、定位资产过大、DOM 查找低效与渲染阻塞脚本这三类常见瓶颈。读完本文,你将掌握完整的 calculateColor 颜色映射实现、onMessage 监听与 Canvas 绘制图标的背景脚本写法,并具备独立执行 npm run build 构建、在 Edge 中以未打包方式加载扩展、以及使用性能分析器排查瓶颈的实操能力。
课程定位:浏览器扩展系列的最后一块拼图
在 浏览器扩展模块 的前两讲中,你已经完成了:
- 理解浏览器如何工作、扩展的部署方式;
- 建立表单、调用 CO2 Signal API 抓取区域电力碳强度数据、用
localStorage持久化 API key 与区域代码,并异步显示结果。
本讲要解决的问题是:扩展的图标颜色会随实时碳强度数据变化(绿色代表清洁电力、深色代表高碳排),同时系统性地学习浏览器如何处理这类「幕后」工作,以及如何测量你所建网页的真实性能表现。这个「圆点」图标的设计灵感来自 Energy Lollipop 扩展(详见 模块致谢)。
网页处理效能的基礎
"网页处理效能攸关两件事:网页多快地载入,与程式多快地执行。"
让网页在所有装置、所有使用者、所有情境下都快速运作,是一个庞大的主题。以下是开发网页或扩展时应铭记的要领,其中第一件事是先收集效能数据——浏览器内建的开发者工具就能做到。
打开 Edge 的 Performance 分页
- 在 Edge 中点击「设定及更多」按钮(右上角三个点),选择「更多工具 > 开发人员工具」;
- 或使用键盘快捷键:Windows 为
Ctrl+Shift+I,Mac 为Option+Command+I; - 打开后切换到 Performance 分页,这里就是效能分析工具所在。
录制并解读时间线
打开一个网站(例如 microsoft.com),点击 Record 按钮并重新整理网页;停止录制后,你将获得该网页 script、render、paint 三类事件的过程与资讯:
提示:要取得真实的网页开启时间,记得先清除浏览器快取——首访与回访的表现通常差异很大。
接着在时间线上框选页面载入期间的事件,在**总览面板(Overview)**中查看该区间的效能摘要,为自己的网页效能截取一张「快照」:
再检查 Event Log 面板,确认是否有网页事件耗时超过 15 毫秒——超过该阈值的事件值得重点优化:
动手练习:对这个课程网站本身打开开发者工具,检查是否存在 bottleneck。找出载入最久与最快的物件各是什么?
效能分析时要盯住的三类「问题点」
每一位网页开发者在发布作品前都应注意以下常见瓶颈,避免上线后的意外:
1. 资产(Asset)大小 过去几年网页「变重」了,也因此变慢,其中相当一部分负担来自图片。好的习惯是确保图片经过最佳化,以合理的档案大小与解析度呈现给使用者。可以查阅 Internet Archive 的 page-weight 报告,了解网页负担随时间的增长趋势。
2. DOM 查找元素(Traversal) 浏览器必须依照你的代码建立 Document Object Model(DOM)。为了页面性能,应让标签数量保持最小化,只使用与造型页面真正需要的功能。此外,过量的 CSS 也可以最佳化——例如只在单一页面使用的造型样板,不应放进全域样式表。
3. JavaScript 渲染阻塞(Render-blocking)
每一位 JavaScript 开发者都要警惕 render-blocking 脚本:它们必须在 DOM 查找与浏览器呈现之前先载入执行。可以考虑在脚本标签上使用 defer 属性——本课程仓库的盆栽盒(Terrarium)专案就实践了这一点。
也可以借助 WebPageTest 等网页测速网站测试一些站点,学习业界确认网页效能的常规检查项。
背景任务实战:建立函式计算颜色
前文讲清了浏览器如何呈现你送出的资产,下面补齐扩展的最后几项功能。核心思路是:拿到 API 回传的二氧化碳浓度数值后,映射到一个颜色,再透过 chrome.runtime 传给背景脚本更新工具栏图标。
calculateColor 函式
编辑扩展原始档 src/index.js,在一系列用于 DOM 存取的 const 变数之后新增 calculateColor():
function calculateColor(value) {
let co2Scale = [0, 150, 600, 750, 800];
let colors = ['#2AA364', '#F5EB4D', '#9E4229', '#381D02', '#381D02'];
let closestNum = co2Scale.sort((a, b) => {
return Math.abs(a - value) - Math.abs(b - value);
})[0];
console.log(value + ' is closest to ' + closestNum);
let num = (element) => element > closestNum;
let scaleIndex = co2Scale.findIndex(num);
let closestColor = colors[scaleIndex];
console.log(scaleIndex, closestColor);
chrome.runtime.sendMessage({ action: 'updateIcon', value: { color: closestColor } });
}
逐段拆解这段算法:
| 步骤 | 代码 | 作用 |
|---|---|---|
| 1 | co2Scale = [0, 150, 600, 750, 800] |
定义碳强度刻度(克 CO2/kWh) |
| 2 | colors = ['#2AA364', '#F5EB4D', ...] |
对应的颜色矩阵:绿→黄→橙棕→深棕 |
| 3 | co2Scale.sort(...) 后取 [0] |
按「与输入值的绝对距离」排序,取得最接近的刻度点 closestNum |
| 4 | findIndex(element > closestNum) |
找到排序数组中第一个大于最接近值的刻度索引,作为颜色矩阵下标 |
| 5 | chrome.runtime.sendMessage(...) |
把选定的颜色以 updateIcon 动作传往背景脚本 |
颜色刻度的对应关系可以这样理解:约 0–150 区间偏绿(清洁),150–600 偏黄(中等),600–750 偏橙(偏高),750 以上为深棕色(很高)——这与 英文版课程 中的颜色分段说明一致。
对照仓库中的完整解答 solution/src/index.js,可以看到 calculateColor 最终被实现为 async 箭头函式,并在 displayCarbonUsage 中于 axios 呼叫 CO2 Signal API(https://api.co2signal.com/v1/latest,携带 countryCode 参数与 auth-token 标头)成功后被调用(第 50 行:calculateColor(CO2))。解答中还做了数据校验(carbonIntensity / fossilFuelPercentage 缺失时抛错)与错误分支处理,这些都是课程代码片段之外的稳健化补充。
认识 chrome.runtime API
chrome.runtime 是处理扩展所有背景工作的 API,本扩展借此在内容脚本与背景脚本之间传递讯息。官方文档对它的描述是:
"在应用程序中,使用 chrome.runtime API 来接收背景页面、回传关于 manifest 的资讯、监听并回应事件。你也可以利用此 API 转换 URL 的相对路径成绝对路径。"
它的价值在于:让扩展的不同脚本部分彼此通信、在不冻结 UI 的前提下处理背景工作、管理扩展生命周期事件。
两个实用提醒(课程原文强调):
- 如果你为 Edge 开发此扩展,会惊讶于自己用的竟是
chromeAPI——新版 Edge 运行在 Chromium 引擎上,所以这些工具在 Edge 中同样可用; - 如果要剖析浏览器扩展本身,请在扩展上执行开发者工具(扩展是独立于浏览器主视窗的个别个体),而不是在主视窗里开。
设定图示默认颜色
在 init() 函式中,借由呼叫 chrome.runtime.sendMessage 将图标颜色先设为通用绿,让使用者在资料载入前就确认扩展正常工作。对照解答代码(init 函式),这段绿色设定位于读取 localStorage 存取 API key 与区域代码之前:
chrome.runtime.sendMessage({
action: 'updateIcon',
value: {
color: 'green',
},
});
这个初始化达成了四件事:以中性绿作为默认状态、在扩展载入时立即提供视觉回馈、与背景脚本建立通信模式、确保使用者在资料载入前看到的是一个功能正常的扩展。
呼叫函式、执行呼叫
在 CO2 Signal API 回传的 promise 物件下方(即取得 CO2 数值之后)呼叫函式:
//let CO2...
calculateColor(CO2);
这一步把 API 数据流与视觉指标系统连接起来:每当新的碳强度资料到达,图标会自动更新,同时保持了「资料抓取」与「显示逻辑」的关注点分离。
背景脚本:onMessage 监听者与 Canvas 绘制
最后,在档案 /dist/background.js 中,为这些背景行为新增事件监听者:
chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
if (msg.action === 'updateIcon') {
chrome.browserAction.setIcon({ imageData: drawIcon(msg.value) });
}
});
//参考 energy lollipop extension,很好的程式!
function drawIcon(value) {
let canvas = document.createElement('canvas');
let context = canvas.getContext('2d');
context.beginPath();
context.fillStyle = value.color;
context.arc(100, 100, 50, 0, 2 * Math.PI);
context.fill();
return context.getImageData(50, 50, 100, 100);
}
这段背景脚本做了什麼:为任何来自内容脚本、发往背景工作管理者的讯息建立事件监听者;当收到 action === 'updateIcon' 的讯息时,执行后续程式,利用 Canvas API 在离屏画布上画出一个半径 50、圆心 (100,100) 的彩色圆,再用 getImageData(50, 50, 100, 100) 截取 100×100 的影像资料,交给 chrome.browserAction.setIcon 更新工具栏图标。
值得注意的是仓库中课程文档的演化:英文版课程给出的新写法改用了 chrome.action.setIcon 与 new OffscreenCanvas(200, 200)(OffscreenCanvas 可避免阻塞 UI,性能更平滑),而本文沿用的是繁体中文版原文的 chrome.browserAction + document.createElement('canvas') 写法,两者概念一致,学习时以你跟随的教材版本为准。关于 Canvas API 的更多细节,会在往后的太空游戏课程中学习。
整个消息传递链路可以概括为:内容脚本 chrome.runtime.sendMessage({action:'updateIcon', value:{color}}) → 背景脚本 onMessage 监听者 → drawIcon() 用 Canvas 产出 ImageData → setIcon 更新工具栏图标。
构建、加载与验证
仓库的解答工程 solution/package.json 给出了构建环境前提与脚本:
{
"name": "carbon-trigger-extension",
"engines": {
"npm": ">=9.0.0",
"node": ">=18.0.0"
},
"scripts": {
"watch": "webpack --watch",
"build": "webpack"
},
"dependencies": {
"axios": "^1.15.0"
},
"devDependencies": {
"webpack": "^5.105.4",
"webpack-cli": "^5.1.4"
}
}
按 solution 的 README 说明,完整流程为:
npm install安装依赖(需要 Node.js ≥ 18、npm ≥ 9);npm run build由 webpack 构建扩展,产物输出到dist目录;- 在 Edge 右上角「三点」菜单进入扩展面板,选择「载入拆解项目(Load Unpacked)」,指向
dist资料夹即可加载; - 输入 CO2 Signal API key 与区域代码(例如波士顿为
US-NEISO),重新建置并刷新扩展后,观察工具栏圆点随你所在区域的能源碳强度改变颜色。
现在,你一眼就能判断:是现在去跑腿、洗碗、烘干衣服的好时机,还是该等电网更清洁一些——扩展的价值正在于这种「随取随用」的决策提示。
挑战与作业
挑战:调查一些历史悠久的开源网站,根据其 GitHub 提交历史,你能分辨出它们过去几年以来在效能上做了哪些调整?它们的共同痛点是什麼?(提示:在 commit message 中搜索 "optimize"、"performance"、"faster" 等关键词,观察是否存在反复出现的同类问题。)
作业:分析网页效能——提供一份详细报告,指出一个网页效能上的问题点,分析该网页缓慢的原因并给出改善方案。要求不只依赖浏览器工具,还应调研第三方工具(如 Lighthouse、WebPageTest、GTmetrix 等)辅助分析,学习评量详见 英文版作业说明。
小结
本讲完成了 Carbon Trigger 扩展的最后一环,并交付了两组可迁移的技能:
- 扩展架构层面:
chrome.runtime.sendMessage/onMessage的内容脚本—背景脚本通信模式、init()中的默认状态设定、以及用 Canvas API 动态生成工具栏图标的完整实现,这套「消息驱动」的写法可直接套用到任何需要后台状态可视化的扩展; - 性能工程层面:用 DevTools Performance 分页录制时间线、以 15ms 为阈值检查 Event Log、从资产大小 / DOM 查找 / 渲染阻塞脚本三个维度建立瓶颈清单,再辅以 WebPageTest 等外部工具交叉验证。
至此你已建立了一款实用的浏览器扩展,并学会了浏览器内部处理这类背景工作的方式与监测它效能分析的方法。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00


