Web-Dev-For-Beginners 浏览器扩展课程第一部分:理解浏览器与打造第一个 Manifest V3 碳足迹扩展
本篇基于 Web-Dev-For-Beginners 课程的浏览器扩展项目第一部分(5-browser-extension/1-about-browsers),系统讲解浏览器的工作机制(URL 解析、HTTP 请求、渲染引擎、缓存与 cookies)、浏览器扩展的安装与加载流程,并给出一个可复制、可运行的实战路径:基于 starter 代码库、Webpack 5 构建链和 Manifest V3 清单,动手写出一个展示本地区域碳足迹的 Edge/Chrome 扩展的第一阶段——配置表单与结果界面的 HTML 搭建与首次构建加载。读完本文,你将能够解释浏览器处理网页请求的完整链路,并独立完成扩展的 npm install、npm run build 与 "Load Unpacked" 开发者模式安装闭环。
一、浏览器是什么:从 WorldWideWeb 到现代渲染引擎
浏览器扩展(Browser Extension)是为浏览器添加额外功能的迷你应用。但在动手构建之前,必须先理解浏览器本身的工作方式。
定义:浏览器是让用户能够访问服务器上的内容、并将其呈现为网页的软件应用。✅ 一点历史:第一个浏览器名为 WorldWideWeb,由 Sir Timothy Berners-Lee 于 1990 年创建。
浏览器处理一次网页请求的完整链路(原文档核心内容):
- 用户输入 URL(Uniform Resource Locator)地址连接互联网;
- 通过
http或https协议(Hypertext Transfer Protocol)访问资源; - 浏览器与 Web 服务器通信,拉取网页内容(HTML、CSS、JavaScript);
- 此刻,浏览器的**渲染引擎(Rendering Engine)**负责在用户的设备上——无论是手机、台式机还是笔记本——将内容输出为可视化页面。
除了渲染,浏览器还提供两项关键能力:
- 内容缓存(Caching):将已获取的网页资源缓存下来,避免每次访问都回到服务器取回;
- Cookies 存储:cookies 是包含浏览活动记录所需信息的小型数据,浏览器负责持久化保存它们。
这两项能力正是后续课程中扩展功能(本地存储 API 键、区域代码)所依赖的浏览器基础设施。
浏览器并不相同:跨浏览器差异是必须面对的约束
原文档强调了一个关键点:所有浏览器并不完全一样。每个浏览器各有优劣,专业 Web 开发者必须理解如何让网页在各种浏览器(cross-browser)下都正常工作,其中就包括小视口(viewport)控制(如手机屏幕)与离线(offline)处理等任务。
- 规划功能时,用 caniuse 网站(原文推荐加入书签)上的技术支持清单,可以判断你的技术在各主流浏览器的支持程度,从而决定优先支持哪些浏览器;
- 想知道你的用户群中哪些浏览器最流行?查看**分析(analytics)**数据——Web 开发流程中会安装各类分析包,它们会告诉你不同主流浏览器中的使用占比,帮助你确定支持优先级。
二、为什么做浏览器扩展:小而专,解决重复操作
做扩展的动因很直接:当某个重复性操作需要频繁进出浏览器时,扩展是最便捷的答案。原文档给出的典型场景:
- 在多个网页中反复查看颜色值 → 安装一个 color-picker 类扩展;
- 难以记住各站点的密码 → 安装 password-management 类扩展。
浏览器扩展往往把数量有限的几项任务做得非常精到,这也是它易于开发、趣味十足的原因。✅ 自检问题:你最喜欢的浏览器扩展是什么?它完成什么任务?
三、扩展的安装与加载流程:npm build → Load Unpacked / Reload
在动手写代码前,先走一遍"编写并部署浏览器扩展"的完整流程。各浏览器管理这一过程的方式略有差异,但 Chrome、Firefox、Edge 的流程本质相同,原文档以 Edge 为例:
- 用
npm build构建扩展(starter 代码库中对应的实际脚本是npm run build,见下文 package.json 的scripts.build: "webpack"); - 点击浏览器右上角的
...图标,进入 Extensions(扩展)管理面板; - 若是首次安装:选择
Load Unpacked,把构建产物文件夹(本项目为/dist)加载进去; - 若扩展已安装:代码改动后点击该扩展旁的
reload重新加载即可。
✅ 注意区分:本流程针对你自己构建的扩展(开发者侧载)。若要安装各浏览器官方商店(如 Microsoft Edge Add-ons)已发布上架的扩展,则直接去对应商店搜索安装即可——这也正是文末"挑战"环节要做的事。
四、项目准备:一个展示区域碳足迹的扩展
本项目的产品形态是一个区域碳足迹扩展:显示你所在地区的能源使用量与能源来源。扩展内置一个表单,用于收集访问 CO2 Signal API 所需的 API 密钥,并在结果界面输出该地区的碳使用数据。
所需资源清单(原文档完整继承):
- API 密钥:CO2 Signal 的 API key——在其官网页面输入邮箱即可收到密钥邮件;
- 区域代码:对应 Electricity Map 的区域编码(Electricity Map 的 zones 接口提供全集;例如波士顿使用
US-NEISO); - starter 代码:仓库中的 start 文件夹 即课程起点,后续所有代码都在这个文件夹内完成;
- NPM:Node 的包管理工具,需本地安装;它会按 package.json 中列出的依赖安装好构建所需的 Web 资产。
✅ 实操提示:拿到 API key 和区域代码后先记下来,后续会反复用到。安全上切勿把密钥提交进代码仓库。
代码库结构:dist 与 src 的分工
原文档给出的目录树如下(dist 放构建产物与默认配置,src 放你书写的源码):
dist
-|manifest.json (defaults set here) # 扩展默认配置
-|index.html (front-end HTML markup) # 前端 HTML 标记
-|background.js (background JS) # 后台脚本
-|main.js (built JS) # Webpack 打包后的 JS
src
-|index.js (your JS code goes here) # 你的 JS 代码写在这里
结合仓库实际文件可以进一步确认每个角色的实现事实:
dist/manifest.json 是 Manifest V3 清单,决定了扩展的身份、权限与入口(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:使用 MV3,后台脚本以service_worker形式声明(background.js);"action.default_popup": "index.html":点击扩展图标时弹出的就是dist/index.html,这正是我们本阶段要填充表单与结果区的文件;"host_permissions": ["<all_urls>"]:为后续调用外部 API 预留的宿主权限。
dist/index.html 目前是带占位注释的骨架(index.html):
<body class="container">
<img alt="plants and people" src="images/plants-people.png" />
<div>
<h1>Welcome to Your Personal Carbon Trigger!</h1>
</div>
<!--form area--> <!-- 本阶段要填充的表单区域 -->
<!--result area--> <!-- 本阶段要填充的结果区域 -->
<script src="main.js"></script>
</body>
页面通过 styles.css 引入基础样式(基于 Basic.css 的变量体系,支持 prefers-color-scheme: dark 暗色模式,见 styles.css),并以 <script src="main.js"> 挂载打包产物。
src/index.js 是带编号注释的脚手架(index.js),为后续课程的实现排好了 1–6 的落点:1 表单字段选择器、2 设置监听并启动应用、3 初始检查、4 处理表单提交、5 设置用户的 API key 与区域、6 调用 API——本阶段(第一部分)先只动 HTML,JS 逻辑留到后续课程。
五、构建扩展的 HTML:两个屏幕(配置表单 + 结果区)
这个扩展有两个界面:第一屏收集 API 密钥与区域代码,第二屏输出该区域的碳使用数据。
现在动手:在 /dist 文件夹的 index.html 中,在 <!--form area--> 处填入表单区域(原文档完整代码):
<form class="form-data" autocomplete="on">
<div>
<h2>New? Add your Information</h2>
</div>
<div>
<label>Region Name</label>
<input type="text" required class="region-name" />
</div>
<div>
<label>Your API Key from tmrow</label>
<input type="text" required class="api-key" />
</div>
<button class="search-btn">Submit</button>
</form>
这个表单的用途是:让用户填入保存的信息,并将它们写入本地存储(localStorage)。结构要点:
class="form-data":整个表单的容器类,后续 JS 用它绑定submit事件;class="region-name"与class="api-key":两个必填输入框(required),JS 通过这两个类名取值并存入 localStorage;autocomplete="on":允许浏览器自动填充,改善首次配置体验;class="search-btn":提交按钮。
接着,在表单下方添加结果区域(原文档完整代码):
<div class="result">
<div class="loading">loading...</div>
<div class="errors"></div>
<div class="data"></div>
<div class="result-container">
<p><strong>Region: </strong><span class="my-region"></span></p>
<p><strong>Carbon Usage: </strong><span class="carbon-usage"></span></p>
<p><strong>Fossil Fuel Percentage: </strong><span class="fossil-fuel"></span></p>
</div>
<button class="clear-btn">Change region</button>
</div>
各区域的职责(结合仓库最终实现印证):
loading:请求 API 期间的加载提示;errors:API 失败或数据无效时显示错误信息;data:保留原始数据用于开发调试;result-container:格式化展示碳数据;其中my-region、carbon-usage、fossil-fuel三个 span 就是后续 JS 填充文本的目标节点;clear-btn("Change region"):允许用户更换区域、重新配置扩展。
可以对照 solution 目录中的成品构建产物 验证这些类名正是后续 JS 的查询目标:实现中通过 document.querySelector(".form-data")、.region-name、.api-key、.errors、.loading、.result-container、.carbon-usage、.fossil-fuel、.my-region、.clear-btn 分别取到上述节点,把 apiKey 与 region 写入 localStorage,随后以 auth-token 请求头调用 CO2 Signal 的 /v1/latest 接口,取回 carbonIntensity(单位:克 CO2/千瓦时)与 fossilFuelPercentage(化石燃料占比)后填入对应 span,并用 display 样式在 loading / 表单 / 结果三种状态间切换。
六、构建链:npm install 与 Webpack 打包
HTML 写好后,回到终端完成本阶段的收尾——安装依赖并构建:
npm install
该命令使用 Node 的包管理器 npm,按 package.json 安装扩展构建流程所需的依赖。从仓库的 package.json 可以确认其具体内容:
{
"name": "carbon-trigger-extension",
"engines": {
"npm": ">=9.0.0",
"node": ">=18.0.0"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"watch": "webpack --watch",
"build": "webpack"
},
"devDependencies": {
"webpack": "^5.105.0",
"webpack-cli": "^5.1.4"
},
"dependencies": {
"axios": "^1.15.0"
}
}
webpack+webpack-cli是构建工具链:Webpack 是一个 bundler,负责控制编译与打包;axios是运行期依赖,供后续课程发起 API 请求使用(starter 的 main.js 目前为空文件,构建后才会被填充——对照 solution 的构建产物 可以看到 axios 库已被完整打进 bundle,这正是"代码被捆绑(bundled)"的直观证据);engines声明了运行前提:Node >= 18、npm >= 9,环境不满足时npm install会直接报错;scripts中除build: "webpack"外还提供watch: "webpack --watch"——开发时开启 watch 模式,src/下的改动会被自动重新打包,省去每次手动 build。
构建完成后:
npm run build
输出即 /dist/main.js。此时若把该扩展作为开发者扩展部署到 Edge,弹出窗口中表单就会以干净排版显示——本阶段(第一部分)到此完成,后续课程将在此基础上加入交互逻辑。
从源码结构看:扩展图标"能量点"系统
作为对整体架构的预告,starter 的 background.js 展示了 MV3 service worker 如何与弹窗页面协作:它监听 chrome.runtime.onMessage 事件,当收到 action === 'updateIcon' 的消息时,调用 drawIcon 函数,用 OffscreenCanvas 画布绘制一个 100×100 的彩色圆点并 getImageData 后通过 chrome.action.setIcon 更新扩展栏图标颜色。solution 的构建产物中可以看到,carbonIntensity 会先按 [0, 150, 600, 750, 800] 分档取最近值,再映射到 #2AA364(绿)→ #381D02(深褐)的色阶——这就是浏览器扩展栏上那枚"碳强度指示点"(灵感来自 Energy Lollipop 扩展)。理解这条"popup 发消息 → service worker 改图标"的调用链,是理解后续课程背景脚本(Part 3)的钥匙。
七、练习、挑战与自我提升
课后挑战(原文档 🚀 Challenge):去浏览器扩展商店浏览,挑选一个扩展安装到你的浏览器上,然后设法"拆解"它的文件。你能发现什么?——商店扩展的解包结构与本课程的 manifest.json + 打包 JS 结构如出一辙,这是把课堂知识落到实处的最好方式。
课后作业:本课的正式作业是 Restyle your extension,要求你在保持可用性的前提下为扩展设计配色、字体与布局,用 npm run build + 重新加载的方式测试表单、加载、结果、错误各视觉状态,并按评分表(视觉设计 / 功能性 / 代码质量 / 可访问性四个维度)自评。
复习与自学习:本课带我们回顾了一点浏览器历史。建议顺着这条线继续:阅读 Web 浏览器发展史、万维网(World Wide Web)的历史,以及 Sir Tim Berners-Lee 关于 Web 诞生 30 年的访谈,理解万维网先驱们当初如何构想着它的用途——这会让你在后续设计扩展功能时更有历史纵深。
可立即动手的清单(结合原文档与仓库):
- 打开浏览器的扩展管理页(如
edge://extensions或chrome://extensions),开启开发者模式,清点你已安装的扩展; - 在 DevTools 的 Network 面板里观察一次网页加载,验证"请求 → 响应 → 渲染"的链路;
- 按本文第五、六节完成
dist/index.html的表单与结果区填写,跑通npm install、npm run build与 Load Unpacked 全流程; - 对照 solution 的完整实现 与 starter 的起点代码,逐行理解从脚手架到成品之间差了什么。
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


