概念涉及
1. HTTP/HTTPS
- HTTP协议: 理解HTTP的工作原理,包括请求方法(GET、POST、PUT、DELETE等)、状态码(200、404、500等)和请求头、响应头的结构。
- HTTPS安全性: 学习SSL/TLS加密的基础知识,以及如何通过HTTPS保护数据传输的安全性。
2. 网络请求
- AJAX: 学习如何使用AJAX技术进行异步请求,提升用户体验。
- Fetch API: 掌握现代浏览器中Fetch API的使用,以及与XHR(XMLHttpRequest)的对比。
- Promise与async/await: 学习如何处理异步请求的结果,使用Promise及async/await语法。
3. WebSockets
- WebSocket协议: 了解WebSocket的基本概念及其与HTTP的区别,研究如何实现实时通信功能。
4. CORS
- 跨域资源共享(CORS) : 学习浏览器的同源策略和如何使用CORS解决跨域问题。
5. 前端性能优化
- 网络请求优化: 学习HTTP/2和HTTP/3的优势,使用内容分发网络(CDN)提升加载速度。
- 资源压缩与缓存策略: 理解如何使用Gzip、Brotli压缩资源,以及如何利用浏览器缓存加速访问。
6. API设计
- RESTful API: 学习REST架构风格的原则,如何设计和实现RESTful API。
- GraphQL: 了解GraphQL的基本概念及其与REST的不同之处。
7. 安全性
- 安全最佳实践: 学习前端开发中的安全问题,如XSS(跨站脚本攻击)、CSRF(跨站请求伪造)等,以及如何防范。
8. 网络监控与调试
- 浏览器开发者工具: 学习如何使用开发者工具中的网络面板,监控请求和响应,进行性能分析。
9. 网络协议
- TCP/IP基础: 理解传输控制协议(TCP)和互联网协议(IP)的基本概念。
- CDN和负载均衡: 学习内容分发网络的工作原理及其对前端性能的影响。
细聊概念
http相关
HTTP(超文本传输协议)是用于在客户端和服务器之间传输数据的协议。HTTP的工作原理可以分为以下几个步骤:
- 建立连接:客户端浏览器向服务器发起连接请求,一般使用TCP协议(默认端口为80)。
- 发送请求:连接建立后,客户端会发送一个HTTP请求,包括请求方法、URI、HTTP版本及请求头信息。
- 处理请求:服务器接收到请求后,根据请求的内容进行处理,并生成响应。
- 发送响应:服务器将处理结果返回给客户端,发送一个HTTP响应,包括状态码、响应头和响应体。
- 关闭连接:一旦数据传输完成,TCP连接会被关闭(在HTTP/1.1中,默认是保持连接的)。
请求方法
HTTP定义了几种常用请求方法:
- GET:用于请求指定的资源,常用于获取数据。GET请求包含在URL中,易于缓存,但不适合传递敏感信息。
- POST:用于向服务器提交数据,常用于表单提交。POST请求将数据包含在请求体中,适合提交较大数据。
- PUT:用于更新指定资源,通常伴随有请求体,表示要更新的数据。
- DELETE:用于请求服务器删除指定的资源。
- PATCH:用于部分更新资源。
状态码
状态码用于表示HTTP请求的处理结果,常见的状态码包括:
- 200 OK:请求成功,服务器返回请求的资源。
- 201 Created:成功处理请求,并创建了新的资源。
- 204 No Content:请求成功,但没有内容返回(常用于DELETE请求)。
- 301 Moved Permanently:请求的资源已永久移动到新的 URL,客户端应使用新的 URL 访问 (网站更改了某个页面的 URL,旧 URL 会返回 301 状态码,并指向新的 URL)
- 302 Found:请求的资源临时移动到新的 URL,客户端应使用新的 URL 访问,但未来仍可能使用旧 URL (网站临时维护,将用户重定向到另一个页面)
- 304 Not Modified:资源未修改,客户端可以使用缓存的版本。客户端发送了
If-Modified-Since
或If-None-Match
头,服务器检查后发现资源未修改。(浏览器缓存了某个资源,服务器返回 304 状态码,指示浏览器使用缓存) - 400 Bad Request:请求有误,服务器无法理解。
- 401 Unauthorized:请求未授权,需用户进行身份验证。
- 403 Forbidden:服务器理解请求,但拒绝处理。
- 404 Not Found:请求的资源不存在。
- 500 Internal Server Error:服务器内部错误,无法完成请求。
请求头和响应头
-
请求头:包含了客户端的信息和请求的附加信息,例如:
Accept
: 表示客户端能够处理的内容类型。User-Agent
: 提供浏览器类型和版本的信息。Content-Type
: 表示发送数据的格式。
-
响应头:包括服务器的信息和响应的元数据,例如:
Content-Type
: 指示返回内容的类型。Cache-Control
: 控制缓存策略。Set-Cookie
: 用于设置客户端的Cookie。
HTTPS的安全性
HTTPS(安全的超文本传输协议)是HTTP的安全版本,通过SSL/TLS协议实现数据加密,确保数据在传输过程中的安全性。其工作原理如下:
- SSL/TLS握手: 在客户端与服务器建立连接时,首先进行SSL/TLS握手,期间双方交换信息,包括支持的加密算法和生成会话密钥。
- 证书验证: 服务器会向客户端提供其数字证书,客户端通过可信的证书颁发机构(CA)验证该证书的有效性和可信度。
- 建立安全连接: 一旦验证通过,客户端和服务器使用对称加密算法生成一个会话密钥,该密钥用于后续的数据加密和解密。
- 数据传输: 在传输过程中,所有数据均通过SSL/TLS层进行加密,确保数据在传输过程中的机密性与完整性。
- 连接终止: 数据传输完成后,SSL/TLS会话关闭,连接终止。
通过采用HTTPS,可以有效保护用户数据不被窃取或篡改,增强用户与网站之间的信任。
一个完整的请求包含什么
1. 请求行(Request Line)
请求行是HTTP请求的第一行,包含三个部分:
- 请求方法:如GET、POST、PUT、DELETE等,表示客户端希望执行的操作。
- 请求URI:指定要访问的资源路径,如
/index.html
。 - HTTP版本:如
HTTP/1.1
,表示客户端使用的HTTP协议版本。
示例:
GET /index.html HTTP/1.1
2. 请求头(Request Headers)
请求头包含了一系列的键值对,提供了关于请求的附加信息。常见的请求头包括:
- Host:指定服务器的域名或IP地址。
- User-Agent:描述客户端的信息,如浏览器类型和版本。
- Accept:指定客户端能够接收的MIME类型。
- Content-Type:指定请求体的媒体类型(如
application/json
)。 - Authorization:包含用于身份验证的凭证。
示例:
Host: www.example.com
User-Agent: Mozilla/5.0
Accept: text/html,application/xhtml+xml
Content-Type: application/json
Authorization: Bearer abc123
3. 空行(Empty Line)
请求头和请求体之间必须有一个空行,用于分隔头部和主体。
示例:
(空行)
4. 请求体(Request Body)
请求体包含客户端发送给服务器的数据,通常用于POST、PUT等请求方法。请求体的格式由Content-Type
头指定,常见的格式包括:
- 表单数据:如
application/x-www-form-urlencoded
。 - JSON数据:如
application/json
。 - 文件上传:如
multipart/form-data
。
示例(JSON格式):
{
"username": "user123",
"password": "pass123"
}
请求行和请求体的关系
-
分工明确:
- 请求行定义了“做什么”(请求方法)和“对谁做”(请求URI)。
- 请求体提供了“怎么做”的具体数据(如果需要)。
-
依赖关系:
- 请求行是HTTP请求的核心部分,所有请求都必须包含请求行。
- 请求体是可选的,只有在需要传递数据时才存在。
-
示例场景:
-
GET请求:通常只有请求行,没有请求体。
GET /index.html HTTP/1.1
POST请求:包含请求行和请求体。
POST /login HTTP/1.1 Content-Type: application/json Content-Length: 45 { "username": "user123", "password": "pass123" }
-
5. 常见的conten-type和请求体(Request Body)关系
1. application/x-www-form-urlencoded
(常见)
-
用途:用于发送表单数据,通常用于HTML表单提交。
-
请求体格式:数据以键值对的形式编码,键和值之间用
=
连接,多个键值对之间用&
分隔。 -
示例:
Content-Type: application/x-www-form-urlencoded username=user123&password=pass123
-
特点:
- 数据会被URL编码(如空格变为
+
,特殊字符变为%XX
)。 - 适合发送简单的文本数据。
- 数据会被URL编码(如空格变为
2. application/json
(目前基本上是这个)
-
用途:用于发送JSON格式的数据,常用于RESTful API。
-
请求体格式:数据以JSON格式编码,包含键值对或嵌套结构。
-
示例:
Content-Type: application/json { "username": "user123", "password": "pass123" }
-
特点:
- 支持复杂的数据结构(如数组、嵌套对象)。
- 适合现代Web应用和API交互。
3. multipart/form-data
(常见 - 表单提交/文件上传)
-
用途:用于上传文件或同时发送文本和文件数据。
-
请求体格式:数据被分成多个部分(parts),每个部分包含一个文件或文本字段,用边界符(boundary)分隔。
-
示例:
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW ------WebKitFormBoundary7MA4YWxkTrZu0gW Content-Disposition: form-data; name="username" user123 ------WebKitFormBoundary7MA4YWxkTrZu0gW Content-Disposition: form-data; name="file"; filename="example.txt" Content-Type: text/plain (文件内容) ------WebKitFormBoundary7MA4YWxkTrZu0gW--
-
特点:
- 适合上传文件或混合数据。
- 请求体较大,因为包含边界符和元数据。
4. text/plain
(不常见)
-
用途:用于发送纯文本数据。
-
请求体格式:数据为普通文本,没有特定格式。
-
示例:
Content-Type: text/plain This is a plain text message.
-
特点:
- 适合发送简单的文本信息。
- 不支持结构化数据。
5. application/xml
(不常见)
-
用途:用于发送XML格式的数据。
-
请求体格式:数据以XML格式编码。
-
示例:
Content-Type: application/xml <user> <username>user123</username> <password>pass123</password> </user>
-
特点:
- 适合需要严格结构化的数据。
- 比JSON更冗长,但支持更复杂的数据验证。
6. application/octet-stream
-
用途:用于发送二进制数据,通常用于文件上传。
-
请求体格式:数据为原始二进制格式。
-
示例:
Content-Type: application/octet-stream (二进制数据)
-
特点:
- 适合发送未知或非文本类型的数据。
- 服务器需要根据上下文解析数据。
vue结合axios是如何处理这些Content-Type的
1. application/x-www-form-urlencoded
用于发送表单数据,Axios默认会将JavaScript对象转换为x-www-form-urlencoded
格式。
示例:
axios.post('/login', {
username: 'user123',
password: 'pass123'
}, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error(error);
});
注意:
如果需要手动将对象转换为x-www-form-urlencoded
格式,可以使用qs
库:
import qs from 'qs';
axios.post('/login', qs.stringify({
username: 'user123',
password: 'pass123'
}), {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
2. application/json
用于发送JSON格式的数据,Axios默认使用application/json
作为Content-Type
。
示例:
axios.post('/api/data', {
username: 'user123',
password: 'pass123'
}, {
headers: {
'Content-Type': 'application/json'
}
})
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error(error);
});
注意:
Axios会自动将JavaScript对象序列化为JSON字符串,因此不需要手动处理。
3. multipart/form-data
用于上传文件或混合数据,通常需要借助FormData
对象。
示例:
const formData = new FormData();
formData.append('username', 'user123');
formData.append('file', fileInput.files[0]); // 假设fileInput是一个文件输入框
axios.post('/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error(error);
});
注意:
multipart/form-data
的请求体由FormData
对象生成,Axios会自动设置正确的Content-Type
和边界符(boundary)。
4. 处理二进制文件下载
发送请求
使用Axios发送请求时,需要指定responseType
为blob
,以便正确处理二进制数据。
示例:
axios.get('/download/file', {
responseType: 'blob' // 指定响应类型为Blob
})
.then(response => {
// 处理文件下载
handleFileDownload(response.data, 'filename.ext');
})
.catch(error => {
console.error('文件下载失败', error);
});
处理响应
从服务器获取的二进制数据是一个Blob
对象。我们需要将其转换为文件并保存到本地。
示例:
function handleFileDownload(blob, filename) {
// 创建一个指向Blob对象的URL
const url = window.URL.createObjectURL(blob);
// 创建一个<a>标签
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', filename); // 设置下载文件名
// 模拟点击下载
document.body.appendChild(link);
link.click();
// 移除<a>标签
document.body.removeChild(link);
// 释放Blob对象的URL
window.URL.revokeObjectURL(url);
}
注意事项
-
文件名:如果服务器返回的文件名在响应头中(如
Content-Disposition
),可以从响应头中提取文件名:const contentDisposition = response.headers['content-disposition']; const filename = contentDisposition.split('filename=')[1].trim();
-
文件类型:如果需要根据文件类型设置
Blob
的MIME类型,可以使用Blob
构造函数:const blob = new Blob([response.data], { type: 'application/pdf' });
-
大文件下载:对于大文件下载,可以考虑使用分块下载或流式下载,以提高性能和用户体验。
处理二进制文件下载的关键步骤包括:
- 设置
responseType: 'blob'
:确保Axios正确处理二进制数据。 - 创建Blob URL:使用
window.URL.createObjectURL
将Blob对象转换为可下载的URL。 - 模拟点击下载:通过动态创建
<a>
标签并设置download
属性,触发文件下载。 - 清理资源:下载完成后,释放Blob URL以避免内存泄漏。
前端输入uir到页面呈现经历了什么
1. 输入URL
用户在浏览器地址栏中输入URL(如https://www.example.com
),并按下回车键。
2. DNS解析
浏览器需要将域名(如www.example.com
)解析为对应的IP地址,以便与服务器建立连接。具体步骤如下:
- 浏览器检查本地缓存(如浏览器缓存、操作系统缓存)是否有该域名的IP地址。
- 如果缓存中没有,浏览器会向本地DNS服务器发起请求。
- 如果本地DNS服务器没有缓存,它会向根DNS服务器、顶级域名服务器(如
.com
)和权威DNS服务器逐级查询,最终获取IP地址。
3. 建立TCP连接
浏览器通过IP地址与服务器建立TCP连接。这个过程通常包括以下步骤:
-
三次握手:
- 浏览器发送SYN包到服务器。
- 服务器回复SYN-ACK包。
- 浏览器发送ACK包,连接建立。
-
如果使用HTTPS,还会进行TLS/SSL握手,建立加密连接。
4. 发送HTTP请求
浏览器向服务器发送HTTP请求,请求行中包含请求方法(如GET)、URI(如/index.html
)和HTTP版本(如HTTP/1.1
)。请求头中可能包含Host
、User-Agent
、Accept
等信息。
示例:
GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0
Accept: text/html
5. 服务器处理请求
服务器接收到请求后,根据请求的URI和请求方法进行处理:
- 如果是静态资源(如HTML、CSS、JS文件),服务器直接返回文件内容。
- 如果是动态资源(如PHP、Node.js),服务器会执行相应的后端逻辑,生成HTML内容。
- 服务器将处理结果封装在HTTP响应中,包括状态码(如200)、响应头(如
Content-Type
)和响应体(如HTML内容)。
示例:
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1234
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
</head>
<body>
<h1>Hello, World!</h1>
</body>
</html>
6. 浏览器接收响应
浏览器接收到服务器的HTTP响应后,开始解析响应内容:
- 解析HTML:浏览器将HTML文档解析为DOM(文档对象模型)树。
- 加载外部资源:如果HTML中包含外部资源(如CSS、JS、图片),浏览器会发起额外的HTTP请求来获取这些资源。
- 解析CSS:浏览器将CSS解析为CSSOM(CSS对象模型)树。
- 执行JavaScript:浏览器执行JavaScript代码,可能会修改DOM或CSSOM。
7. 构建渲染树
浏览器将DOM树和CSSOM树合并为渲染树(Render Tree),渲染树只包含需要显示的节点及其样式信息。
8. 布局(Layout)
浏览器根据渲染树计算每个节点的几何信息(如位置、大小),这一过程称为布局或重排(Reflow)。
9. 绘制(Painting)
浏览器将渲染树的每个节点绘制到屏幕上,这一过程称为绘制或重绘(Repaint)。
10. 页面呈现
最终,页面内容显示在浏览器窗口中,用户可以与之交互。
WebSockets
WebSockets 是一种在单个 TCP 连接上进行全双工通信的协议,适用于需要实时数据传输的场景(如聊天应用、实时通知、在线游戏等)
1. WebSockets 的基本概念
- 全双工通信:客户端和服务器可以同时发送和接收数据。
- 持久连接:建立连接后,连接会一直保持,直到客户端或服务器主动关闭。
- 低延迟:相比 HTTP 轮询,WebSockets 的延迟更低,适合实时应用。
2. WebSockets 的工作流程
- 握手:客户端通过 HTTP 请求发起 WebSocket 连接,服务器返回确认信息。
- 通信:连接建立后,客户端和服务器可以通过
send
方法发送数据,通过事件监听接收数据。 - 关闭:客户端或服务器可以主动关闭连接。
3. 在Vue 3中使用WebSocket的最佳实践
建立连接: 在Vue 3中,可以在setup
函数中创建WebSocket连接。使用onMounted
钩子来建立连接,确保组件加载后开始通信。
<template>
<div>
<h1>WebSocket Demo</h1>
<div>Message: {{ message }}</div>
</div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue';
const message = ref('');
let socket;
onMounted(() => {
socket = new WebSocket('ws://your-websocket-url');
socket.onopen = () => {
console.log('WebSocket connection opened');
};
socket.onmessage = (event) => {
message.value = event.data;
console.log('Message from server: ', event.data);
};
socket.onclose = () => {
console.log('WebSocket connection closed');
};
socket.onerror = (error) => {
console.error('WebSocket error: ', error);
};
});
onBeforeUnmount(() => {
if (socket) {
socket.close();
}
});
</script>
- 处理消息: 在
onmessage
中处理接收到的消息,根据需要更新状态或执行操作。 - 优雅关闭: 在
onBeforeUnmount
中关闭WebSocket连接,以清理资源。确保组件卸载时不会有未处理的连接。 - 错误处理: 在连接的
onerror
和onclose
事件中处理错误和连接关闭逻辑,可以进行重连机制等。 - 状态管理: 可以结合Vuex或Pinia等状态管理库来管理应用中的WebSocket状态,例如连接状态、接收到的消息等。
- 优化性能: 根据实际情况,可以考虑使用节流(throttle)或防抖(debounce)来减少频繁的数据发送。 二次封装WebSocket是一种在项目中更方便地使用WebSocket的方式,通过封装来简化常用操作,提高代码复用性和可维护性。以下是如何在Vue 3中实现WebSocket的二次封装的示例。
创建WebSocket封装类
首先,我们可以创建一个WebSocketService
类,用于管理WebSocket的连接、发送和接收消息等。
// WebSocketService.js
class WebSocketService {
constructor(url) {
this.url = url;
this.socket = null;
this.listeners = {};
}
// 建立连接
connect() {
this.socket = new WebSocket(this.url);
this.socket.onopen = () => {
console.log('WebSocket connection opened');
};
this.socket.onmessage = (event) => {
this.onMessage(event.data);
};
this.socket.onclose = () => {
console.log('WebSocket connection closed');
this.reconnect();
};
this.socket.onerror = (error) => {
console.error('WebSocket error: ', error);
};
}
// 处理接收到的消息
onMessage(data) {
const parsedData = JSON.parse(data);
if (this.listeners[parsedData.type]) {
this.listeners[parsedData.type].forEach(callback => callback(parsedData.payload));
}
}
// 发送消息
send(message) {
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
this.socket.send(JSON.stringify(message));
}
}
// 注册消息监听
on(type, callback) {
if (!this.listeners[type]) {
this.listeners[type] = [];
}
this.listeners[type].push(callback);
}
// 断线重连
reconnect() {
setTimeout(() => {
console.log('Reconnecting...');
this.connect();
}, 5000); // 5秒后重连
}
// 关闭连接
disconnect() {
if (this.socket) {
this.socket.close();
}
}
}
export default WebSocketService;
2. 在Vue组件中使用封装类
接下来,我们可以在Vue组件中使用这个封装好的WebSocket服务。
<template>
<div>
<h1>WebSocket Demo</h1>
<div>Message: {{ message }}</div>
</div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue';
import WebSocketService from './WebSocketService';
const message = ref('');
const wsService = new WebSocketService('ws://your-websocket-url');
onMounted(() => {
wsService.connect();
wsService.on('messageType', (data) => {
message.value = data;
});
});
onBeforeUnmount(() => {
wsService.disconnect();
});
</script>
3. 优化和扩展
- 消息类型:可以定义不同类型的消息,通过
on
方法注册不同类型的回调函数,以支持不同的消息处理。 - 心跳机制:可以实现心跳检测,确保WebSocket连接的活跃性。
- 错误处理和重连策略:可以根据业务需求优化重连策略以及处理连接错误的方式。
- 事件总线:可以考虑在更复杂的场景下,把WebSocket服务和事件总线结合使用,以支持更灵活的组件间通信。
跨域资源共享(CORS)
跨域资源共享(CORS,Cross-Origin Resource Sharing)是一种机制
,允许网页从不同的域名(即跨域)请求资源。通常情况下,浏览器出于安全考虑,会限制跨域请求,以防止恶意网站访问其他网站的资源。CORS 通过在服务器端设置特定的 HTTP 头来允许或拒绝跨域请求。
CORS 的工作原理
- 简单请求:对于某些简单的请求(如 GET 或 POST 请求,且不包含自定义头信息),浏览器会直接发送请求,并在请求头中包含
Origin
字段,表示请求的来源。服务器可以根据Origin
字段决定是否允许该请求,并在响应头中包含Access-Control-Allow-Origin
字段来指示允许的源。 - 预检请求:对于复杂的请求(如 PUT、DELETE 请求,或包含自定义头信息的请求),浏览器会先发送一个 OPTIONS 请求(称为预检请求),询问服务器是否允许该跨域请求。服务器在响应中通过
Access-Control-Allow-Origin
、Access-Control-Allow-Methods
等字段来指示允许的源、方法等。如果预检请求通过,浏览器才会发送实际的请求。
CORS 相关的 HTTP 头
Origin
:由浏览器自动添加,表示请求的来源。Access-Control-Allow-Origin
:服务器返回的头,表示允许的源。可以是具体的域名,也可以是*
表示允许所有源。Access-Control-Allow-Methods
:服务器返回的头,表示允许的 HTTP 方法(如 GET、POST 等)。Access-Control-Allow-Headers
:服务器返回的头,表示允许的请求头。Access-Control-Allow-Credentials
:服务器返回的头,表示是否允许发送凭据(如 cookies)。
示例
假设你有一个前端应用运行在 https://example.com
,并且需要从 https://api.example.com
获取数据。你可以在服务器端设置以下响应头来允许跨域请求:
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Credentials: true
注意事项
- 如果请求需要携带凭据(如 cookies),服务器必须设置
Access-Control-Allow-Credentials: true
,并且Access-Control-Allow-Origin
不能为*
,必须指定具体的源。 - 如果服务器没有正确配置 CORS,浏览器会阻止跨域请求,并抛出错误。
CORS 涉及前端和后端交互
CORS(跨域资源共享)主要是在后端进行配置和处理的机制。虽然浏览器会在前端发起跨域请求时自动添加必要的头信息,但真正的跨域请求的允许与否取决于后端服务器的设置。
前端的角色
- 发起请求:前端应用(如浏览器中的 JavaScript)发起跨域请求时,会在请求头中添加
Origin
字段,表示请求的来源。 - 处理响应:前端会根据后端响应的 CORS 头部(例如
Access-Control-Allow-Origin
)来判断是否可以处理响应内容。
后端的角色
-
配置响应头:后端服务器需要根据需求配置相应的 CORS 头部,以允许特定的源进行跨域请求。例如:
Access-Control-Allow-Origin
:指示允许的来源。Access-Control-Allow-Methods
:指示允许的 HTTP 方法。Access-Control-Allow-Headers
:指示允许的请求头。
以vue3项目为例 CORS应用
假设场景
假设你的前端 Vue 3 应用运行在 http://localhost:8080
,而你的 API 服务器运行在 http://localhost:5000
。
后端设置(以 Node.js Express 为例)
在后端,你需要配置 CORS 以允许来自前端的请求。下面是一个基本的 Express 服务器设置示例:
const express = require('express');
const cors = require('cors');
const app = express();
// 使用 CORS 中间件
app.use(cors({
origin: 'http://localhost:8080', // 允许来自这个源的请求
methods: 'GET,POST,PUT,DELETE', // 允许的 HTTP 方法
credentials: true // 允许发送凭据(如 cookies)
}));
app.get('/api/data', (req, res) => {
res.json({ message: 'Hello from API' });
});
app.listen(5000, () => {
console.log('Server is running on http://localhost:5000');
});
Vue 3 前端请求
在 Vue 3 项目中,你可以使用 Axios 或 Fetch API 来发起请求。以下是一个使用 Axios 的示例。
首先,确保安装了 Axios:
npm install axios
然后,你可以在 Vue 组件中发起请求:
<template>
<div>
<h1>{{ message }}</h1>
<button @click="fetchData">获取数据</button>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
message: ''
};
},
methods: {
async fetchData() {
try {
const response = await axios.get('http://localhost:5000/api/data', {
withCredentials: true // 如果需要发送凭据
});
this.message = response.data.message;
} catch (error) {
console.error('请求出错:', error);
}
}
}
};
</script>
如果你选择使用 Fetch API,代码示例如下:
<template>
<div>
<button @click="fetchData">获取数据</button>
<h1>{{ message }}</h1>
</div>
</template>
<script>
export default {
data() {
return {
message: ''
};
},
methods: {
async fetchData() {
try {
const response = await fetch('http://localhost:5000/api/data', {
method: 'GET',
credentials: 'include' // 如果需要发送凭据
});
const data = await response.json();
this.message = data.message;
} catch (error) {
console.error('请求出错:', error);
}
}
}
};
</script>
axios
的 withCredentials: true
和 fetch
的 credentials: 'include'
都用于控制跨域请求时是否发送凭据(如 cookies、HTTP 认证信息等)。
1. 作用
-
withCredentials: true
(Axios) 和credentials: 'include'
(Fetch) 的作用是:- 在跨域请求中,允许浏览器将当前域的 cookies 和其他凭据(如 HTTP 认证信息)附加到请求中。
- 如果后端服务器支持 CORS 并配置了
Access-Control-Allow-Credentials: true
,这些凭据会被发送到目标服务器。
-
默认行为:
- 在跨域请求中,默认情况下,浏览器不会发送 cookies 或其他凭据。
- 如果启用了
withCredentials
或credentials: 'include'
,浏览器会将这些凭据附加到请求中。
2. 使用场景
-
用户认证:
- 当用户登录后,服务器会返回一个包含认证信息的 cookie(如
sessionId
)。 - 在后续的跨域请求中,需要将这个 cookie 发送到服务器以验证用户身份。
- 当用户登录后,服务器会返回一个包含认证信息的 cookie(如
-
跨域资源共享(CORS) :
- 如果前端应用和后端 API 部署在不同的域名下(如前端在
https://example.com
,后端在https://api.example.com
),并且需要共享凭据,则需要启用此选项。
- 如果前端应用和后端 API 部署在不同的域名下(如前端在
-
需要携带凭据的请求:
- 某些 API 要求客户端在请求中携带凭据(如 cookies 或 HTTP 认证头),否则会返回 401 或 403 错误。
3. 后端配置
为了支持跨域请求携带凭据,后端服务器需要配置以下内容:
4. CORS 配置
- 设置
Access-Control-Allow-Origin
为具体的域名(不能为*
)。 - 设置
Access-Control-Allow-Credentials: true
。
示例(Node.js + Express) :
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors({
origin: 'https://example.com', // 允许的源
credentials: true // 允许发送凭据
}));
app.get('/api/data', (req, res) => {
res.json({ message: 'Hello from API' });
});
app.listen(5000, () => {
console.log('Server is running on http://localhost:5000');
});
4.1 Cookie 配置
- 如果使用 cookies,确保 cookie 的
SameSite
属性设置为None
,并且Secure
属性为true
(仅在 HTTPS 下有效)。
示例(设置 cookie) :
res.cookie('sessionId', '12345', {
httpOnly: true,
secure: true, // 仅在 HTTPS 下传输
sameSite: 'None' // 允许跨域发送
});
5. 注意事项
-
Access-Control-Allow-Origin
不能为*
:- 如果启用了
withCredentials
或credentials: 'include'
,后端必须明确指定允许的源(如https://example.com
),而不能使用通配符*
。
- 如果启用了
-
HTTPS 要求:
- 如果启用了
withCredentials
或credentials: 'include'
,建议使用 HTTPS 协议,以确保凭据的安全性。
- 如果启用了
-
SameSite Cookie:
- 如果后端设置了
SameSite: Strict
或SameSite: Lax
,跨域请求可能无法发送 cookies。需要将SameSite
设置为None
并启用Secure
。
- 如果后端设置了
CORS 调试
如果你尝试从 Vue 应用中请求后端 API,但没有正确配置 CORS,可能会遇到跨域错误。通常可以在浏览器的控制台中查看详细的错误信息。
生产环境
在生产环境中,你需要确保后端服务器的 CORS 设置是安全的,不要随意允许所有来源的请求,以防止潜在的安全风险。
注意事项
- 无须手动设置 CORS 头:CORS 的控制
主要是由后端通过响应头来实现
。前端不需要手动设置Access-Control-Allow-Origin
等头部,这些都是由后端服务器负责的。 - 错误处理:在应对可能的跨域错误时,确保在前端可以捕获到错误并进行适当的处理,向用户提供反馈。
代理配置(开发中可选)
在 Vue 项目中,尤其是使用 Vue CLI 时,可以通过 vue.config.js 文件配置代理来避免跨域问题,开发时比较方便:
// vue.config.js 如果是 vite 在 vite.config.js 配置
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:5000',
changeOrigin: true, // 是否更改源
pathRewrite: { '^/api': '' }, // 重写路径
},
},
},
};
然后你可以通过相对路径 /api/data
发起请求,Webpack 的开发服务器会自动代理到 http://localhost:5000/api/data
。
前端网络请求的方式
XMLHttpRequest
这是原生 JavaScript 提供的一个接口,可以用于发起 HTTP 请求。虽然使用起来相对复杂,但在一些旧版浏览器中仍然有效。
// 原生 XMLHttpRequest
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data', true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(JSON.parse(xhr.responseText));
}
};
xhr.send();
Fetch API
Fetch API 是现代浏览器(注意有兼容性,但是目前的浏览器基本上都是可以的)提供的一个更简单、基于 Promise 的接口,易于使用和理解。
fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) {
throw new Error('网络响应不正常');
}
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('获取数据时出错:', error));
使用 Axios 进行请求
import axios from 'axios';
axios.get('https://api.example.com/data')
.then(response => {
console.log(response.data);
})
.catch(error => {
console.error('请求出错:', error);
});
WebSocket(实时通信)
对于需要实时数据更新的应用(例如聊天应用),可以使用 WebSocket API。WebSocket 提供了一种在单个连接上进行全双工、双向通信的方式,非常适合实时应用。
const socket = new WebSocket('wss://api.example.com/socket');
socket.onopen = function () {
console.log('连接已建立');
socket.send('Hello Server!');
};
socket.onmessage = function (event) {
console.log('收到消息:', event.data);
};
socket.onclose = function () {
console.log('连接已关闭');
};
socket.onerror = function (error) {
console.error('WebSocket 错误:', error);
};
对于现代项目,通常推荐使用 Fetch API 或 Axios,因为它们更易于使用,并提供更好的功能和支持。而对于需要实时通信的应用,则可以考虑使用 WebSocket。
网络监控与调试
浏览器开发者工具
大多数现代浏览器都提供强大的开发者工具,用于网络监控与调试。
1.1 打开开发者工具
通常可以通过按 F12 键或者右键点击页面并选择“检查”来打开。
1.2 网络面板
在开发者工具中,有一个“网络(Network)”面板,可以用来监控 HTTP 请求和响应。使用方法如下:
- 查看请求:在网络面板中,可以看到所有的网络请求,包括请求方法(GET、POST 等)、请求 URL、状态码、响应时间等。
- 查看响应:点击具体的请求,可以查看该请求的详细信息,包括请求头、响应头、响应体等。
- 过滤请求:可以根据请求类型(XHR、JS、CSS 等)过滤请求,方便查找特定类别的请求。
- 分析性能:通过查看各请求的时间,可以判断延迟、下载速度等,从而优化性能。
使用 Fiddler 或 Postman
- Fiddler:这是一个强大的网络调试代理工具,可以捕获和分析 HTTP 和 HTTPS 请求,适用于诊断和调试网络问题。
- Postman:除了用于API测试外,Postman 也可以生成请求并查看返回的响应。这对于调试 API 非常方便。
监控 API 性能
有一些在线服务(如 Google Analytics、New Relic、Sentry 等)可以监控你的 API 性能,帮助识别慢请求或错误。
使用网络监控工具
一些专门的网络监控工具可以帮助实时监测应用的网络情况,例如:
- Wireshark:一个功能强大的网络分析工具,可以捕获和分析网络流量,适合深入的网络调试。
- Charles Proxy:一个 HTTP 代理/监视器,可以查看请求和响应的详细信息,支持 SSL 代理。
性能分析
使用 Lighthouse 等工具,可以分析网站的性能,包括网络请求的优化情况,给出改善建议。
监控网页质量的代码
1. 使用 Performance API
浏览器提供的 Performance API 可以获取页面的加载性能数据,例如页面加载时间、资源加载时间等。
// 获取性能数据
function logPerformance() {
const performanceData = performance.timing;
const pageLoadTime = performanceData.loadEventEnd - performanceData.navigationStart;
console.log('页面加载时间:', pageLoadTime, '毫秒');
const domContentLoadedTime = performanceData.domContentLoadedEventEnd - performanceData.navigationStart;
console.log('DOM 内容加载时间:', domContentLoadedTime, '毫秒');
const firstPaintTime = performance.getEntriesByName("first-paint")[0]?.startTime || 0;
console.log('首次绘制时间:', firstPaintTime, '毫秒');
}
// 在页面加载完成后调用
window.addEventListener('load', logPerformance);
2. 使用 Lighthouse API
你可以使用 Lighthouse 的 Node.js API 来监控网页质量。Lighthouse 提供了性能、可访问性、最佳实践等方面的分析。
首先,确保你安装了 Lighthouse:
npm install -g lighthouse
然后,你可以使用以下 Node.js 脚本来运行 Lighthouse:
const lighthouse = require('lighthouse');
const chromeLauncher = require('chrome-launcher');
async function runLighthouse(url) {
const chrome = await chromeLauncher.launch({ chromeFlags: ['--headless'] });
const options = { logLevel: 'info', output: 'html', port: chrome.port };
const runnerResult = await lighthouse(url, options);
// 输出报告
const reportHtml = runnerResult.report;
const reportPath = 'lighthouse-report.html';
require('fs').writeFileSync(reportPath, reportHtml);
console.log(`报告已生成: ${reportPath}`);
await chrome.kill();
}
// 调用函数
runLighthouse('https://example.com');
3.使用 Web Vitals 监控用户体验
Web Vitals 是一组关键性能指标,可以帮助你评估用户体验。以下代码使用 Web Vitals 库来监控关键指标并输出到控制台。
首先,你需要安装 web-vitals
:
npm install web-vitals
然后,可以使用以下代码来监控和记录不同的 Web Vitals 指标:
import { getFID, getLCP, getCLS } from 'web-vitals';
// 监控用户体验指标
getFID((metric) => {
console.log('首次输入延迟(FID):', metric.value);
});
getLCP((metric) => {
console.log('最大内容绘制(LCP):', metric.value);
});
getCLS((metric) => {
console.log('累积布局偏移(CLS):', metric.value);
});
4. 结合 Google Analytics 或其他服务
你可以使用 Google Analytics 或其他分析服务来监控页面的质量和性能。例如,将 Web Vitals 指标发送到 Google Analytics:
import { getCLS, getFID, getLCP } from 'web-vitals';
function sendToAnalytics(metric) {
const { name, delta } = metric;
const payload = {
v: '1',
tid: 'UA-XXXX-Y', // 替换为你的 Google Analytics ID
cid: '555',
t: 'event',
ec: 'Web Vitals',
ea: name,
el: 'metric',
ev: Math.round(delta), // 将值发送为整数
};
const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://www.google-analytics.com/collect', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(new URLSearchParams(payload).toString());
}
// 发送 Web Vitals 指标到 Google Analytics
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getLCP(sendToAnalytics);
前端网络安全性如何保障
1. 使用 HTTPS
- 加密:通过 HTTPS(安全超文本传输协议)来加密数据传输,确保数据在传输过程中不会被窃取或篡改。
- 证书:确保使用有效的 SSL/TLS 证书,避免中间人攻击。
2. 输入验证与转义
- 客户端验证:在用户输入提交前,进行基本的格式检查和验证(如邮箱格式、密码强度等)。
- 转义输出:对用户输入进行转义,以防止 XSS(跨站脚本攻击)。使用 HTML 实体编码来处理用户生成的内容。
3. 使用内容安全策略 (CSP)
-
CSP:内容安全策略是一个浏览器安全特性,可以防止 XSS 攻击。通过设置 HTTP 头部
Content-Security-Policy
来限制允许加载的资源(如脚本、图像等)。Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com;
4. 预防 CSRF(跨站请求伪造)
- 使用 CSRF 令牌:为敏感操作生成随机的 CSRF 令牌,并在请求中进行验证,确保请求的合法性。
- SameSite Cookie:使用 SameSite 属性来限制 cookie 的发送,预防 CSRF 攻击。
5. 设置 HttpOnly 和 Secure Cookie
- HttpOnly:设置 cookie 的 HttpOnly 属性,防止 JavaScript 访问 cookie,降低 XSS 风险。
- Secure:设置 cookie 的 Secure 属性,确保 cookie 只在 HTTPS 连接中传输。
6. 采用安全的第三方库和 API
- 依赖管理:定期检查和更新依赖项,避免使用存在已知安全漏洞的库。
- 最小权限策略:对外部 API 的访问权限进行限制,只暴露必要的接口。
7. 使用强密码和多因素认证
- 强密码政策:要求用户使用强密码,包括字母、数字和特殊字符的组合。
- 多因素认证(MFA) :实施 MFA,增加用户账号的安全层级。
8. 监测与日志记录
- 错误监控:使用监控工具(如 Sentry 或 LogRocket)记录和分析错误,以及时识别并修复安全漏洞。
- 日志记录:合理记录用户活动和系统日志,以帮助发现异常行为并追踪潜在攻击。
9. 交互限制与用户授权
- 限流:对重要操作(如登录、注册等)进行限流,防止暴力破解攻击。
- 用户权限管理:实施基于角色的访问控制,确保用户只能访问自己有权限的内容。
10. 定期安全审计
- 代码审计:定期进行代码审计,发现潜在的安全风险并进行修复。
- 渗透测试:进行渗透测试,模拟攻击以检查系统的脆弱性。
TCP/IP 基础
什么是 TCP/IP
TCP/IP(Transmission Control Protocol/Internet Protocol)是互联网的基础协议族,负责数据在网络中的传输和路由。它由两个核心协议组成:
- IP(Internet Protocol) :负责将数据包从源地址发送到目标地址。
- TCP(Transmission Control Protocol) :在 IP 的基础上,提供可靠的、面向连接的数据传输服务。
TCP 的特点
- 可靠性:TCP 通过确认机制、重传机制和校验和确保数据完整无误地到达目的地。
- 面向连接:在数据传输前,TCP 会通过“三次握手”建立连接。
- 有序性:TCP 保证数据包按发送顺序到达。
对前端的影响
-
性能优化:
- 减少 TCP 连接的开销:通过 HTTP/2 或 HTTP/3 复用连接,减少 TCP 握手次数。
- 减少数据包数量:合并资源(如 CSS、JavaScript)以减少 TCP 数据包的数量。
-
调试网络问题:
- 了解 TCP 的工作原理有助于分析网络延迟、丢包等问题。
CDN(内容分发网络)
什么是 CDN?
CDN(Content Delivery Network)是一种分布式网络架构,通过将内容缓存到全球多个节点,使用户可以从离自己最近的节点获取资源,从而加速内容加载。
CDN 的工作原理
- 内容缓存:将静态资源(如图片、CSS、JavaScript)缓存到 CDN 的边缘节点。
- 就近访问:用户请求资源时,CDN 会根据用户的地理位置,选择最近的节点提供服务。
- 负载均衡:CDN 会自动分配流量,避免单个服务器过载。
对前端的影响
-
加速资源加载:
- 使用 CDN 可以显著减少静态资源的加载时间,提升页面性能。
-
提高可用性:
- CDN 的分布式架构可以提高资源的可用性,即使某个节点故障,其他节点仍可提供服务。
-
优化用户体验:
- 通过减少延迟,提升用户访问速度,尤其是在全球范围内分发内容时。
前端如何使用 CDN
- 静态资源托管:将前端静态资源(如 JavaScript、CSS、图片)托管到 CDN。
- 配置缓存策略:通过设置 HTTP 缓存头(如
Cache-Control
)优化 CDN 缓存行为。
负载均衡
什么是负载均衡?
负载均衡(Load Balancing)是一种将网络流量分配到多个服务器的技术,目的是提高系统的可用性、扩展性和性能。
负载均衡的工作原理
- 流量分配:负载均衡器根据预设的算法(如轮询、最小连接数、哈希等)将请求分配到后端服务器。
- 健康检查:负载均衡器会定期检查后端服务器的健康状态,自动剔除故障服务器。
- 动态扩展:在流量增加时,负载均衡器可以动态添加新的服务器。
对前端的影响
-
提高系统可用性:
- 负载均衡可以避免单点故障,确保即使某个服务器宕机,服务仍可正常运行。
-
优化性能:
- 通过将流量分配到多个服务器,减少单个服务器的负载,提升响应速度。
-
支持高并发:
- 负载均衡可以扩展系统的处理能力,支持更多的并发用户。
前端如何与负载均衡配合
-
无状态设计:
- 前端应用应尽量设计为无状态,避免依赖特定服务器的会话数据。
-
API 请求优化:
- 前端应合理设计 API 请求,避免频繁的短连接,减少负载均衡器的压力。
前端性能优化
减少 HTTP 请求
- 合并文件:将多个 CSS 或 JavaScript 文件合并为一个文件,减少请求次数。
- 使用 CSS Sprites:将多个小图标合并为一张大图,通过
background-position
显示需要的部分。 - 内联关键资源:将关键的 CSS 或 JavaScript 直接内联到 HTML 中,减少首次渲染的阻塞。
压缩资源
- 压缩文件:使用工具(如 Gzip、Brotli)压缩 HTML、CSS、JavaScript 文件。
- 压缩图片:使用工具(如 ImageOptim、TinyPNG)压缩图片,或使用现代图片格式(如 WebP、AVIF)。
- 删除无用代码:使用 Tree Shaking 和代码分割工具(如 Webpack)删除未使用的代码。
使用缓存
- 浏览器缓存:通过设置 HTTP 缓存头(如
Cache-Control
、ETag
)缓存静态资源。 - Service Worker:使用 Service Worker 实现离线缓存和资源预加载。
- CDN 缓存:将静态资源托管到 CDN,并配置缓存策略。
优化加载顺序
- 延迟加载(Lazy Load) :延迟加载非关键资源(如图片、视频),直到用户滚动到它们的位置。
- 异步加载 JavaScript:使用
async
或defer
属性异步加载 JavaScript 文件,避免阻塞页面渲染。 - 预加载关键资源:使用
<link rel="preload">
预加载关键资源(如字体、CSS、JavaScript)。
优化渲染性能
-
减少重绘和回流:
- 避免频繁操作 DOM。
- 使用
transform
和opacity
实现动画,而不是top
、left
等属性。
-
使用虚拟 DOM:在框架(如 React、Vue)中使用虚拟 DOM 减少直接操作真实 DOM 的次数。
-
优化 CSS 选择器:避免使用复杂的选择器,减少样式计算时间。
使用现代前端技术
- HTTP/2 或 HTTP/3:使用 HTTP/2 或 HTTP/3 协议,支持多路复用、头部压缩等特性,提升加载速度。
- WebAssembly:将性能关键的部分用 WebAssembly 实现,提升计算性能。
- PWA(渐进式 Web 应用) :通过 Service Worker 和 Manifest 文件实现离线访问和原生应用体验。
优化图片和媒体
- 响应式图片:使用
<picture>
和srcset
根据设备分辨率加载合适的图片。 - 懒加载图片:使用
loading="lazy"
属性延迟加载图片。 - 视频优化:使用合适的视频格式(如 MP4、WebM),并压缩视频文件。
减少 JavaScript 执行时间
- 代码分割:使用动态导入(如
import()
)按需加载 JavaScript 模块。 - Web Workers:将耗时的计算任务放到 Web Workers 中执行,避免阻塞主线程。
- 避免长任务:将长时间运行的 JavaScript 任务拆分为多个小任务,使用
requestIdleCallback
或setTimeout
分批执行。
监控和分析
- 性能监控工具:使用 Lighthouse、Web Vitals、PageSpeed Insights 等工具分析页面性能。
- 真实用户监控(RUM) :通过监控真实用户的性能数据(如加载时间、交互延迟)优化体验。
- 错误监控:使用 Sentry、LogRocket 等工具监控前端错误,及时修复问题。
示例代码
延迟加载图片
<img src="placeholder.jpg" data-src="image.jpg" loading="lazy" alt="Lazy Loaded Image">
<script>
document.addEventListener('DOMContentLoaded', function() {
const images = document.querySelectorAll('img[data-src]');
images.forEach(img => {
img.src = img.dataset.src;
});
});
</script>
异步加载 JavaScript
<script src="script.js" async></script>
预加载关键资源
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
使用 Service Worker 缓存
// service-worker.js
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open('my-cache').then((cache) => {
return cache.addAll([
'/',
'/styles.css',
'/script.js',
'/image.jpg'
]);
})
);
});
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request);
})
);
});
性能监测
利用 Google Analytics 监测用户体验。
// vue3 项目为例 在main.js中,插入GA代码
import { createApp } from 'vue';
import App from './App.vue';
const app = createApp(App);
app.mount('#app');
// Google Analytics 配置
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'YOUR_GA_TRACKING_ID'); // 替换为你的 GA ID
通过分析工具
使用 rollup-plugin-visualizer
进行分析。(vite搭建项目为例,webpack也有对应的插件自行了解吧)
在 Vite 配置中已经引入了 visualizer
插件,它将在构建完成后生成一个可视化的报告,帮助你了解构建包的组成,以及某些依赖的打包大小。
关于前端性能优化的各种代码指标
前端性能优化是提高用户体验的关键因素之一。为了确保应用能够快速响应和加载,开发者需要监控和优化多个指标。以下是一些常见的前端性能优化指标,以及相应的代码示例和应使用的最佳实践。
加载时间指标
首屏加载时间(First Contentful Paint, FCP)
表示浏览器开始渲染页面的时间,内容开始展示的时间。
// 使用 Performance API 监控 FCP
const fcpObserver = new PerformanceObserver((entryList) => {
const entries = entryList.getEntries();
console.log('首屏加载时间 (FCP):', entries[0].startTime);
});
fcpObserver.observe({ type: 'paint', buffered: true });
首次有效绘制(First Meaningful Paint, FMP)
首次有效绘制是指用户看到主要内容的时间。
const fmpObserver = new PerformanceObserver((entryList) => {
const entries = entryList.getEntries();
console.log('首次有效绘制时间 (FMP):', entries[0].startTime);
});
fmpObserver.observe({ type: 'paint', buffered: true });
页面加载时间 (Page Load Time)
从用户 request 发送到页面完全加载的时间。
window.addEventListener('load', () => {
const pageLoadTime = performance.timing.loadEventEnd - performance.timing.navigationStart;
console.log('页面加载时间:', pageLoadTime, '毫秒');
});
交互性能指标
首次输入延迟(First Input Delay, FID)
用户首次与页面交互(如点击、输入等)到浏览器立即响应的时间。
let fidObserver = new PerformanceObserver((observer) => {
observer.getEntries().forEach((entry) => {
console.log('首次输入延迟 (FID):', entry.startTime);
});
});
fidObserver.observe({ type: 'first-input', buffered: true });
最大内容绘制(Largest Contentful Paint, LCP)
表示从页面开始加载到内容(如图像、文本块等)呈现时间。
const lcpObserver = new PerformanceObserver((entryList) => {
const entries = entryList.getEntries();
console.log('最大内容绘制时间 (LCP):', entries[0].startTime);
});
lcpObserver.observe({ type: 'largest-contentful-paint', buffered: true });
用户体验指标
累积布局偏移(Cumulative Layout Shift, CLS)
量化页面视觉稳定性的指标,描述页面加载过程中组件移动的程度。
let clsValue = 0;
const clsObserver = new PerformanceObserver((entryList) => {
entryList.getEntries().forEach((entry) => {
clsValue += entry.value;
});
console.log('累积布局偏移 (CLS):', clsValue);
});
clsObserver.observe({ type: 'layout-shift', buffered: true });
网络性能指标
网络请求时间
监控每个网络请求的时间,帮助识别慢请求。
const networkObserver = new PerformanceObserver((entryList) => {
entryList.getEntries().forEach((entry) => {
console.log(`请求: ${entry.name}, 耗时: ${entry.duration} ms`);
});
});
networkObserver.observe({ type: 'resource', buffered: true });
使用 Lighthouse 进行全面性能审计
运行 Lighthouse 可为网页提供详细的性能报告和优化建议,包含 FCP、LCP、CLS、FID 等。
# 安装 Lighthouse
npm install -g lighthouse
# 运行 Lighthouse 审计
lighthouse https://example.com --output html --output-path ./report.html
监控与日志记录
日志记录
结合错误监控工具(如 Sentry、LogRocket)来收集和分析性能指标。
import * as Sentry from '@sentry/browser';
Sentry.init({ dsn: 'YOUR_SENTRY_DSN' });
// 静态性能指标
Sentry.captureEvent({
message: 'Performance Metrics',
extra: {
fcp: fcpTime,
lcp: lcpTime,
timeToInteractive: timeToInteractive,
},
});
vue3+vite5项目的全链路性能优化相关代码 示例
在Vue 3 + Vite 5项目中,全链路性能优化可以从多个方面入手,包括代码分割、懒加载、缓存策略、Tree Shaking、图片优化等。
代码分割与懒加载
Vite 支持动态导入(Dynamic Import),可以将代码分割成多个小块,按需加载。
// 使用动态导入进行懒加载
const LazyComponent = defineAsyncComponent(() => import('./components/LazyComponent.vue'));
Tree Shaking
Vite 默认支持 Tree Shaking,确保只打包使用到的代码。确保你的代码库支持 ES 模块(ESM),并且避免使用副作用较大的模块。
// 只导入需要的部分
import { ref, computed } from 'vue';
图片优化
使用 Vite 的插件 vite-plugin-imagemin
来压缩图片。
pnpm add vite-plugin-imagemin -D
// vite.config.js
import { defineConfig } from 'vite';
import viteImagemin from 'vite-plugin-imagemin';
export default defineConfig({
plugins: [
viteImagemin({
gifsicle: { optimizationLevel: 7, interlaced: false },
optipng: { optimizationLevel: 7 },
mozjpeg: { quality: 20 },
pngquant: { quality: [0.8, 0.9], speed: 4 },
svgo: {
plugins: [
{ name: 'removeViewBox' },
{ name: 'removeEmptyAttrs', active: false },
],
},
}),
],
});
缓存策略
通过配置 HTTP 缓存头来优化静态资源的缓存。
// vite.config.js
import { defineConfig } from 'vite';
export default defineConfig({
build: {
rollupOptions: {
output: {
assetFileNames: 'assets/[name]-[hash].[ext]',
chunkFileNames: 'assets/[name]-[hash].js',
entryFileNames: 'assets/[name]-[hash].js',
},
},
},
});
使用 vite-plugin-compression
压缩资源
使用 vite-plugin-compression
插件来压缩构建后的资源文件。
pnpm add vite-plugin-compression -D
// vite.config.js
import { defineConfig } from 'vite';
import viteCompression from 'vite-plugin-compression';
export default defineConfig({
plugins: [
viteCompression({
algorithm: 'gzip',
ext: '.gz',
}),
],
});
使用 vite-plugin-pwa
实现 PWA
通过 vite-plugin-pwa
插件实现 PWA,提升应用的离线体验。
pnpm add vite-plugin-pwa -D
// vite.config.js
import { defineConfig } from 'vite';
import { VitePWA } from 'vite-plugin-pwa';
export default defineConfig({
plugins: [
VitePWA({
registerType: 'autoUpdate',
manifest: {
name: 'My App',
short_name: 'App',
theme_color: '#ffffff',
icons: [
{
src: '/icons/icon-192x192.png',
sizes: '192x192',
type: 'image/png',
},
{
src: '/icons/icon-512x512.png',
sizes: '512x512',
type: 'image/png',
},
],
},
}),
],
});
使用 vite-plugin-html
优化 HTML
通过 vite-plugin-html
插件来优化 HTML 文件的生成。
pnpm add vite-plugin-html -D
// vite.config.js
import { defineConfig } from 'vite';
import { createHtmlPlugin } from 'vite-plugin-html';
export default defineConfig({
plugins: [
createHtmlPlugin({
minify: true,
inject: {
data: {
title: 'My App',
},
},
}),
],
});
使用 vite-plugin-inspect
分析构建
使用 vite-plugin-inspect
插件来分析构建结果,找出性能瓶颈。
pnpm add vite-plugin-inspect -D
// vite.config.js
import { defineConfig } from 'vite';
import Inspect from 'vite-plugin-inspect';
export default defineConfig({
plugins: [
Inspect(),
],
});
使用 vite-plugin-analyzer
进行包分析
使用 vite-plugin-analyzer
插件来分析打包后的文件大小。
pnpm add vite-plugin-analyzer -D
// vite.config.js
import { defineConfig } from 'vite';
import { analyzer } from 'vite-plugin-analyzer';
export default defineConfig({
plugins: [
analyzer(),
],
});
使用 vite-plugin-svg-icons
优化 SVG 图标
通过 vite-plugin-svg-icons
插件优化 SVG 图标的使用。
pnpm add vite-plugin-svg-icons -D
// vite.config.js
import { defineConfig } from 'vite';
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
import path from 'path';
export default defineConfig({
plugins: [
createSvgIconsPlugin({
iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
symbolId: 'icon-[dir]-[name]',
}),
],
});
使用 vite-plugin-optimize-persist
优化持久化缓存
通过 vite-plugin-optimize-persist
插件优化持久化缓存。
pnpm add vite-plugin-optimize-persist -D
// vite.config.js
import { defineConfig } from 'vite';
import { VitePluginOptimizePersist } from 'vite-plugin-optimize-persist';
export default defineConfig({
plugins: [
VitePluginOptimizePersist(),
],
});
优化路由
- 懒加载路由:在 Vue Router 中使用懒加载机制,以减少初始加载时间。
const routes = [
{
path: '/home',
component: () => import('./views/Home.vue'),
},
];
使用虚拟列表
在需要展示大量列表项时,可以使用虚拟列表技术(如 vue-virtual-scroller
),只渲染可见部分,提升渲染性能
减少第三方库的使用
- 按需引入库:只引入项目中实际需要的部分,而不是整个库。
import { Button } from 'element-plus'; // 按需引入
懒加载图片
使用原生的 loading="lazy"
属性,实现图片的懒加载,提高页面的首次渲染速度。
<img src="image.jpg" loading="lazy" alt="Image">
事件处理
- 使用事件捕获:使用事件捕获模式减少事件冒泡带来的性能损耗。
<button @click.capture="handleClick">Click Me</button>
- 事件的节流与防抖:使用节流和防抖技术避免重复触发高频事件。
const throttledFunction = throttle(() => {
// 处理逻辑
}, 1000);
总结
前端性能优化是一个持续的过程,需要从多个角度(如网络、渲染、资源加载等)进行优化。通过合理使用缓存、减少请求、优化代码和监控性能,可以显著提升用户体验和应用性能。