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
6
const user = {
name: '张三',
age: 20
};

localStorage.setItem('user', user); // ❌ 错误!会变成 "[object Object]"


2.3.2 解决方案:JSON.stringify()和JSON.parse()

保存对象

1
2
3
4
5
6
7
const 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
37
const 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
5
function deleteCookie(name) {
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
}

deleteCookie('username');


对比项 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的工作流程:

  1. 浏览器检测到跨域请求,发送预检请求(OPTIONS)到目标服务器
  2. 服务器检查请求来源,通过响应头告知浏览器允许的源、方法和头部
  3. 浏览器根据服务器的CORS响应头决定是否允许实际请求
  4. 如果允许,浏览器发送实际请求;否则阻止请求并报错

常用的CORS响应头

1
2
3
Access-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
23
async 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
<!DOCTYPE html>
<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 代码解析

核心思路

  1. 使用localStorage.setItem()保存数据
  2. 使用localStorage.getItem()加载数据
  3. 使用JSON.stringify()JSON.parse()处理对象
  4. 每次修改数据后立即保存

关键点

  • 数据以JSON格式保存
  • 页面加载时自动恢复数据
  • 所有操作都会自动保存

7. 本节总结

你已经学会了:

浏览器存储

  • LocalStorage - 永久保存数据
  • SessionStorage - 临时保存数据
  • Cookie - 传统存储方式

跨域问题

  • 什么是跨域
  • 为什么有跨域限制
  • CORS解决方案

实战技能

  • 使用LocalStorage保存用户数据
  • 处理跨域请求
  • 使用AI生成数据持久化应用

浏览器存储和跨域处理是现代Web开发中的基础技术。掌握LocalStorage、SessionStorage和Cookie的使用场景,以及理解CORS机制和解决方案,对于构建实际应用至关重要。建议在实际项目中练习这些技术的应用,注意数据安全和隐私保护。