Carbon Trigger 浏览器扩展完整代码解析:基于 CO2 Signal API 构建电力碳排放监控扩展
2026/9/10 12:51:50 网站建设 项目流程

Carbon Trigger 浏览器扩展完整代码解析:基于 CO2 Signal API 构建电力碳排放监控扩展

【免费下载链接】Web-Dev-For-Beginners24 Lessons, 12 Weeks, Get Started as a Web Developer项目地址: https://gitcode.com/GitHub_Trending/we/Web-Dev-For-Beginners

本指南以 Web-Dev-For-Beginners 开源课程中 5-browser-extension/solution 的完整代码为核心,系统讲解如何基于 tmrow 的 CO2 Signal API 构建一款浏览器扩展:它能够在浏览器扩展栏中实时显示你所在区域的电力碳排放强度,并通过彩色圆点图标给出直观反馈,帮助你决定何时进行洗衣机烘干等高耗电活动。读完本文,你将掌握扩展的构建与安装流程(npm + webpack + Edge Load Unpacked)、API 密钥与区域代码的配置方法,以及完整源码中颜色映射算法、localStorage 持久化与后台脚本消息通信的底层实现。

项目背景:为什么需要一个"碳触发"扩展

Carbon Trigger 的核心思想来自微软绿色云倡导团队,最初是一个网站项目。该扩展调用 CO2 Signal API 追踪指定区域的电力使用情况,把"当前区域电网有多脏"这一抽象数据,转化为浏览器扩展栏中一枚直观的彩色圆点。用户可以随时(ad hoc)唤起扩展,根据实时数据判断当下是否适合执行耗电活动——例如,在区域电力负载高峰期推迟使用烘干机,就是一种典型的低碳决策。

这一"圆点系统"的视觉概念借鉴了针对加州排放设计的 Energy Lollipop 扩展,其绿色代表清洁电力、暖色代表高碳排放的配色逻辑在实现中被完整保留(见下文calculateColor分析)。

环境准备与构建流程

前置条件

  • 本机需安装 npm;
  • 将仓库中的5-browser-extension/solution目录(完整代码)复制到本地文件夹。课程还提供了 starter 初始代码,其中src/index.js仅保留编号注释占位,供学习者自行补全(查看 starter 源码骨架)。

安装依赖并构建

npm install npm run build

npm install会安装项目声明的依赖。查看 solution/package.json 可以看到技术栈:

  • webpack ^5.105webpack-cli ^5.1:打包工具,将src/index.js连同依赖(axios)打包输出到dist目录;
  • axios ^1.15:用于向 CO2 Signal API 发起 HTTP 请求的 HTTP 客户端;
  • engines 字段:要求npm >= 9.0.0node >= 18.0.0,构建前请确认版本满足要求;
  • scripts 配置build执行webpack完成一次构建,watch执行webpack --watch监听源码变化自动重新打包,适合开发调试期使用。

在 Edge 中安装扩展

  1. 点击浏览器右上角"三点"菜单,进入扩展(Extensions)面板;
  2. 开启"开发人员模式"(Developer mode);
  3. 选择"加载解压缩的扩展"(Load Unpacked)
  4. 在弹出的目录选择框中,打开刚才npm run build生成的dist目录;
  5. 扩展加载成功后,会在扩展栏显示图标。

提示:Edge 的新版本基于 Chromium 内核,因此可直接复用 Chrome 的扩展 API(chrome.*),这也是源码中使用chrome.runtimechrome.action等接口的原因。

配置 API 密钥与区域代码

要正常使用,扩展需要两项关键输入:

  • API 密钥:前往 CO2 Signal 官网,在页面输入框中填写邮箱即可通过邮件获取 API key;
  • 区域代码:对应 Electricity Map 的电网分区编码,可查询区域代码列表。例如波士顿所在的美国东北部 ISO 区域,代码为US-NEISO

将密钥与区域代码填入扩展界面后,扩展栏中的彩色圆点会随之更新,实时反映你所在区域的能耗强度。

源码级解析:完整代码如何工作

完整代码入口 src/index.js 是整个扩展的前端逻辑核心,以下按模块拆解。

1. DOM 引用与结果区声明

const form = document.querySelector('.form-data'); const region = document.querySelector('.region-name'); const apiKey = document.querySelector('.api-key'); const errors = document.querySelector('.errors'); const loading = document.querySelector('.loading'); const results = document.querySelector('.result-container'); const usage = document.querySelector('.carbon-usage'); const fossilfuel = document.querySelector('.fossil-fuel'); const myregion = document.querySelector('.my-region'); const clearBtn = document.querySelector('.clear-btn');

代码先通过querySelector一次性抓取表单字段(区域名、API 密钥)与结果展示区(碳强度、化石燃料占比、错误提示、加载状态等)的 DOM 节点,为后续读写做准备。

2. 颜色计算函数 calculateColor:数据到视觉的映射

calculateColor = async (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]; let num = (element) => element > closestNum; let scaleIndex = co2Scale.findIndex(num); let closestColor = colors[scaleIndex]; chrome.runtime.sendMessage({ action: 'updateIcon', value: { color: closestColor } }); };

其工作流程为:

  1. 定义 CO2 强度刻度数组(单位:克/千瓦时)[0, 150, 600, 750, 800]
  2. 定义对应的颜色数组:#2AA364绿色(清洁)、#F5EB4D黄色(中等)、#9E4229橙色(偏高)、#381D02深棕(很高);
  3. 通过对刻度数组排序,找出与当前 CO2 值最接近的刻度点closestNum(排序比较函数按"绝对值距离"排序,取[0]即最近者);
  4. findIndex定位第一个大于该刻度的元素下标,据此取色;
  5. 通过chrome.runtime.sendMessage向后台脚本发送updateIcon消息,携带目标颜色。

这里映射逻辑的边界语义:CO2 值落在0–150显示绿色,150–600黄色,600–750橙色,750+深棕(750800两档共用同一深棕,属于设计上对极端值的收敛处理)。

3. 调用 API:displayCarbonUsage

const displayCarbonUsage = async (apiKey, region) => { try { await axios .get('https://api.co2signal.com/v1/latest', { params: { countryCode: region }, headers: { 'auth-token': apiKey }, }) .then((response) => { const data = response?.data?.data; if (data?.carbonIntensity == null || data?.fossilFuelPercentage == null) { throw new Error('Missing carbon intensity or fossil fuel data'); } let CO2 = Math.floor(data.carbonIntensity); calculateColor(CO2); loading.style.display = 'none'; form.style.display = 'none'; myregion.textContent = region; usage.textContent = Math.round(data.carbonIntensity) + ' grams (grams C02 emitted per kilowatt hour)'; fossilfuel.textContent = data.fossilFuelPercentage.toFixed(2) + '% (percentage of fossil fuels used to generate electricity)'; results.style.display = 'block'; }); } catch (error) { console.warn('Data fetch failed:', error.message); loading.style.display = 'none'; results.style.display = 'none'; errors.textContent = 'Sorry, data unavailable for the selected region.'; } };

关键实现细节:

  • 请求构造:以countryCode作为查询参数传入区域代码,通过auth-token请求头携带 API 密钥,向https://api.co2signal.com/v1/latest拉取最新数据;
  • 数据校验:使用可选链response?.data?.data安全取值,并显式校验carbonIntensityfossilFuelPercentage是否为空,缺失即抛出错误进入 catch 分支——这是对课程反复强调的"异步数据校验"的工程化落实;
  • UI 状态机:请求成功后依次隐藏 loading 与表单、填充区域名、碳强度(克/千瓦时)与化石燃料占比(百分比,保留两位小数)、显示结果容器;失败时隐藏 loading 与结果区,并在errors节点输出 "Sorry, data unavailable for the selected region.",同时console.warn记录错误原因,便于开发者调试。

4. 持久化与初始化:setUpUser / init / reset

const setUpUser = async (apiKey, region) => { localStorage.setItem('apiKey', apiKey); localStorage.setItem('region', region); loading.style.display = 'block'; errors.textContent = ''; clearBtn.style.display = 'block'; displayCarbonUsage(apiKey, region); }; const init = async () => { const storedApiKey = localStorage.getItem('apiKey'); const storedRegion = localStorage.getItem('region'); chrome.runtime.sendMessage({ action: 'updateIcon', value: { color: 'green' } }); if (storedApiKey === null || storedRegion === null) { form.style.display = 'block'; results.style.display = 'none'; loading.style.display = 'none'; clearBtn.style.display = 'none'; errors.textContent = ''; } else { results.style.display = 'none'; form.style.display = 'none'; displayCarbonUsage(storedApiKey, storedRegion); clearBtn.style.display = 'block'; } }; const reset = async (e) => { e.preventDefault(); localStorage.removeItem('region'); init(); };
  • setUpUser:表单提交后把密钥与区域写入localStorage,显示 loading 并立即发起首次 API 调用;
  • init:应用启动时先读取本地存储;若缺少密钥或区域则展示表单;否则直接以已存参数拉取数据;无论何种分支,都会先通过sendMessage将默认图标设为绿色,保证扩展一加载就有明确的"工作正常"视觉反馈(对应第 3 课中"设置默认图标颜色"的实践);
  • reset:清除本地存储中的region并重新执行init,即"清除配置、回到表单"的入口,由clearBtn触发。

5. 事件绑定与启动

form.addEventListener('submit', (e) => handleSubmit(e)); clearBtn.addEventListener('click', (e) => reset(e)); init();

文件末尾将表单submit事件绑定到handleSubmit(内部调用setUpUser(apiKey.value, region.value)),将清除按钮click事件绑定到reset,最后调用init()启动整个应用。这一"先声明函数、再绑定监听、最后启动"的组织方式,也是 starter 骨架中//1 ~ //6编号注释对应的实现顺序。

后台脚本:图标动态更新原理

第 3 课:后台任务与性能 详细讲解了与前台sendMessage配套的后台监听逻辑,完整实现了"数据 → 颜色 → 图标"的链路:

chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) { if (msg.action === 'updateIcon') { chrome.action.setIcon({ imageData: drawIcon(msg.value) }); } }); function drawIcon(value) { const canvas = new OffscreenCanvas(200, 200); const 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); }

其技术要点:

  • 消息传递:前台脚本通过chrome.runtime.sendMessage({ action: 'updateIcon', ... })发送消息,后台脚本通过chrome.runtime.onMessage.addListener订阅,action字段充当消息路由标识;
  • 动态绘制:使用 Canvas API 的OffscreenCanvas在离屏绘制一个指定颜色的实心圆,再经getImageData截取图像数据,交给chrome.action.setIcon更新浏览器工具栏图标——离屏绘制不会阻塞 UI,是扩展性能优化的典型做法;
  • 该课程同时讲解了浏览器性能分析(Edge DevTools 的 Performance 面板、Profiler 记录、Event Log 中超过 15ms 的事件检查等),可用于对本扩展进行性能验证与调优,例如确认 API 请求、颜色计算与图标绘制各环节的开销。

完整工作流程总结

  1. 用户首次打开扩展,init()发现localStorage为空,显示配置表单;
  2. 用户输入 API 密钥与区域代码并提交,setUpUser持久化配置并触发displayCarbonUsage
  3. axios 携带countryCode参数与auth-token请求头调用 CO2 Signal API;
  4. 校验通过后计算碳强度,调用calculateColor映射出颜色,经sendMessage通知后台脚本;
  5. 后台脚本用OffscreenCanvas绘制圆点图标并调用chrome.action.setIcon更新扩展栏图标;
  6. 之后每次打开扩展,都会从localStorage读取配置并自动拉取最新数据;点击清除按钮则回到配置表单。

常见问题与调试建议

  • 构建失败:确认 node >= 18、npm >= 9(package.json engines 的硬性要求),并确认npm install成功执行;
  • 图标不变化:检查是否已重新执行npm run build并在浏览器扩展页点击"重新加载",dist目录必须包含最新打包产物;
  • 数据获取失败:确认 API 密钥有效、区域代码格式正确(如US-NEISO),错误信息会显示在扩展界面的 errors 区域,同时可在开发者工具控制台查看console.warn输出的具体原因;
  • 性能验证:参考 3-background-tasks-and-performance,用 Performance 面板录制并分析扩展加载流程,观察是否存在渲染阻塞或过长任务。

以上便是 Carbon Trigger 扩展从构建、安装、配置到源码原理的完整解读。你可以直接使用5-browser-extension/solution目录中的成品代码,也可以基于 starter 骨架 按课程编号提示自行实现一遍,以加深对表单、异步 API 调用、localStorage 持久化与扩展消息通信的整体理解。

【免费下载链接】Web-Dev-For-Beginners24 Lessons, 12 Weeks, Get Started as a Web Developer项目地址: https://gitcode.com/GitHub_Trending/we/Web-Dev-For-Beginners

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询