Web-Dev-For-Beginners 浏览器扩展实战:构建 Carbon Trigger——用 CO2 Signal API 追踪区域电网碳强度
本篇指南完整还原 Web-Dev-For-Beginners 课程第 5 模块(浏览器扩展)的最终交付物:一个名为 Carbon Trigger 的浏览器扩展。读完后,你将掌握如何用 CO2 Signal API 查询指定电网区域的实时碳强度与化石燃料占比、如何用 Webpack 构建 Manifest V3 扩展并在 Edge 中以“加载解包扩展”方式安装,以及扩展如何通过 chrome.runtime.sendMessage 在弹出窗口与 Service Worker 之间通信、动态绘制着色小圆点图标来直观反映区域用电的“碳负担”。
1. 扩展定位:把区域电网碳强度放进浏览器工具栏
solution/README.md 对扩展的定位非常明确:借助 tmrow 的 CO2 Signal API 追踪某个电网区域的电力碳排放情况,把提醒直接做到浏览器里——当你浏览网页时,一眼就能看到所在区域的电网有多“重”,从而做出更合理的用电决策(比如电网碳强度高的时段,推迟运行干衣机这类高耗能电器)。这个思路源自课程作者 Jen Looper 的绿色网站项目,其中“彩色圆点”图标系统的灵感则来自面向加州排放数据的 Energy Lollipop 扩展。
从 模块总览 可以看到,该扩展兼容 Edge、Chrome 与 Firefox,课程将它的开发拆成了三节递进的课:
- 浏览器基础——扩展的运行模型;
- 表单与 Local Storage——用户输入 API key 与区域码后如何持久化;
- 后台任务与性能——Service Worker 与性能衡量。
solution/ 目录就是这一系列课程的完整参考答案,下面按“先跑起来,再看实现”的顺序展开。
2. 环境要求与构建流程
前置条件
按 solution/README.md 的要求,你需要:
- 本机已安装 npm;
- 把
solution/代码下载一份到本地文件夹中。
环境版本有明确下限:从 package.json 的 engines 字段可以确认,项目要求 node >= 18.0.0、npm >= 9.0.0。
安装依赖与构建
在代码目录中依次执行两条命令(这两条也是原文档中仅有的两个命令,务必完整保留):
npm install
npm run build
对照 package.json 可以看到这两个命令的实际含义:
- 依赖非常克制:运行时依赖只有一个
axios ^1.15.0(用于请求 API);开发依赖是webpack ^5.105.4和webpack-cli ^5.1.4; scripts中定义了build: webpack和watch: webpack --watch。也就是说,npm run build等价于直接运行 webpack 打包,产物输出到dist/目录;开发时也可以用npm run watch开启增量重建。
构建完成后,dist/ 里就是可加载的扩展本体。当前仓库中 dist 目录 已经包含了构建产物,包含 manifest.json、background.js、main.js、index.html、styles.css 等文件,可以直接打开研究其结构(下一节)。
3. 扩展结构解析:Manifest V3 与动态圆点图标
manifest.json:最小可用的 MV3 配置
manifest.json 展示了该扩展的完整声明,全文只有寥寥几行,却是理解整个扩展架构的钥匙:
{
"manifest_version": 3,
"name": "My Carbon Trigger",
"version": "0.1.0",
"host_permissions": ["<all_urls>"],
"background": {
"service_worker": "background.js"
},
"action": {
"default_popup": "index.html"
}
}
各字段的作用:
manifest_version: 3:使用 Manifest V3,后台运行逻辑由 Service Worker 承担(这是 MV3 与旧版事件页最大的区别);background.service_worker:指向background.js,负责监听消息并操作浏览器 API(如修改图标);action.default_popup:点击工具栏图标时弹出index.html,即扩展的“迷你网页”界面;host_permissions: ["<all_urls>"]:从源码结构看,这里申请了全域权限,因为扩展的index.js会通过axios请求api.co2signal.com这类跨域 API。
background.js:OffscreenCanvas 动态绘制圆点
background.js 是后台 Service Worker,全文不足 20 行:
chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
if (msg.action === 'updateIcon') {
chrome.action.setIcon({ imageData: drawIcon(msg.value) });
}
});
function drawIcon(value) {
let canvas = new OffscreenCanvas(200, 200);
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);
}
这里的机制值得注意:扩展没有为每种碳强度等级准备一张静态图标图片,而是用 OffscreenCanvas 在运行时画一个实心圆,再截取中央 100x100 的 ImageData 交给 chrome.action.setIcon。这意味着“圆点系统”只需要传递一个颜色值,图标就能覆盖任意颜色——这正是 Energy Lollipop 式“颜色即信息”设计的实现基础。
弹出窗口界面
dist/index.html 是点击图标后弹出的界面,结构上分为两块:
- 表单区(
form.form-data):包含input.region-name(区域码)与input.api-key(API key)两个必填输入框和提交按钮; - 结果区(
div.result):包含loading、errors、result-container(展示 Region / Carbon Usage / Fossil Fuel Percentage 三项)以及button.clear-btn(“Change region”切换区域按钮)。
这些 class 名与 index.js 里的 querySelector 一一对应(见下一节),表单与结果面板的显隐完全由 JavaScript 控制。
4. 核心逻辑:API 调用、CO2 色阶与本地持久化
弹出窗口的全部逻辑由 src/index.js 实现(webpack 将其打包为 dist/main.js),可以分为四条主线。
4.1 表单字段与结果容器
脚本开头用 querySelector 绑定了一组 DOM 引用:表单 .form-data、输入框 .region-name 与 .api-key,结果侧的 .errors、.loading、.result-container、.carbon-usage、.fossil-fuel、.my-region 和 .clear-btn。这与 dist/index.html 中的标记完全吻合,属于“纯 class 选择器、不依赖 id”的稳健写法。
4.2 调用 CO2 Signal API
数据获取集中在 displayCarbonUsage(src/index.js#L34-L68):
await axios
.get('https://api.co2signal.com/v1/latest', {
params: { countryCode: region },
headers: { 'auth-token': apiKey },
})
要点:
- 端点为
https://api.co2signal.com/v1/latest,即以“区域码”作为countryCode查询参数拉取该区域的最新一次读数(而非历史序列); - 认证通过自定义请求头
auth-token携带 API key 完成; - 拿到响应后先做防御性校验:
data.carbonIntensity与data.fossilFuelPercentage任一为null就抛出异常,走错误分支; - 成功时展示两个指标:碳强度(取整后以 “grams (grams CO2 emitted per kilowatt hour)” 呈现)和化石燃料占比(
toFixed(2)保留两位小数,说明其为 0–100 的百分比数值); - 失败时(网络错误、key 无效、区域无数据等)不抛给用户堆栈,而是在错误区显示固定文案 “Sorry, data unavailable for the selected region.”,并隐藏 loading 与结果面板。
4.3 CO2 色阶映射:把克数变成颜色
calculateColor(src/index.js#L17-L32)把抽象的碳强度数值翻译为工具栏圆点的颜色:
let co2Scale = [0, 150, 600, 750, 800];
let colors = ['#2AA364', '#F5EB4D', '#9E4229', '#381D02', '#381D02'];
co2Scale是五个锚点阈值(克 CO2 / 千瓦时),colors与之逐位对应:深绿#2AA364→ 黄#F5EB4D→ 赭红#9E4229→ 深棕#381D02;- 算法先对锚点按“与当前值的距离”排序取最近者,再找到该值在色阶数组中的位置,从而得到对应颜色;
- 最后通过
chrome.runtime.sendMessage({ action: 'updateIcon', value: { color: closestColor } })通知后台 Service Worker 换色——这正是 3.2 节onMessage监听器处理的消息,两条代码在此闭环。
4.4 Local Storage 持久化与初始化流程
setUpUser(src/index.js#L72-L80):表单提交后把apiKey与region写入localStorage,然后立即发起首次查询;init(src/index.js#L89-L116):启动时先读取 localStorage 的这两个键。若缺失,就展示表单、隐藏结果区;若已存在,则直接隐藏表单并用存储的凭据拉取数据——这就是“只需配置一次”的体验来源;- 初始化时还会发送一条
updateIcon消息,把圆点先置为通用的green,避免图标处于未定义状态; reset(“Change region”按钮)只删除region键再调用init(),重新进入表单流程。
这一整套“表单 + Local Storage + 初始化读取”的模式,正是课程第 2 节 Forms and local storage 所教授内容的综合落地。
5. 安装与使用:Edge 加载解包扩展、API Key 与区域码
在 Edge 中加载
构建完成后(npm run build 生成 dist/),在 Edge 中按 solution/README.md 的步骤安装:
- 点击浏览器右上角的“三个点”菜单,进入 扩展(Extensions) 面板;
- 选择 “Load Unpacked”(加载解包);
- 在目录选择提示中打开
dist文件夹,扩展即完成加载。
配置 API Key 与区域码
扩展首次使用时需要两项配置(填入弹出窗口的表单即可):
- API key:CO2 Signal API 的访问密钥,通过其官网(co2signal.com)以邮件注册方式免费申请;
- 区域码:对应 Electricity Map 电网分区体系的区域代码,例如波士顿作者使用的就是
US-NEISO。README 建议到 Electricity Map 的 zones 接口(api.electricitymap.org/v3/zones)查询你所在区域的代码。
配置完成后,工具栏上的圆点会随所在区域的电网碳强度实时变化:绿点意味着当前时段以低碳电源为主,适合安排大模型训练、视频转码、跑干衣机等“碳重”活动;黄、赭、深棕则依次提示电网化石燃料占比升高,适合把高耗能任务延后。README 原文将这一交互总结为:圆点“给出一个指针,告诉你此时哪些高能耗活动适合执行”。
6. 小结:从课程骨架到可运行扩展
回顾整条链路:npm install 装入 webpack 与 axios → npm run build 把 src/index.js 等源码打成 dist/ 下的 main.js → manifest.json 声明 MV3 的 Service Worker 与弹出窗口 → Edge 加载解包 → 用户填入 API key 与区域码,localStorage 记住配置 → 每次打开弹出窗口即拉取 /v1/latest 最新读数,并按 0/150/600/750/800 五档色阶更新工具栏圆点。
这个方案麻雀虽小五脏俱全:跨域 API 调用、表单处理、本地持久化、消息通信、运行时图标生成一个都不少,是学习浏览器扩展开发的理想范本。若想进一步深挖,可以对照 模块总 README 中的三节课逐一阅读,尤其是 Background tasks and performance 一节中关于 Service Worker 生命周期与性能分析工具的内容;start 目录 则保留了留空注释的脚手架版本,适合先自己动手填写、再回来看本 solution 对照学习。
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 StartedRust0624
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

