:调用 CO2 Signal API 并用 Local Storage 为扩展记忆用户设置)
Web-Dev-For-Beginners 浏览器扩展实战二调用 CO2 Signal API 并用 Local Storage 为扩展记忆用户设置【免费下载链接】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 仓库浏览器扩展模块的第二课讲解如何让一个静态的表单页面“活起来”通过document.querySelector建立 DOM 引用、用addEventListener响应表单提交、用localStorage持久化用户设置并用fetchasync/await调用 CO2 Signal API 获取实时碳强度数据。读完后你能够独立完成一个带记忆功能、带加载与错误状态、可接入任意 REST API 的 Manifest V3 浏览器扩展页面逻辑。项目背景一个会记忆、能联网的扩展页面本模块的总目标是构建一个可在 Edge、Chrome、Firefox 中运行的“Carbon Trigger”扩展用户输入 API 密钥和区域代码后扩展会查询该区域电网的碳排放强度每千瓦时排放的 CO₂ 克数与化石燃料发电占比帮助使用者判断当前时段是否适合进行高耗电活动。模块导读见 浏览器扩展总览。第一节课你已经完成了表单界面本课Forms and local storage要解决两个核心问题API 集成教代码学会与其他服务通信——发起 HTTP 请求、携带认证头、解析 JSON 响应、处理失败数据持久化利用浏览器的 Local Storage 以“键值对”形式记住用户设置让扩展在下次打开时自动恢复状态就像图书馆的卡片目录记住每本书的位置。对应到仓库里的工程结构你需要关注三处文件start/src/index.js练习用的起点文件用注释//1//6标出了六段代码应放置的位置表单字段引用、监听器、初始化检查、表单提交、用户设置、API 调用solution/src/index.js仓库提供的完整参考实现start/dist/manifest.json扩展清单声明了 Manifest V3、Service Worker 与弹出页面。从 start/dist/manifest.json 可以看到几个关键声明manifest_version: 3、action: { default_popup: index.html }——即点击工具栏图标时弹出 start/dist/index.html 页面本课所有 JavaScript 逻辑都运行在这个弹出页的上下文里host_permissions: [all_urls]则授予扩展向外部 API 发起请求的权限这正是fetch能跨域访问 CO2 Signal 服务的前提。第一步建立可操作的 DOM 元素引用JavaScript 要操控界面首先需要拿到具体元素的引用。仓库练习文件 start/src/index.js 中标注//1的位置正是放置这些引用的地方。用const变量捕获每个关键元素// form fields const form document.querySelector(.form-data); const region document.querySelector(.region-name); const apiKey document.querySelector(.api-key); // results 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);这段代码做的事用document.querySelector()配合 CSS 类选择器捕获表单元素.form-data、.region-name、.api-key建立到结果展示区.result-container、.carbon-usage、.fossil-fuel、.my-region的引用建立到 UI 状态元素加载指示器.loading、错误提示.errors、清除按钮.clear-btn的引用每个引用存入const变量供后文各处复用避免反复查询 DOM。第二步绑定事件监听器让扩展响应用户操作事件监听器是代码“监视”用户交互的方式。在练习文件的//2位置添加form.addEventListener(submit, (e) handleSubmit(e)); clearBtn.addEventListener(click, (e) reset(e)); init();要点解析为表单绑定submit监听器——用户按回车或点击提交时触发为清除按钮绑定click监听器用于重置表单事件对象(e)被显式传给处理函数供后续调用e.preventDefault()脚本末尾立即调用init()根据本地存储决定扩展的初始状态这里用了箭头函数的简写形式比传统函数表达式更简洁二者效果等价。快速自检如果在表单提交处理中忘记e.preventDefault()会怎样页面会按浏览器默认行为重新加载丢失全部 JavaScript 状态中断用户体验。第三步初始化与重置——基于 Local Storage 的状态机init()是扩展的“导航系统自检”检查是否已有用户用过扩展并据此调整界面reset()则让用户回到全新状态。对应练习文件的//3位置function init() { // 检查用户是否曾保存过 API 凭据 const storedApiKey localStorage.getItem(apiKey); const storedRegion localStorage.getItem(regionName); // 把扩展图标设为通用绿色下一课实现的占位逻辑 // TODO: 在下一课中实现图标更新 if (storedApiKey null || storedRegion null) { // 首次使用显示设置表单 form.style.display block; results.style.display none; loading.style.display none; clearBtn.style.display none; errors.textContent ; } else { // 回访用户自动加载其已保存的数据 displayCarbonUsage(storedApiKey, storedRegion); results.style.display none; form.style.display none; clearBtn.style.display block; } } function reset(e) { e.preventDefault(); // 清除已存区域允许用户选择新地点 localStorage.removeItem(regionName); // 重新进入初始化流程 init(); }执行流程从浏览器本地存储读取apiKey与regionName若任一为null首次用户显示设置表单、隐藏结果/加载/清除按钮并清空错误提示若两者都存在回访用户隐藏表单、显示清除按钮并直接调用displayCarbonUsage()拉取数据reset()删除已存的区域键值后重新执行init()把扩展拉回首次使用的状态。整个流程可以看作一个由本地存储驱动的状态机Local Storage 的关键特性数据跨浏览器会话持久存在区别于 sessionStorage以键值对存储核心方法是getItem()/setItem()/removeItem()键不存在时getItem()返回null这正好被init()用作“首次/回访”的判据数据在关闭浏览器、重启电脑甚至浏览器崩溃后依然保留且读取无网络延迟隔离性浏览器扩展拥有独立于普通网页的本地存储空间不会与访问过的网站互相干扰。打开开发者工具F12→Application标签 → 展开Local Storage即可检查这些键值安全提示在真实生产应用中把 API 密钥存在 LocalStorage 是有风险的因为页面上的任何 JavaScript 都能读到它。本课为了学习目的这样做完全可以但生产级应用应把敏感凭据放在受保护的服务器端。第四步处理表单提交浏览器默认在表单提交后刷新页面而我们要拦截这一行为以获得单页应用的流畅体验。对应练习文件的//4位置function handleSubmit(e) { e.preventDefault(); setUpUser(apiKey.value, region.value); }e.preventDefault()阻止默认的整页刷新行为从 API 密钥与区域输入框中用.value提取用户输入把两个值交给setUpUser()处理。另外注意HTML 中的表单字段带有required属性浏览器会在事件派发之前自动校验用户是否填写了 API 密钥与区域——也就是说只有校验通过handleSubmit才会运行。第五步保存用户偏好并触发首次请求setUpUser()负责“落盘 转场”把凭据存入本地存储切换到加载状态然后发起第一个 API 调用。对应练习文件的//5位置function setUpUser(apiKey, regionName) { // 为用户以后的会话保存凭据 localStorage.setItem(apiKey, apiKey); localStorage.setItem(regionName, regionName); // 更新 UI显示加载状态 loading.style.display block; errors.textContent ; clearBtn.style.display block; // 用用户凭据获取碳用量数据 displayCarbonUsage(apiKey, regionName); }逐步来看先setItem保存密钥与区域供下次init()直接恢复然后显示加载指示器、清空历史错误、亮出清除按钮最后调用displayCarbonUsage()。数据持久化与界面状态切换在同一个动作里协调完成用户感知到的只是“点了提交开始加载”。第六步调用 CO2 Signal API 并展示碳用量数据这是本课的重头戏练习文件//6位置。API 是不同应用之间通信的方式像 19 世纪的电报网络操作者向远方站点发送请求并取回所需信息。REST 风格的 API 使用标准 HTTP 方法GET/POST/PUT/DELETE、以 URL 为端点、通常返回 JSON。本扩展使用的 CO2 Signal API 提供全球电网的实时碳强度数据接口为GET https://api.co2signal.com/v1/latest?countryCode区域代码认证通过请求头auth-token完成。认证流程如下用现代fetch实现无第三方依赖// 现代 fetch 方案无需外部依赖 async function displayCarbonUsage(apiKey, region) { try { // 从 CO2 Signal API 获取碳强度数据 const response await fetch(https://api.co2signal.com/v1/latest, { method: GET, headers: { auth-token: apiKey, Content-Type: application/json }, // 为指定区域添加查询参数 ...new URLSearchParams({ countryCode: region }) { url: https://api.co2signal.com/v1/latest?countryCode${region} } }); // 检查 API 请求是否成功 if (!response.ok) { throw new Error(API request failed: ${response.status}); } const data await response.json(); const carbonData data.data; // 计算取整后的碳强度 const carbonIntensity Math.round(carbonData.carbonIntensity); // 用获取的数据更新用户界面 loading.style.display none; form.style.display none; myregion.textContent region.toUpperCase(); usage.textContent ${carbonIntensity} grams (grams CO₂ emitted per kilowatt hour); fossilfuel.textContent ${carbonData.fossilFuelPercentage.toFixed(2)}% (percentage of fossil fuels used to generate electricity); results.style.display block; // TODO: calculateColor(carbonIntensity) - 下一课实现 } catch (error) { console.error(Error fetching carbon data:, error); // 显示用户友好的错误信息 loading.style.display none; results.style.display none; errors.textContent Sorry, we couldn\t fetch data for that region. Please check your API key and region code.; } }拆解这段函数用到的现代 JavaScript 要点async/await让异步流程以接近同步的可读性书写。发起请求期间扩展不会冻结其他代码照常执行——如果同步阻塞就像空管在等某架飞机应答时停掉了所有航班response.ok检查在解析 JSON 之前就抛出API request failed: status把 HTTP 层错误如 401 密钥无效、404 区域不存在与数据错误区分开try/catch兜底无论网络断开、请求失败还是数据结构异常都会落到catch先console.error记录再把“请检查 API 密钥和区域代码”这样的友好提示写入.errors而不是让扩展直接崩溃模板字符串${}组织展示文本carbonIntensity取整后显示为“每千瓦时排放的 CO₂ 克数”fossilFuelPercentage保留两位小数显示为“化石燃料发电占比”认证auth-token请求头携带用户的 API 密钥countryCode查询参数指定电网区域例如波士顿对应US-NEISO。至此整条链路已经闭合DOM 引用 → 事件监听 → 本地存储判断状态 → 表单提交 → 偏好落盘 → 异步请求 → 结果展示与错误降级。对照仓库参考实现从 fetch 到 axios仓库中的完整参考实现 solution/src/index.js 与课文代码结构一一对应querySelector引用、init、handleSubmit、setUpUser、displayCarbonUsage、reset、底部监听器与init()启动调用但有几处值得注意的实现差异请求库参考实现使用axios见 solution/package.json 中axios: ^1.15.0依赖查询参数通过params: { countryCode: region }传递比手工拼接 URL 更清晰课文则演示零依赖的原生fetch两条路线都值得掌握存储键名参考实现使用localStorage.setItem(region, region)而课文使用regionName。同一扩展内读写必须一致改动时注意init/setUpUser/reset三处同步数据校验参考实现在渲染前显式检查carbonIntensity与fossilFuelPercentage是否为null缺失即抛出 “Missing carbon intensity or fossil fuel data”这比课文版本更严谨图标联动参考实现包含calculateColor()按[0, 150, 600, 750, 800]的阈值把碳强度映射为[#2AA364, #F5EB4D, #9E4229, #381D02]等颜色并通过chrome.runtime.sendMessage({ action: updateIcon, ... })让 manifest.json 声明的 Service Worker 更新工具栏图标。课文中该部分标注为TODO属于第三课后台任务与性能的内容。从源码结构看参考实现的init()中同样通过chrome.runtime.sendMessage先把图标设为绿色——这说明“图标按碳强度变色”是后续课程的主题本课聚焦表单、存储与 API。构建与验证按 start 目录说明 的步骤验证本课成果npm install npm run build构建脚本来自 start/package.jsonbuild: webpack要求 Node ≥ 18、npm ≥ 9。随后在浏览器以 Edge 为例右上角“…”菜单进入扩展管理面板选择“Load Unpacked”并选中dist文件夹即可加载扩展输入 API 密钥与区域代码后查看结果。修改代码后重新npm run build并在浏览器中刷新扩展即可看到变化。进阶挑战与延伸练习原文档为学习者准备了三个层次的挑战Copilot Agent 挑战在displayCarbonUsage基础上实现增强版要求包含——1) 失败请求的指数退避重试机制2) 发起请求前对区域代码做输入校验3) 带进度指示的加载动画4) API 响应在 localStorage 中按 30 分钟过期时间戳缓存5) 展示历史调用数据并附 TypeScript 风格的 JSDoc 注释。这是对缓存策略、速率限制与优雅降级等生产级模式的综合演练。浏览器 API 探索任选一个 Web API 做小型演示——Geolocation定位、Notifications桌面通知、HTML Drag and Drop、Web Storage高级本地存储或 FetchXMLHttpRequest的现代替代并回答它解决什么现实问题如何处理错误与边界情况有哪些安全考量跨浏览器支持如何课程作业Adopt an API——自选一个外部 API构建一个解决真实问题的浏览器扩展。要求包含表单输入、正确的错误处理、本地存储偏好、加载状态使用 ES6 与async/await并通过评分表API 集成、代码质量、用户体验、本地存储、文档自评。学习路径与复习清单本课在整体学习曲线中的定位原文档给出的时间线阶段主题要点15 分钟DOM 基础querySelector掌握、事件监听、状态管理入门20 分钟Local Storage键值持久化、会话管理、偏好处理、存储检查工具25 分钟表单处理校验、事件拦截、数据提取、UI 状态切换35 分钟API 集成HTTP 请求、认证模式、JSON 解析、响应处理40 分钟异步编程Promise、async/await、错误管理、非阻塞操作30 分钟错误处理try/catch、友好提示、优雅降级、调试技巧1 周高级模式缓存策略、速率限制、重试机制、性能优化1 个月生产能力安全最佳实践、API 版本化、监控与日志、可扩展架构5 分钟内可做打开任意网站 DevTools 的 Application 面板查看 localStorage写一个简单 HTML 表单测试浏览器校验在控制台练习localStorage读写用 Network 面板观察表单提交的数据。1 小时内可做构建一个保存用户偏好的扩展表单实现客户端表单校验并给出有用的错误信息练习chrome.storageAPI 做扩展数据持久化让界面响应已保存的用户设置。一周可做完成带表单功能的完整扩展掌握 local / sync / session 三类存储实现自动补全等高级表单功能为用户数据添加导入/导出跨浏览器充分测试。一个月可做用各类存储 API 构建复杂应用学习离线优先开发模式参与涉及数据持久化的开源项目掌握隐私优先开发与 GDPR 合规沉淀可复用的表单处理与数据管理库。学完本课你应能独立回答这些问题DOM 引用如何让 JavaScript 掌控界面Local Storage 为何能在浏览器会话之间保持持久async/await如何让 API 调用不冻结扩展请求失败时catch分支如何把技术错误翻译成用户能理解的提示以及——如果把 Local Storage 和 API 结合起来你会如何设计一个“存储供 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创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考