|
|
7 months ago | |
|---|---|---|
| .. | ||
| README.md | 7 months ago | |
| assignment.md | 7 months ago | |
README.md
浏览器扩展项目第二部分:调用 API,使用本地存储
journey
title 你的 API 集成与存储旅程
section 基础
设置 DOM 引用: 3: Student
添加事件监听器: 4: Student
处理表单提交: 4: Student
section 数据管理
实现本地存储: 4: Student
构建 API 调用: 5: Student
处理异步操作: 5: Student
section 用户体验
添加错误处理: 5: Student
创建加载状态: 4: Student
优化交互: 5: Student
课前测验
介绍
还记得你刚开始构建的浏览器扩展吗?现在你拥有一个看起来很不错的表单,但它本质上是静态的。今天我们将通过连接真实数据并赋予它记忆功能来让它变得生动。
想想阿波罗任务控制计算机——它们不仅仅展示固定信息。它们不断与航天器通信,更新遥测数据,并记住关键的任务参数。这就是我们今天要构建的动态行为。你的扩展将连接互联网,抓取真实的环境数据,并记住你下次的设置。
API 集成听起来可能很复杂,但实际上就是教你的代码如何与其他服务通信。无论你是在获取天气数据、社交媒体信息流,还是像今天一样获取碳足迹信息,关键都是建立这些数字连接。我们还将探讨浏览器如何持久保存信息——类似于图书馆如何使用卡片目录来记住书本位置。
完成本课后,你将拥有一个能够获取真实数据、保存用户偏好设置并提供流畅体验的浏览器扩展。让我们开始吧!
mindmap
root((动态扩展))
DOM Manipulation
Element Selection
Event Handling
State Management
UI Updates
Local Storage
Data Persistence
Key-Value Pairs
Session Management
User Preferences
API Integration
HTTP Requests
Authentication
Data Parsing
Error Handling
Async Programming
Promises
Async/Await
Error Catching
Non-blocking Code
User Experience
Loading States
Error Messages
Smooth Transitions
Data Validation
✅ 按照对应文件中的编号段落知道在哪里放置你的代码
设置扩展中要操作的元素
在你的 JavaScript 操作界面之前,需要先获取特定的 HTML 元素引用。就像望远镜需要对准特定的星星一样——伽利略研究木星的卫星前必须先定位并聚焦于木星。
在你的 index.js 文件中,我们将创建 const 变量来捕获每个重要表单元素的引用。这类似于科学家给他们的设备贴标签——这样他们不必每次都在整个实验室中搜索,就可以直接访问需要的东西。
flowchart LR
A[JavaScript 代码] --> B[document.querySelector]
B --> C[CSS 选择器]
C --> D[HTML 元素]
D --> E[".form-data"]
D --> F[".region-name"]
D --> G[".api-key"]
D --> H[".loading"]
D --> I[".errors"]
D --> J[".result-container"]
E --> K[表单元素]
F --> L[输入字段]
G --> M[输入字段]
H --> N[UI 元素]
I --> O[UI 元素]
J --> P[UI 元素]
style A fill:#e1f5fe
style D fill:#e8f5e8
style K fill:#fff3e0
style L fill:#fff3e0
style M fill:#fff3e0
// 表单字段
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');
这段代码的作用:
- 捕获 表单元素,使用
document.querySelector()结合 CSS 类选择器 - 创建 了区域名称和 API 密钥输入字段的引用
- 建立 结果显示元素的连接,用于展示碳使用相关数据
- 设置 访问 UI 元素,如加载指示器和错误信息
- 存储 每个元素引用在
const变量中,便于代码中反复使用
添加事件监听器
现在我们让你的扩展响应用户操作。事件监听器是你的代码监控用户交互的方式。想象早期电话交换机的操作员——他们监听来电,当有人想要通话时连接正确的线路。
sequenceDiagram
participant User
participant Form
participant JavaScript
participant API
participant Storage
User->>Form: 填写区域/API密钥
User->>Form: 点击提交
Form->>JavaScript: 触发提交事件
JavaScript->>JavaScript: handleSubmit(e)
JavaScript->>Storage: 保存用户偏好
JavaScript->>API: 获取碳数据
API->>JavaScript: 返回数据
JavaScript->>Form: 用结果更新界面
User->>Form: 点击清除按钮
Form->>JavaScript: 触发点击事件
JavaScript->>Storage: 清除保存的数据
JavaScript->>Form: 重置到初始状态
form.addEventListener('submit', (e) => handleSubmit(e));
clearBtn.addEventListener('click', (e) => reset(e));
init();
理解这些概念:
- 绑定 表单提交事件监听器,当用户按下回车或点击提交时触发
- 连接 清除按钮的点击监听器,用于重置表单
- 传递 事件对象
(e)给处理函数以实现额外控制 - 立即调用
init()函数,用于设置扩展的初始状态
✅ 注意这里使用的箭头函数简写语法。这种现代 JavaScript 写法比传统函数表达式更简洁,不过两者都有效!
🔄 教学检查点
事件处理理解:在进行初始化之前,请确保你能:
- ✅ 解释
addEventListener如何将用户操作连接到 JavaScript 函数 - ✅ 理解为何要将事件对象
(e)传给处理函数 - ✅ 认识
submit和click事件的区别 - ✅ 描述
init()函数何时运行及其原因
快速自测:如果忘记在表单提交时调用 e.preventDefault() 会怎么样?
答案:页面会重新加载,所有 JavaScript 状态丢失,用户体验中断
构建初始化和重置函数
让我们创建扩展的初始化逻辑。init() 函数就像船舶的导航系统检查仪器——它判断当前状态并相应调整界面。它会检查用户是否之前使用过你的扩展,并加载他们之前的设置。
reset() 函数则为用户提供一个重新开始的机会——类似科学家在实验间隙重置仪器以保证数据清洁。
function init() {
// 检查用户是否之前保存过API凭证
const storedApiKey = localStorage.getItem('apiKey');
const storedRegion = localStorage.getItem('regionName');
// 将扩展图标设置为通用绿色(未来课程的占位符)
// 待办事项:在下一课实现图标更新
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();
}
这里发生的事情分解:
- 从浏览器本地存储获取 保存的 API 密钥和区域数据
- 检测 是否为首次使用(无存储凭据)或是回访用户
- 为新用户显示 设置表单,并隐藏其他界面元素
- 为回访用户自动加载 保存的数据并显示重置选项
- 基于是否有数据 管理用户界面的状态
关于本地存储的关键概念:
- 数据持久保存,跨浏览器会话有效(与会话存储不同)
- 以键值对方式保存,使用
getItem()和setItem() - 当指定键无数据时返回
null - 提供简单方式 存储用户偏好和设置
💡 理解浏览器存储:LocalStorage 就像赋予你的扩展持久记忆。想象古代亚历山大图书馆如何保存卷轴——信息在学者离开再回来时依然可用。
关键特点:
- 关闭浏览器后依然保存数据
- 重启计算机和浏览器崩溃后数据依然存在
- 提供丰富存储空间保存用户偏好
- 无需网络延迟即可即时访问
重要提示:你的浏览器扩展有自己的独立本地存储,独立于普通网页。这保证了安全且避免与其他网站冲突。
你可以通过打开浏览器开发者工具(F12),进入 Application 选项卡,展开 Local Storage 部分查看存储的数据。
stateDiagram-v2
[*] --> CheckStorage: 扩展启动
CheckStorage --> FirstTime: 无存储数据
CheckStorage --> Returning: 发现数据
FirstTime --> ShowForm: 显示设置表单
ShowForm --> UserInput: 用户输入数据
UserInput --> SaveData: 存储到 localStorage
SaveData --> FetchAPI: 获取碳数据
Returning --> LoadData: 从 localStorage 读取
LoadData --> FetchAPI: 获取碳数据
FetchAPI --> ShowResults: 显示数据
ShowResults --> UserAction: 用户交互
UserAction --> Reset: 点击清除按钮
UserAction --> ShowResults: 查看数据
Reset --> ClearStorage: 删除保存的数据
ClearStorage --> FirstTime: 返回设置
⚠️ 安全注意事项:在生产环境中,将 API 密钥存储在 LocalStorage 存在安全风险,因为 JavaScript 代码可以访问这些数据。学习用途这样做可以,但实际应用应使用安全的服务器端存储敏感凭证。
处理表单提交
接下来我们处理用户提交表单时的行为。默认情况下,浏览器表单提交会重新加载页面,但我们要拦截这种行为,使体验更加流畅。
这种做法类似任务控制对航天器通信的处理——不是每次传输都重置整个系统,而是持续运行同时处理新信息。
创建一个函数,捕获表单提交事件并提取用户输入内容:
function handleSubmit(e) {
e.preventDefault();
setUpUser(apiKey.value, region.value);
}
以上我们做了:
- 阻止 表单默认提交行为避免刷新页面
- 提取 用户在 API 密钥和区域字段中的输入值
- 将 表单数据传递给
setUpUser()函数进行处理 - 保持 单页面应用行为,避免页面重新加载
✅ 请记得你的 HTML 表单字段带有 required 属性,所以浏览器会自动验证用户必须填写 API 密钥和区域后,才会执行此函数。
设置用户偏好
setUpUser 函数负责保存用户凭据并启动第一次 API 调用。这样实现从设置到展示结果的平滑过渡。
function setUpUser(apiKey, regionName) {
// 保存用户凭证以供将来会话使用
localStorage.setItem('apiKey', apiKey);
localStorage.setItem('regionName', regionName);
// 更新界面以显示加载状态
loading.style.display = 'block';
errors.textContent = '';
clearBtn.style.display = 'block';
// 使用用户凭证获取碳排放使用数据
displayCarbonUsage(apiKey, regionName);
}
一步步过程为:
- 保存 API 密钥和区域名到本地存储以便未来使用
- 显示 加载指示器告知用户正在获取数据
- 清除 任何先前显示的错误信息
- 显示 清除按钮,供用户以后重置设置
- 发起 API 调用获取真实的碳使用数据
这个函数通过协调数据持久化和界面更新,创造了无缝的用户体验。
显示碳使用数据
现在我们将扩展连接到外部数据源,通过 API 获取数据。这使你的扩展从独立工具变成可以访问互联网上实时信息的应用。
理解 API
API 是不同应用间通信的方式。可以把它想象成 19 世纪连接远距离城市的电报系统——操作员会向远方电报站发送请求,并收取返回的信息。每次你查看社交媒体、问语音助手问题、或用送货应用时,都离不开 API 帮助完成数据交换。
flowchart TD
A[您的扩展] --> B[HTTP 请求]
B --> C[CO2 信号 API]
C --> D{请求有效吗?}
D -->|是| E[查询数据库]
D -->|否| F[返回错误]
E --> G[碳排放数据]
G --> H[JSON 响应]
H --> I[您的扩展]
F --> I
I --> J[更新界面]
subgraph "API 请求"
K[请求头: auth-token]
L[参数: countryCode]
M[方法: GET]
end
subgraph "API 响应"
N[碳强度]
O[化石燃料百分比]
P[时间戳]
end
style C fill:#e8f5e8
style G fill:#fff3e0
style I fill:#e1f5fe
关于 REST API 的关键概念:
- REST 表示“表述性状态转移”
- 使用 标准 HTTP 方法 (GET、POST、PUT、DELETE)来操作数据
- 返回 通常是 JSON 这类可预测格式的数据
- 提供 对不同请求的一致 URL 端点
✅ 我们使用的 CO2 Signal API 提供全球电网的实时碳强度数据。帮助用户了解用电对环境的影响!
💡 理解异步 JavaScript:
async关键字 让你的代码同时处理多个操作。当你向服务器请求数据时,不希望整个扩展卡住——那就像空中交通管制在等待一架飞机响应时停止所有操作。主要好处:
- 保持 扩展在数据加载期间依然响应
- 允许 在网络请求时执行其他代码
- 提升 代码可读性,相较传统回调模式更易懂
- 支持 网络异常时优雅错误处理
这里有个关于 async 的视频:
🎥 点击上图观看 async/await 相关视频。
🔄 教学检查点
异步编程理解:开始写 API 函数之前,请确认你理解:
- ✅ 为什么使用
async/await,而非阻塞整个扩展 - ✅
try/catch块如何优雅处理网络错误 - ✅ 同步与异步操作的区别
- ✅ API 调用失败时会发生什么,如何应对
现实生活的异步连接:
- 点外卖:你不会一直待在厨房旁边,会拿到收据后继续其他活动
- 发邮件:发送时应用不会卡死,你可以继续写邮件
- 网页加载:图片边加载边显示,你已经可以看文本了
API 认证流程:
sequenceDiagram
participant Ext as 扩展
participant API as CO2 信号 API
participant DB as 数据库
Ext->>API: 使用认证令牌请求
API->>API: 验证令牌
API->>DB: 查询碳数据
DB->>API: 返回数据
API->>Ext: JSON 响应
Ext->>Ext: 更新界面
创建获取并展示碳使用数据的函数:
// 现代的 fetch API 方法(不需要外部依赖)
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';
// 待办: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.';
}
}
详细解析:
- 使用 现代的
fetch()API,替代外部库如 Axios,代码更简洁且无依赖 - 实现 使用
response.ok做适当错误检查,及早捕捉 API 失败 - 通过
async/await处理异步操作,使代码流程更易读 - 使用
auth-token头部进行 CO2 Signal API 认证 - 解析 JSON 响应数据并提取碳强度信息
- 更新 多个 UI 元素,格式化显示环保数据
- 提供 当 API 调用失败时易理解的错误提示
示例中展示的现代 JavaScript 技巧:
- 模板字符串 用
${}语法,更清晰字符串格式化 - try/catch 错误处理 让应用更健壮
- async/await 异步模式 优雅调用网络接口
- 对象解构 提取 API 响应中需要的数据
- 链式方法调用 实现多步 DOM 操作
✅ 这个函数展示了多个重要的网页开发概念——与外部服务器通信,认证处理,数据解析,界面更新,以及错误管理。这些是专业开发者日常使用的基础技能。
flowchart TD
A[开始 API 调用] --> B[获取请求]
B --> C{网络成功?}
C -->|否| D[网络错误]
C -->|是| E{响应正常?}
E -->|否| F[API 错误]
E -->|是| G[解析 JSON]
G --> H{数据有效?}
H -->|否| I[数据错误]
H -->|是| J[更新界面]
D --> K[显示错误信息]
F --> K
I --> K
J --> L[隐藏加载]
K --> L
style A fill:#e1f5fe
style J fill:#e8f5e8
style K fill:#ffebee
style L fill:#f3e5f5
🔄 教学检查点
完整系统理解:确认你掌握整个流程:
- ✅ DOM 引用为什么能让 JavaScript 控控界面
- ✅ 本地存储如何实现跨浏览器会话的数据持久化
- ✅ async/await 如何让 API 调用不冻结扩展
- ✅ API 请求失败时会怎样,以及如何处理异常
- ✅ 用户体验为何包含加载状态和错误提示
🎉 你已经完成了: 一个浏览器扩展,它:
- 连接 互联网,获取真实环保数据
- 持久保存 用户设置跨会话
- 优雅处理 错误,避免崩溃
- 提供 流畅且专业的用户体验
通过运行 npm run build 并刷新浏览器扩展测试你的成果。你现在拥有一个功能齐全的碳足迹追踪器。下一课将添加动态图标功能,完善此扩展。
GitHub Copilot Agent 挑战 🚀
使用 Agent 模式完成以下挑战: 描述: 通过添加错误处理改进和用户体验功能来增强浏览器扩展。本挑战将帮助你练习使用现代 JavaScript 模式进行 API 调用、本地存储和 DOM 操作。
提示: 创建一个增强版的 displayCarbonUsage 函数,包含:1) 失败 API 调用的指数退避重试机制,2) 调用 API 前对区域代码的输入验证,3) 带有进度指示器的加载动画,4) 在 localStorage 中缓存 API 响应及过期时间戳(缓存时长30分钟),及 5) 显示先前 API 调用历史数据的功能。同时为所有函数参数和返回类型添加适当的 TypeScript 风格 JSDoc 注释。
在此处了解更多关于 agent 模式的信息。
🚀 挑战
通过探索大量浏览器 API,扩展你对 API 的理解,选择以下浏览器 API 之一并构建一个小示范:
- Geolocation API - 获取用户当前位置信息
- Notification API - 发送桌面通知
- HTML Drag and Drop API - 创建交互式拖放界面
- Web Storage API - 高级本地存储技巧
- Fetch API - XMLHttpRequest 的现代替代方案
研究问题:
- 该 API 解决了哪些现实世界的问题?
- API 如何处理错误和边界情况?
- 使用该 API 有哪些安全考虑?
- 该 API 在不同浏览器中的支持程度如何?
研究后,确定一个 API 具备哪些特性才是开发者友好且可靠的。
课后测验
复习与自学
本课你学习了 LocalStorage 和 API,这两者对专业 web 开发人员都非常有用。你能想到这两者是如何协同工作的吗?思考如何架构一个网站,该网站会存储供 API 使用的项目。
⚡ 你在接下来的 5 分钟内可以做什么
- 打开开发者工具中的 Application 标签,探索任意网站的 localStorage
- 创建一个简单的 HTML 表单,测试浏览器的表单验证
- 尝试在浏览器控制台中存储和检索 localStorage 数据
- 使用 Network 标签检查提交的表单数据
🎯 你在本小时内可以完成的任务
- 完成课后测验并理解表单处理概念
- 构建一个保存用户偏好的浏览器扩展表单
- 实现带有友好错误提示的客户端表单验证
- 练习使用 chrome.storage API 持久化扩展数据
- 创建响应已保存用户设置的用户界面
📅 你一周的扩展开发计划
- 完成一个具备表单功能的完整浏览器扩展
- 掌握不同存储选项:本地、同步和会话存储
- 实现高级表单功能,如自动完成和验证
- 添加用户数据的导入/导出功能
- 跨不同浏览器彻底测试你的扩展
- 优化扩展的用户体验和错误处理机制
🌟 你一个月的 Web API 掌握计划
- 使用多种浏览器存储 API 构建复杂应用
- 学习离线优先开发模式
- 参与开源项目,推动数据持久化
- 掌握隐私保护开发和 GDPR 合规
- 创建用于表单处理和数据管理的可重用库
- 分享关于 Web API 和扩展开发的知识
🎯 你的扩展开发大师时间线
timeline
title API 集成与存储学习进度
section DOM 基础 (15 分钟)
元素引用: querySelector 掌握
: 事件监听器设置
: 状态管理基础
section 本地存储 (20 分钟)
数据持久化: 键值存储
: 会话管理
: 用户偏好处理
: 存储检查工具
section 表单处理 (25 分钟)
用户输入: 表单验证
: 事件阻止
: 数据提取
: UI 状态切换
section API 集成 (35 分钟)
外部通信: HTTP 请求
: 认证模式
: JSON 数据解析
: 响应处理
section 异步编程 (40 分钟)
现代 JavaScript: Promise 处理
: Async/await 模式
: 错误管理
: 非阻塞操作
section 错误处理 (30 分钟)
稳健应用: Try/catch 块
: 用户友好信息
: 优雅降级
: 调试技术
section 高级模式 (1 周)
专业发展: 缓存策略
: 速率限制
: 重试机制
: 性能优化
section 生产技能 (1 个月)
企业功能: 安全最佳实践
: API 版本管理
: 监控与日志
: 可扩展架构
🛠️ 你的全栈开发工具包总结
完成本课后,你已经具备:
- DOM 精通:精确定位和操作元素
- 存储专长:使用 localStorage 进行持久化数据管理
- API 集成:实时数据获取和认证
- 异步编程:使用现代 JavaScript 实现非阻塞操作
- 错误处理:构建稳健且能优雅处理失败的应用
- 用户体验:加载状态、验证及流畅交互
- 现代模式:fetch API、async/await 和 ES6+ 特性
获得的专业技能:你已实现以下模式:
- Web 应用:单页应用并连接外部数据源
- 移动开发:具备离线能力的 API 驱动应用
- 桌面软件:带持久存储的 Electron 应用
- 企业系统:认证、缓存和错误处理
- 现代框架:React/Vue/Angular 数据管理模式
下一个阶段:你已准备好探索高级主题,如缓存策略、实时 WebSocket 连接或复杂状态管理!
任务
免责声明: 本文件由人工智能翻译服务Co-op Translator翻译完成。虽然我们力求准确,但请注意自动翻译可能包含错误或不准确之处。原文文件的母语版本应被视为权威来源。对于重要信息,建议采用专业人工翻译。对于因使用本翻译而产生的任何误解或误释,我们概不负责。

