06.浏览器存储与跨域
1. 为什么需要浏览器存储?
1.1 问题的出现:数据丢失
在之前的待办事项应用中,每次刷新页面后,用户输入的数据都会丢失。这是因为原生JavaScript操作的数据仅存在于内存中,页面刷新会导致内存清空。
核心问题:
Web应用需要将数据持久化存储,以便在页面刷新或重新访问时能够恢复用户数据,提升用户体验。
1.2 浏览器存储的解决方案
浏览器存储 = 在浏览器中保存数据的技术
| 存储方式 | 容量 | 特点 | 适用场景 |
|---|---|---|---|
| Cookie | 4KB | 每次请求都会发送到服务器 | 登录状态、用户偏好 |
| LocalStorage | 5-10MB | 永久保存,除非手动删除 | 用户设置、草稿内容 |
| SessionStorage | 5-10MB | 关闭标签页后自动删除 | 临时数据、会话信息 |
浏览器存储API的引入使得Web应用能够保存用户状态和数据,这是现代单页应用(SPA)和渐进式Web应用(PWA)的基础技术之一。
2. LocalStorage:永久保存数据
2.1 什么是LocalStorage?
2.1.1 LocalStorage的概念
LocalStorage是HTML5提供的Web Storage API之一,允许在浏览器中存储键值对数据。与SessionStorage不同,LocalStorage中存储的数据在浏览器关闭后仍然保留,直到被明确删除或用户清除浏览器数据。
LocalStorage的特点: - 数据以键值对形式存储 - 存储容量通常为5-10MB(因浏览器而异) - 数据仅在当前域名下可访问(同源策略)
2.1.2 LocalStorage的特点
| 特点 | 说明 |
|---|---|
| 永久保存 | 关闭浏览器后数据还在 |
| 同源限制 | 只能访问同域名下的数据 |
| 容量限制 | 通常5-10MB |
| 只能存字符串 | 对象需要转换为JSON |
2.2 LocalStorage的基本操作
2.2.1 保存数据:setItem()
语法:localStorage.setItem('键', '值')
实际例子: 1
2
3
4
5// 保存用户名
localStorage.setItem('username', '张三');
// 保存数字(会自动转换为字符串)
localStorage.setItem('age', '20');
2.2.2 获取数据:getItem()
语法:localStorage.getItem('键')
实际例子: 1
2
3
4
5
6
7// 获取用户名
const username = localStorage.getItem('username');
console.log(username); // '张三'
// 获取不存在的键
const email = localStorage.getItem('email');
console.log(email); // null
2.2.3 删除数据:removeItem()
语法:localStorage.removeItem('键')
实际例子: 1
2
3
4
5
6// 删除用户名
localStorage.removeItem('username');
// 验证是否删除
const username = localStorage.getItem('username');
console.log(username); // null
2.2.4 清空所有数据:clear()
语法:localStorage.clear()
实际例子: 1
2// 清空所有LocalStorage数据
localStorage.clear();
⚠️ 注意:
clear()会删除所有LocalStorage数据,使用时要小心!
2.3 保存对象:JSON转换
2.3.1 问题:LocalStorage只能存字符串
错误示例: 1
2
3
4
5
6const user = {
name: '张三',
age: 20
};
localStorage.setItem('user', user); // ❌ 错误!会变成 "[object Object]"
2.3.2 解决方案:JSON.stringify()和JSON.parse()
保存对象: 1
2
3
4
5
6
7const user = {
name: '张三',
age: 20
};
// 转换为JSON字符串后保存
localStorage.setItem('user', JSON.stringify(user));
读取对象: 1
2
3
4
5
6// 获取JSON字符串
const userStr = localStorage.getItem('user');
// 转换为对象
const user = JSON.parse(userStr);
console.log(user.name); // '张三'
2.4 实战案例:保存用户设置
需求:保存用户的主题设置(明亮/暗黑)
代码: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26// 保存主题
function saveTheme(theme) {
localStorage.setItem('theme', theme);
}
// 加载主题
function loadTheme() {
const theme = localStorage.getItem('theme') || 'light'; // 默认明亮模式
return theme;
}
// 应用主题
function applyTheme(theme) {
document.body.setAttribute('data-theme', theme);
}
// 初始化
const currentTheme = loadTheme();
applyTheme(currentTheme);
// 切换主题按钮
document.getElementById('themeToggle').addEventListener('click', function() {
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
saveTheme(newTheme);
applyTheme(newTheme);
});
3. SessionStorage:临时保存数据
3.1 什么是SessionStorage?
3.1.1 SessionStorage的概念
SessionStorage是Web Storage API的另一种实现,与LocalStorage的主要区别在于数据生命周期。SessionStorage中存储的数据仅在当前浏览器标签页或窗口有效,关闭标签页后数据会被自动清除。
LocalStorage vs SessionStorage:
| 对比项 | LocalStorage | SessionStorage |
|---|---|---|
| 保存时间 | 永久保存 | 关闭标签页后删除 |
| 作用域 | 同域名下所有标签页共享 | 只在当前标签页有效 |
| 使用场景 | 用户设置、偏好 | 临时数据、会话信息 |
3.1.2 SessionStorage的特点
- 临时保存:关闭标签页后自动删除
- 标签页隔离:每个标签页有独立的SessionStorage
- API相同:和LocalStorage的API完全一样
3.2 SessionStorage的基本操作
语法(和LocalStorage完全一样): 1
2
3
4
5
6
7
8
9
10
11// 保存数据
sessionStorage.setItem('key', 'value');
// 获取数据
const value = sessionStorage.getItem('key');
// 删除数据
sessionStorage.removeItem('key');
// 清空所有数据
sessionStorage.clear();
3.3 实战案例:保存表单草稿
需求:用户在表单中输入内容,如果误关闭标签页,下次打开时恢复内容
代码: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37const form = document.getElementById('myForm');
const inputs = form.querySelectorAll('input, textarea');
// 保存表单数据到SessionStorage
function saveFormData() {
const formData = {};
inputs.forEach(input => {
formData[input.name] = input.value;
});
sessionStorage.setItem('formDraft', JSON.stringify(formData));
}
// 恢复表单数据
function loadFormData() {
const formDataStr = sessionStorage.getItem('formDraft');
if (formDataStr) {
const formData = JSON.parse(formDataStr);
inputs.forEach(input => {
if (formData[input.name]) {
input.value = formData[input.name];
}
});
}
}
// 监听输入事件,自动保存
inputs.forEach(input => {
input.addEventListener('input', saveFormData);
});
// 页面加载时恢复数据
loadFormData();
// 表单提交后清除草稿
form.addEventListener('submit', function() {
sessionStorage.removeItem('formDraft');
});
4. Cookie:传统的存储方式
4.1 什么是Cookie?
4.1.1 Cookie的概念
Cookie是HTTP协议的一部分,用于在客户端存储少量数据。与LocalStorage和SessionStorage不同,Cookie会在每次HTTP请求中自动发送到服务器,这使得它特别适合存储需要在客户端和服务器之间共享的数据,如用户认证令牌、会话ID等。
Cookie的特点: - 存储容量限制为4KB - 每次HTTP请求都会自动发送到服务器 - 可以设置过期时间 - 受同源策略限制
4.1.2 Cookie的特点
| 特点 | 说明 |
|---|---|
| 容量小 | 只有4KB |
| 自动发送 | 每次请求都会发送到服务器 |
| 可以设置过期时间 | 可以设置多久后过期 |
| 同源限制 | 只能访问同域名下的Cookie |
4.2 Cookie的基本操作
4.2.1 设置Cookie
语法:document.cookie = '键=值; expires=过期时间; path=路径'
实际例子: 1
2
3
4
5
6
7// 设置Cookie(简单)
document.cookie = 'username=张三';
// 设置Cookie(带过期时间)
const date = new Date();
date.setTime(date.getTime() + 7 * 24 * 60 * 60 * 1000); // 7天后过期
document.cookie = `username=张三; expires=${date.toUTCString()}`;
4.2.2 获取Cookie
语法:document.cookie(返回所有Cookie的字符串)
实际例子: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 获取所有Cookie
const cookies = document.cookie;
console.log(cookies); // "username=张三; theme=dark"
// 需要自己解析(比较麻烦)
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) {
return parts.pop().split(';').shift();
}
return null;
}
const username = getCookie('username');
console.log(username); // '张三'
4.2.3 删除Cookie
方法:设置过期时间为过去的时间
实际例子: 1
2
3
4
5function deleteCookie(name) {
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
}
deleteCookie('username');
4.3 Cookie vs LocalStorage
| 对比项 | Cookie | LocalStorage |
|---|---|---|
| 容量 | 4KB | 5-10MB |
| 自动发送 | ✅ 每次请求都发送 | ❌ 不自动发送 |
| 过期时间 | ✅ 可以设置 | ❌ 永久保存 |
| 使用场景 | 登录状态、用户偏好 | 用户设置、草稿内容 |
| API | 复杂(需要自己解析) | 简单(setItem/getItem) |
在实际开发中,大多数场景下使用LocalStorage更为合适,因为其API更简洁且容量更大。Cookie主要用于需要在客户端和服务器之间自动传输数据的场景,如用户认证令牌、会话ID等。
5. 跨域问题:CORS
5.1 什么是跨域?
5.1.1 跨域的概念
跨域(Cross-Origin)是指浏览器执行一个脚本时,该脚本试图访问与当前页面不同源(Origin)的资源。浏览器的同源策略(Same-Origin Policy)限制了这种跨域访问,以保护用户安全。
同源的定义: - 协议(protocol)相同 - 域名(domain)相同 - 端口(port)相同
只要上述三个要素中有任何一个不同,即视为跨域。
实际例子: 1
2
3
4
5
6
7
8
9
10
11// 同源
https://example.com/page1 → https://example.com/page2 ✅
// 跨域(域名不同)
https://example.com → https://other.com ❌
// 跨域(协议不同)
http://example.com → https://example.com ❌
// 跨域(端口不同)
https://example.com → https://example.com:8080 ❌
5.1.2 为什么有跨域限制?
安全原因:
- 防止恶意网站窃取其他网站的数据
- 保护用户隐私和安全
实际例子: 1
2
3
4
5
6
7// 恶意网站尝试获取你的银行账户信息
// 如果没有跨域限制,恶意网站可以:
fetch('https://bank.com/api/userInfo')
.then(res => res.json())
.then(data => {
// 窃取你的银行信息!
});
跨域限制是浏览器的安全机制,用于防止恶意网站通过脚本访问其他网站的数据,从而保护用户隐私和数据安全。
5.2 什么是CORS?
5.2.1 CORS的定义
CORS = Cross-Origin Resource Sharing(跨域资源共享)
作用:允许服务器明确允许某些跨域请求
CORS机制允许服务器通过HTTP响应头明确指定哪些源可以访问其资源,从而在保证安全的前提下实现跨域资源共享。
5.2.2 CORS的工作原理
CORS的工作流程:
- 浏览器检测到跨域请求,发送预检请求(OPTIONS)到目标服务器
- 服务器检查请求来源,通过响应头告知浏览器允许的源、方法和头部
- 浏览器根据服务器的CORS响应头决定是否允许实际请求
- 如果允许,浏览器发送实际请求;否则阻止请求并报错
常用的CORS响应头: 1
2
3Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type
5.3 如何解决跨域问题?
5.3.1 方法1:服务器设置CORS(推荐)
后端设置(以Node.js为例): 1
2
3
4
5
6
7
8
9
10
11
12
13// 允许所有来源
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
});
// 或只允许特定来源
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'https://example.com');
next();
});
需要注意的是,CORS配置是在服务器端完成的,前端开发者无法直接解决跨域问题。如果遇到跨域问题,需要与后端开发者协作配置CORS响应头。
5.3.2 方法2:使用代理(开发环境)
使用Vite代理(开发环境): 1
2
3
4
5
6
7
8
9
10
11
12// vite.config.js
export default {
server: {
proxy: {
'/api': {
target: 'https://api.example.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
};
前端使用: 1
2
3
4// 开发环境:请求会被代理到 https://api.example.com
fetch('/api/users')
.then(res => res.json())
.then(data => console.log(data));
5.3.3 方法3:JSONP(已过时,不推荐)
JSONP =
利用<script>标签不受跨域限制的特性
实际例子: 1
2
3
4
5
6// 不推荐使用,仅作了解
function jsonp(url, callback) {
const script = document.createElement('script');
script.src = `${url}?callback=${callback}`;
document.body.appendChild(script);
}
⚠️ 注意:JSONP已经过时,现代开发中不推荐使用。
5.4 实战案例:处理跨域请求
需求:从API获取数据,处理可能的跨域问题
代码: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23async function fetchData() {
try {
const response = await fetch('https://api.example.com/data', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error('请求失败');
}
const data = await response.json();
return data;
} catch (error) {
if (error.message.includes('CORS')) {
console.error('跨域错误:请检查服务器CORS设置');
} else {
console.error('请求失败:', error);
}
}
}
6. AI实战:使用AI实现数据持久化
6.1 任务描述
目标:使用AI工具生成一个带数据持久化的待办事项应用
功能要求:
- 使用LocalStorage保存待办事项
- 页面刷新后数据不丢失
- 可以清除所有数据
- 处理跨域问题(如果有API调用)
6.2 第一步:设计Prompt
Prompt示例: 1
2
3
4
5
6
7用HTML、CSS、JavaScript创建一个待办事项应用,要求:
1. 使用LocalStorage保存待办事项数据
2. 页面刷新后数据不丢失
3. 包含添加、删除、完成待办事项功能
4. 包含清除所有数据功能
5. 使用现代JavaScript语法(async/await、数组方法)
6. 代码注释清晰,样式简洁美观
6.3 AI生成的代码(示例)
HTML结构: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>待办事项(数据持久化)</title>
<style>
body {
font-family: '微软雅黑', Arial, sans-serif;
max-width: 600px;
margin: 50px auto;
padding: 20px;
}
.input-group {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
#todoInput {
flex: 1;
padding: 10px;
}
button {
padding: 10px 20px;
cursor: pointer;
}
.todo-item {
display: flex;
justify-content: space-between;
padding: 10px;
margin-bottom: 10px;
background: #f0f0f0;
border-radius: 4px;
}
.todo-item.completed {
opacity: 0.6;
text-decoration: line-through;
}
</style>
</head>
<body>
<h1>📝 待办事项</h1>
<div class="input-group">
<input type="text" id="todoInput" placeholder="输入待办事项...">
<button onclick="addTodo()">添加</button>
<button onclick="clearAll()">清除全部</button>
</div>
<ul id="todoList"></ul>
<script>
// JavaScript代码在下面
</script>
</body>
</html>
JavaScript代码: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74// 从LocalStorage加载待办事项
function loadTodos() {
const todosStr = localStorage.getItem('todos');
return todosStr ? JSON.parse(todosStr) : [];
}
// 保存待办事项到LocalStorage
function saveTodos(todos) {
localStorage.setItem('todos', JSON.stringify(todos));
}
// 获取待办事项列表
let todos = loadTodos();
// 渲染待办事项
function renderTodos() {
const list = document.getElementById('todoList');
list.innerHTML = todos.map((todo, index) => `
<li class="todo-item ${todo.completed ? 'completed' : ''}">
<span onclick="toggleTodo(${index})">${todo.text}</span>
<button onclick="deleteTodo(${index})">删除</button>
</li>
`).join('');
}
// 添加待办事项
function addTodo() {
const input = document.getElementById('todoInput');
const text = input.value.trim();
if (text) {
todos.push({
id: Date.now(),
text,
completed: false
});
saveTodos(todos);
input.value = '';
renderTodos();
}
}
// 删除待办事项
function deleteTodo(index) {
todos.splice(index, 1);
saveTodos(todos);
renderTodos();
}
// 切换完成状态
function toggleTodo(index) {
todos[index].completed = !todos[index].completed;
saveTodos(todos);
renderTodos();
}
// 清除所有待办事项
function clearAll() {
if (confirm('确定要清除所有待办事项吗?')) {
todos = [];
saveTodos(todos);
renderTodos();
}
}
// 按回车键添加
document.getElementById('todoInput').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
addTodo();
}
});
// 初始化
renderTodos();
6.4 代码解析
核心思路:
- 使用
localStorage.setItem()保存数据 - 使用
localStorage.getItem()加载数据 - 使用
JSON.stringify()和JSON.parse()处理对象 - 每次修改数据后立即保存
关键点:
- 数据以JSON格式保存
- 页面加载时自动恢复数据
- 所有操作都会自动保存
7. 本节总结
你已经学会了:
✅ 浏览器存储:
- LocalStorage - 永久保存数据
- SessionStorage - 临时保存数据
- Cookie - 传统存储方式
✅ 跨域问题:
- 什么是跨域
- 为什么有跨域限制
- CORS解决方案
✅ 实战技能:
- 使用LocalStorage保存用户数据
- 处理跨域请求
- 使用AI生成数据持久化应用
浏览器存储和跨域处理是现代Web开发中的基础技术。掌握LocalStorage、SessionStorage和Cookie的使用场景,以及理解CORS机制和解决方案,对于构建实际应用至关重要。建议在实际项目中练习这些技术的应用,注意数据安全和隐私保护。