使用 CO2 Signal API 构建 Carbon Trigger 浏览器扩展:完整实现与源码剖析
【免费下载链接】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 追踪区域电力使用情况,构建一个可以在 Edge 等浏览器中实时提醒你所在地区电力负荷的浏览器扩展。读完本文,你将掌握从npm install、npm run build打包、以 "Load unpacked" 方式安装扩展,到配置 API Key 与区域代码、理解彩色圆点指示器背后calculateColor映射算法与background.js图标绘制的完整实战链路,并能在 starter 脚手架 基础上独立完成功能填空。
项目背景:扩展在解决什么问题
Carbon Trigger 是一个"像迷你网站一样、针对某一特定任务而定制"的浏览器扩展:它检查指定区域的电力使用情况与碳强度(carbon intensity),返回该区域碳足迹的读数。用户只需在表单中输入 API Key 与区域代码,即可按需调用它,从而用数据指导自己的用电决策。例如,当所在区域电力负荷很高时,可以推迟运行烘干机这类高耗电活动。
这一扩展可以运行在 Edge、Chrome 与 Firefox 上。在仓库中,它属于 5-browser-extension 模块的实践成果,课程分三讲:浏览器基础知识、表单与本地存储、后台任务与性能。完成版代码位于 solution,配套的 starter 代码 则把核心逻辑留白,供学习者按步骤补齐。
环境准备与快速上手
你需要先安装 npm(Node.js 自带)。下载本项目代码到本地文件夹后,依次执行:
npm install安装package.json中声明的全部依赖,包括 webpack 构建工具链与 axios HTTP 客户端(见 package.json)。
接着用 Webpack 构建扩展:
npm run build构建产物会输出到dist/目录。仓库的 dist 目录中已包含构建结果:main.js(打包后的弹出页脚本)、background.js(后台 Service Worker)、manifest.json(扩展清单)以及index.html、styles.css。
在 Edge 中安装未打包的扩展
- 点击浏览器右上角的"三点"菜单,进入"扩展"面板;
- 开启"开发人员模式",选择Load unpacked(加载解压缩的扩展);
- 在弹窗中选择
dist文件夹,扩展随即加载完成。
获取运行所需的两个关键输入
使用该扩展需要两样东西:
- CO2 Signal 的 API Key:前往 CO2 Signal 官网,在页面上的输入框中填入你的邮箱即可通过邮件获取;
- 你所在区域的代码(region code):参考 Electricity Map 的 zones 接口(如波士顿对应
US-NEISO),在扩展界面中填入。
将 API Key 与区域代码输入扩展界面后,浏览器扩展栏中的彩色圆点会随之变化,实时反映所在区域的能源使用情况,给你一个"现在做哪些高耗能活动是合适的"直观指示。这一"圆点"系统的设计思路来自 Energy Lollipop 扩展(用于展示加州碳排放)。
源码结构总览:从表单到圆点
完成版源码 solution/src/index.js 是弹出页(popup)的核心逻辑,其运行流程可以拆成六个步骤(与 start/src/index.js 中留白的注释编号一一对应):
- 获取表单字段与结果区域:通过
document.querySelector拿到.form-data、.region-name、.api-key、.errors、.loading、.result-container、.carbon-usage、.fossil-fuel、.my-region、.clear-btn等 DOM 节点; - 绑定监听器并启动应用:
form.addEventListener('submit', ...)与clearBtn.addEventListener('click', ...),最后调用init(); - 初始检查(init):读取
localStorage中的apiKey与region,决定展示表单还是直接显示结果; - 处理表单提交(handleSubmit):阻止默认行为,调用
setUpUser; - 设置用户(setUpUser):把 API Key 与区域写入
localStorage,显示 loading,并触发首次 API 调用; - 调用 API(displayCarbonUsage):请求 CO2 Signal 的
/v1/latest接口,渲染结果并更新图标颜色。
后台脚本 solution/dist/background.js 作为 Service Worker 监听消息,收到updateIcon动作后调用chrome.action.setIcon绘制图标。清单文件 manifest.json 采用 Manifest V3 规范:
{ "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" } }要点:host_permissions声明了对所有 URL 的访问权限(扩展需要请求外部 API);background.service_worker指向后台脚本;action.default_popup指定点击工具栏图标时弹出的页面。
核心实现:displayCarbonUsage 与 CO2 Signal API 调用
displayCarbonUsage(apiKey, region)是获取数据的核心函数,使用 axios 发起 GET 请求:
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'; });值得注意的实现细节:
- 请求参数:区域代码通过
params.countryCode传递,API Key 通过headers['auth-token']传递; - 数据校验:源码在取用
data.carbonIntensity与data.fossilFuelPercentage前先判空,缺失时抛错,避免渲染undefined; - 展示指标:碳强度以"每千瓦时排放的 CO2 克数"为单位取整显示;化石燃料占比用
toFixed(2)保留两位小数显示为百分比; - 错误处理:
catch块会打印console.warn,隐藏 loading 与结果区域,并在.errors中提示 "Sorry, data unavailable for the selected region."。
彩色圆点算法:calculateColor 与五档颜色映射
拿到碳强度数值后,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 } }); };算法逻辑如下:
- 定义碳强度刻度
co2Scale = [0, 150, 600, 750, 800]与对应颜色数组colors = ['绿', '黄', '红棕', '深棕', '深棕']; - 将刻度数组按"与当前值的绝对差"排序,取最接近的基准点
closestNum; - 用
findIndex找到第一个大于该基准点的刻度下标scaleIndex; - 取对应颜色,通过
chrome.runtime.sendMessage发送updateIcon消息通知后台更新图标。
需要注意:co2Scale.sort(...)是原地排序,会修改数组顺序,因此该方法在多次调用时依赖排序后的状态;从实现看它并非纯函数。绿色(约 0–150 区间)代表低碳、适合进行高耗能活动,黄色(约 150–600)代表中等,红色系(600 以上)则提示碳强度偏高,应谨慎安排耗电行为。
后台绘制图标:OffscreenCanvas 画圆
后台脚本 background.js 监听消息并绘制图标:
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在后台线程创建 200×200 的画布,以 (100, 100) 为圆心、半径 50 绘制实心圆,颜色来自前台传入的value.color,最后截取 100×100 的ImageData交给chrome.action.setIcon。这一"动态生成彩色圆点图标"的做法源自 Energy Lollipop 扩展,是本文档明示的设计来源。
表单交互与本地存储:setUpUser / init / reset
三个函数共同构成完整的"输入—持久化—回显"闭环:
- setUpUser(apiKey, region):将
apiKey与region写入localStorage,显示 loading、清空错误提示、显示 clear 按钮,然后立即调用displayCarbonUsage发起首次请求; - init():启动时读取
localStorage中的apiKey与region。若任一缺失,显示表单、隐藏结果与 clear 按钮;若两者都存在,则隐藏表单、直接调用displayCarbonUsage渲染历史配置的结果。无论哪种情况,都先发送一次绿色的updateIcon消息,把图标初始化为通用绿色; - reset(e):仅清除
localStorage中的region(保留 API Key),然后重新执行init(),让用户重新选择区域。
const reset = async (e) => { e.preventDefault(); localStorage.removeItem('region'); init(); };事件绑定在文件末尾统一完成:form.addEventListener('submit', (e) => handleSubmit(e))、clearBtn.addEventListener('click', (e) => reset(e)),最后调用init()启动应用。
运行环境与前置条件
根据 package.json:
- 引擎要求:
npm >= 9.0.0、node >= 18.0.0; - 脚本:
npm run build执行webpack,npm run watch执行webpack --watch用于开发时持续监听重编译; - 依赖:
axios(^1.15.0,用于 API 请求);开发依赖:webpack(^5.105.4)与webpack-cli(^5.1.4); - 关键词标注为 chrome extension / edge extension / carbon usage tracker。
从 starter 到 solution:如何自行完成扩展
如果你希望亲手实现一遍,可以从 starter 代码 开始,其注释编号提示了六个实现步骤(表单字段获取、设置监听器、初始检查、表单提交处理、设置用户、调用 API)。对照本文上述各节,即可逐步补齐:先取 DOM 节点,再实现displayCarbonUsage调 API、setUpUser写 localStorage、handleSubmit绑定提交、init做初始判断,最后form.addEventListener('submit', ...)与init()启动。完成后再执行npm install && npm run build并按上文方式在 Edge 中加载dist文件夹验证效果:输入 API Key 与区域代码后,扩展栏圆点应变为对应颜色,弹出页显示碳强度与化石燃料占比。
使用建议与注意事项
- API Key 与区域代码缺一不可:两者都存入 localStorage 后,扩展才会直接回显结果;只填其一则始终显示表单;
- 错误提示:当所选区域无数据或请求失败时,界面会展示 "Sorry, data unavailable for the selected region.",此时应检查 API Key 是否有效、区域代码是否符合 Electricity Map 的 zones 编码规范;
- 图标机制:圆点颜色的五档映射基于碳强度数值,绿色适合高耗能活动、黄色与红色系则建议推迟或谨慎进行;该机制的灵感来自 Energy Lollipop 扩展;
- 跨浏览器:本扩展基于 Manifest V3,
action与chrome.actionAPI 在 Edge、Chrome、Firefox 均可用,安装流程大同小异,均可通过 "Load unpacked" 加载dist目录。
【免费下载链接】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),仅供参考