首页
/ Web-Dev-For-Beginners 浏览器扩展实战:构建 Carbon Trigger——用 CO2 Signal API 追踪区域电网碳强度

Web-Dev-For-Beginners 浏览器扩展实战:构建 Carbon Trigger——用 CO2 Signal API 追踪区域电网碳强度

2026-09-06 16:57:40作者:温玫谨Lighthearted

本篇指南完整还原 Web-Dev-For-Beginners 课程第 5 模块(浏览器扩展)的最终交付物:一个名为 Carbon Trigger 的浏览器扩展。读完后,你将掌握如何用 CO2 Signal API 查询指定电网区域的实时碳强度与化石燃料占比、如何用 Webpack 构建 Manifest V3 扩展并在 Edge 中以“加载解包扩展”方式安装,以及扩展如何通过 chrome.runtime.sendMessage 在弹出窗口与 Service Worker 之间通信、动态绘制着色小圆点图标来直观反映区域用电的“碳负担”。

Carbon Trigger 扩展在浏览器工具栏中的运行截图,弹出窗口显示区域碳强度与化石燃料占比

1. 扩展定位:把区域电网碳强度放进浏览器工具栏

solution/README.md 对扩展的定位非常明确:借助 tmrow 的 CO2 Signal API 追踪某个电网区域的电力碳排放情况,把提醒直接做到浏览器里——当你浏览网页时,一眼就能看到所在区域的电网有多“重”,从而做出更合理的用电决策(比如电网碳强度高的时段,推迟运行干衣机这类高耗能电器)。这个思路源自课程作者 Jen Looper 的绿色网站项目,其中“彩色圆点”图标系统的灵感则来自面向加州排放数据的 Energy Lollipop 扩展。

模块总览 可以看到,该扩展兼容 Edge、Chrome 与 Firefox,课程将它的开发拆成了三节递进的课:

  1. 浏览器基础——扩展的运行模型;
  2. 表单与 Local Storage——用户输入 API key 与区域码后如何持久化;
  3. 后台任务与性能——Service Worker 与性能衡量。

solution/ 目录就是这一系列课程的完整参考答案,下面按“先跑起来,再看实现”的顺序展开。

2. 环境要求与构建流程

前置条件

solution/README.md 的要求,你需要:

  • 本机已安装 npm
  • solution/ 代码下载一份到本地文件夹中。

环境版本有明确下限:从 package.jsonengines 字段可以确认,项目要求 node >= 18.0.0npm >= 9.0.0

安装依赖与构建

在代码目录中依次执行两条命令(这两条也是原文档中仅有的两个命令,务必完整保留):

npm install
npm run build

对照 package.json 可以看到这两个命令的实际含义:

  • 依赖非常克制:运行时依赖只有一个 axios ^1.15.0(用于请求 API);开发依赖是 webpack ^5.105.4webpack-cli ^5.1.4
  • scripts 中定义了 build: webpackwatch: webpack --watch。也就是说,npm run build 等价于直接运行 webpack 打包,产物输出到 dist/ 目录;开发时也可以用 npm run watch 开启增量重建。

构建完成后,dist/ 里就是可加载的扩展本体。当前仓库中 dist 目录 已经包含了构建产物,包含 manifest.jsonbackground.jsmain.jsindex.htmlstyles.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):包含 loadingerrorsresult-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

数据获取集中在 displayCarbonUsagesrc/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.carbonIntensitydata.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 色阶映射:把克数变成颜色

calculateColorsrc/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 持久化与初始化流程

  • setUpUsersrc/index.js#L72-L80):表单提交后把 apiKeyregion 写入 localStorage,然后立即发起首次查询;
  • initsrc/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 的步骤安装:

  1. 点击浏览器右上角的“三个点”菜单,进入 扩展(Extensions) 面板;
  2. 选择 “Load Unpacked”(加载解包)
  3. 在目录选择提示中打开 dist 文件夹,扩展即完成加载。

在 Edge 的扩展管理界面中选择“加载解包扩展”以安装本地构建的扩展

配置 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 buildsrc/index.js 等源码打成 dist/ 下的 main.jsmanifest.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 对照学习。

登录后查看全文
热门项目推荐
相关项目推荐