首页
/ Web-Dev-For-Beginners 浏览器扩展第 5 课:用 webpack + CO2 Signal API 构建 Carbon Trigger 浏览器扩展(starter 代码实战指南)

Web-Dev-For-Beginners 浏览器扩展第 5 课:用 webpack + CO2 Signal API 构建 Carbon Trigger 浏览器扩展(starter 代码实战指南)

2026-09-06 17:43:19作者:霍妲思

本文基于仓库中 Carbon Trigger 浏览器扩展的 starter 代码说明展开,讲解如何从零构建一个调用 CO2 Signal API、实时反映本地区电网碳排放强度的 Edge/Chrome 扩展:包括 npm 依赖安装、webpack 构建打包、以“Load Unpacked”方式加载未打包扩展,以及如何配置 API 密钥与区域代码。读完后你能够独立完成扩展的构建、加载与配置全流程,并从仓库源码理解其表单提交、本地存储与动态图标变色(“碳点”)的实现原理。

Carbon Trigger 扩展运行效果:彩色圆点与碳排放数据

一、项目定位:为什么要做这个浏览器扩展

Carbon Trigger 是 Web-Dev-For-Beginners 课程第 5 课(Building a browser extension)的核心实战项目。根据 starter 代码文档 的说明:

  • 扩展使用 tmrow 的 CO2 Signal API 追踪电力使用情况,把“你所在地区的电网碳排放有多重”作为一个常驻提醒直接显示在浏览器里;
  • 用户以“随时查看”(ad hoc)的方式使用它,据此判断当前时段是否适合执行高耗能活动(例如课程 README 中举例:在地区用电碳排放高峰时段推迟使用烘干机这类高碳排电器);
  • 扩展在 Edge、Chrome、Firefox 上均可运行,本质是一个“为特定任务定制的迷你网站”。

模块整体由三节课程组成(见 5-browser-extension/README.md):

  1. About the browser——浏览器如何工作、如何部署扩展;
  2. Forms, browsers and local storage——构建表单、调用 API、使用 localStorage;
  3. Background tasks and performance——后台任务与性能测量。

starter 目录(5-browser-extension/start/)就是第 2 节的起步代码:一个带编号注释的空壳 src/index.js,供学习者按编号分段填写代码:

//1
// form fields
// results divs

//6
//call the API

//5
//set up user's api key and region

//4
// handle form submission

//3 initial checks

//2
// set listeners and start app

这些编号注释对应课程文档中的代码段://1 处放 DOM 引用(.form-data.api-key.result-container 等 querySelector),//2 处放事件监听与 init() 启动,//3 放初始化检查(读取 localStorage 判断是否首次用户),//4 放表单提交处理,//5 放 API 密钥与区域配置,//6 放真正的 API 调用。这个“按编号填空”的结构是仓库对初学者友好的关键设计——完整填好的参考实现位于 solution/src/index.js

二、环境准备与构建:npm install / npm run build

starter 文档的“Getting Started”给出了完整的最小操作链,这里结合 start/package.json 补充各步骤的含义与版本前提。

2.1 前置条件

  • 计算机上已安装 npm;
  • start/ 目录的代码拷贝到本地一个文件夹中。

package.jsonengines 字段可确认运行环境要求:npm >= 9.0.0、Node.js >= 18.0.0,低于此版本的工具链不应作为前提。

2.2 安装依赖

npm install

该命令依据 package.json 安装两类依赖:

依赖 版本 用途
webpack ^5.105.0 将 ES module 源码(含 axios 的 import)打包为浏览器可直接加载的脚本
webpack-cli ^5.1.4 提供 webpack 命令行入口,供 npm run build / npm run watch 调用
axios ^1.15.0 运行时依赖,solution 中用它发起对 CO2 Signal API 的 HTTP GET 请求

2.3 构建扩展

npm run build

package.json 中的 scripts 定义如下:

"scripts": {
	"test": "echo \"Error: no test specified\" && exit 1",
	"watch": "webpack --watch",
	"build": "webpack"
}
  • npm run build 执行一次 webpack,产物输出到 dist 目录(文档中“Open the 'dist' folder at the prompt”即指该目录);
  • npm run watch 开启监听模式,适合边写代码边热更新;
  • test 脚本当前是占位实现(无测试用例,exit 1),说明仓库并未为扩展提供自动化测试。

starter 与 solution/package.json 的依赖结构完全一致(同样的 engines、scripts、axios 依赖),差异只在 webpack 的补丁版本号,两者是同一套脚手架的“待填写版”与“完成版”。

三、在 Edge 中加载未打包扩展(Load Unpacked)

starter 文档给出的安装步骤是理解浏览器扩展部署流程的核心,完整继承如下:

  1. 在 Edge 浏览器右上角打开“三个点”菜单,找到 Extensions(扩展)面板;
  2. 在面板中选择 Load unpacked(加载未打包的扩展);
  3. 在弹出的目录选择框中打开构建产物 dist 文件夹,扩展即被加载。

在 Edge 中通过 Load Unpacked 加载扩展

这一步之所以要求先 npm run build,是因为扩展入口必须引用 webpack 打包后的脚本——源码里 import axios from 'axios' 这类 ES module 语法与多文件依赖不能直接交给扩展宿主运行,必须由 webpack 合并成自包含的 bundle。从 solution/src/index.js 的第一行 import axios from 'axios' 即可印证:不经打包,浏览器扩展上下文无法解析这个模块。

四、配置运行:API 密钥与区域代码

扩展加载后并不能直接用。starter 文档明确列出两项必需配置:

  • CO2 Signal API 密钥:在 CO2 Signal 官网通过邮箱注册获取(文档说明是在该页面输入邮箱领取);
  • 区域代码:对应 Electricity Map 的地区编码,可查询 Electricity Map 的区域列表接口 api.electricitymap.org/v3/zones 获得。文档以作者所在的波士顿为例,使用的区域代码是 US-NEISO(新英格兰独立系统运营商区域)。

把这两项填入扩展界面的表单后,扩展栏中的彩色圆点会随你所在地区的能源使用强度变化而变色,提示用户当前时段哪些高能耗活动“合适”、哪些应当推迟。文档同时交代了这一“dot”(圆点)系统的灵感来源:加州排放量的 Energy Lollipop 扩展的图标配色机制。

从源码结构看,区域代码的取值直接决定 API 查询参数:solution/src/index.js 中请求以 params: { countryCode: region } 传给 https://api.co2signal.com/v1/latest,因此区域代码填错(比如误填城市名)会直接导致查询失败,进入 catch 分支显示“Sorry, data unavailable for the selected region.”的报错文案。

五、源码级纵深:starter 编号注释背后跑通了什么

结合 solution/src/index.js 的完整实现,可以看清 starter 中每段编号注释对应的真实逻辑:

5.1 DOM 引用(对应 //1)

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 密钥)与结果展示区(碳强度、化石燃料占比、加载/错误状态)全部固化为常量引用,后续所有 UI 状态切换都围绕它们的 style.displaytextContent 展开。

5.2 本地存储驱动的初始化(对应 //3、//5)

init() 是应用启动入口:

const init = async () => {
	const storedApiKey = localStorage.getItem('apiKey');
	const storedRegion = localStorage.getItem('region');

	//set icon to be generic green
	chrome.runtime.sendMessage({
		action: 'updateIcon',
		value: { color: 'green' },
	});

	if (storedApiKey === null || storedRegion === null) {
		// 首次用户:显示表单
		form.style.display = 'block';
		// ...
	} else {
		// 回头用户:直接拉取数据
		displayCarbonUsage(storedApiKey, storedRegion);
		clearBtn.style.display = 'block';
	}
};

关键行为:用 localStorage 中是否同时存在 apiKeyregion 两个键来区分首次用户与回头用户;首次访问展示表单,回头访问则跳过表单直接发起 API 调用。reset() 只清除 region 键并重新执行 init(),让用户可以重新选择地区。课程文档(2-forms-browsers-local-storage/README.md)特别强调:扩展拥有与网页隔离的独立 localStorage,且在 DevTools 的 Application 面板下可以查看这些数据。

5.3 API 调用与数据校验(对应 //6)

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;

				// ✅ Validate required data before using
				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);
				// ... 更新 usage / fossilfuel / myregion 等 UI
			});
	} catch (error) {
		console.warn('Data fetch failed:', error.message);
		errors.textContent = 'Sorry, data unavailable for the selected region.';
	}
};

这段代码集中体现了本课程的几个知识点:auth-token 请求头完成 API 鉴权、async/await 保持扩展界面在等待网络期间不冻结、可选链 response?.data?.data 与空值校验防御异常响应、catch 分支把网络错误转换为用户可读的提示文案。

5.4 “碳点”变色机制(starter 文档的最后一块拼图)

starter 文档提到“彩色圆点会随地区能源使用变化”,其实现就在 calculateColor

calculateColor = async (value) => {
	let co2Scale = [0, 150, 600, 750, 800];
	let colors = ['#2AA364', '#F5EB4D', '#9E4229', '#381D02', '#381D02'];
	// 找到 co2Scale 中距离当前值最近的刻度,取对应颜色
	chrome.runtime.sendMessage({ action: 'updateIcon', value: { color: closestColor } });
};

实现方式是“最近刻度映射”:把碳强度值对齐到 [0, 150, 600, 750, 800] 这一组刻度(单位是每千瓦时碳排放的克数),分别映射到绿、黄、棕、深棕等颜色,再通过 chrome.runtime.sendMessage 把颜色传给后台脚本去改写扩展图标。这正是 starter 文档中“extension bar 里的 dot 反映你地区的能源使用”的底层机制,也是课程第 3 节(background tasks)要深入讲的后台消息通道。

六、可验证的自检清单

按本文流程操作后,可逐项验证:

  1. npm run build 成功后出现 dist 目录——构建链正常;
  2. Edge 中 Load unpacked 选择 dist 后,扩展列表出现该扩展——加载成功;
  3. 打开扩展弹出界面,未填过配置时显示 API 密钥与区域两个输入项——init() 走了首次用户分支;
  4. 填入 API 密钥与区域代码(如 US-NEISO)提交后,界面显示碳强度(克/千瓦时)与化石燃料占比,扩展栏圆点变色——API 调用与 calculateColor 生效;
  5. 关闭重开扩展,表单不再出现而直接进入结果页——localStorage 持久化生效。

七、小结

本文以 starter 文档 为主线,完整覆盖了这个浏览器扩展项目的实操路径:环境要求(npm >= 9 / Node >= 18)、npm installnpm run build(webpack 打包到 dist)、Edge 的 Load unpacked 加载方式、CO2 Signal API 密钥与区域代码(如波士顿的 US-NEISO)的获取与配置,以及彩色“碳点”的用户价值。配合 start 的空壳源码solution 的完整实现第 2 节课程文档,你可以从“会装、会用”进一步走到“看得懂每一行为什么这么写”的程度。

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

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388