一文吃透前端网络相关层面知识(矿泉水备好)

182 阅读31分钟

概念涉及

1. HTTP/HTTPS

image.png

  • 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设计

apifoxpostmanSwaggerYApi

  • RESTful API: 学习REST架构风格的原则,如何设计和实现RESTful API。
  • GraphQL: 了解GraphQL的基本概念及其与REST的不同之处。

7. 安全性

  • 安全最佳实践: 学习前端开发中的安全问题,如XSS(跨站脚本攻击)、CSRF(跨站请求伪造)等,以及如何防范。

8. 网络监控与调试

  • 浏览器开发者工具: 学习如何使用开发者工具中的网络面板,监控请求和响应,进行性能分析。

9. 网络协议

  • TCP/IP基础: 理解传输控制协议(TCP)和互联网协议(IP)的基本概念。
  • CDN和负载均衡: 学习内容分发网络的工作原理及其对前端性能的影响。

细聊概念

http相关

image.png

image.png

image.png

image.png

image.png HTTP(超文本传输协议)是用于在客户端和服务器之间传输数据的协议。HTTP的工作原理可以分为以下几个步骤:

  1. 建立连接:客户端浏览器向服务器发起连接请求,一般使用TCP协议(默认端口为80)。
  2. 发送请求:连接建立后,客户端会发送一个HTTP请求,包括请求方法、URI、HTTP版本及请求头信息。
  3. 处理请求:服务器接收到请求后,根据请求的内容进行处理,并生成响应。
  4. 发送响应:服务器将处理结果返回给客户端,发送一个HTTP响应,包括状态码、响应头和响应体。
  5. 关闭连接:一旦数据传输完成,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协议实现数据加密,确保数据在传输过程中的安全性。其工作原理如下:

  1. SSL/TLS握手: 在客户端与服务器建立连接时,首先进行SSL/TLS握手,期间双方交换信息,包括支持的加密算法和生成会话密钥。
  2. 证书验证: 服务器会向客户端提供其数字证书,客户端通过可信的证书颁发机构(CA)验证该证书的有效性和可信度。
  3. 建立安全连接: 一旦验证通过,客户端和服务器使用对称加密算法生成一个会话密钥,该密钥用于后续的数据加密和解密。
  4. 数据传输: 在传输过程中,所有数据均通过SSL/TLS层进行加密,确保数据在传输过程中的机密性与完整性。
  5. 连接终止: 数据传输完成后,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)。
    • 适合发送简单的文本数据。

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发送请求时,需要指定responseTypeblob,以便正确处理二进制数据。

示例:

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' });
    
  • 大文件下载:对于大文件下载,可以考虑使用分块下载或流式下载,以提高性能和用户体验。

处理二进制文件下载的关键步骤包括:

  1. 设置responseType: 'blob' :确保Axios正确处理二进制数据。
  2. 创建Blob URL:使用window.URL.createObjectURL将Blob对象转换为可下载的URL。
  3. 模拟点击下载:通过动态创建<a>标签并设置download属性,触发文件下载。
  4. 清理资源:下载完成后,释放Blob URL以避免内存泄漏。

前端输入uir到页面呈现经历了什么

1. 输入URL

用户在浏览器地址栏中输入URL(如https://www.example.com),并按下回车键。

2. DNS解析

浏览器需要将域名(如www.example.com)解析为对应的IP地址,以便与服务器建立连接。具体步骤如下:

  1. 浏览器检查本地缓存(如浏览器缓存、操作系统缓存)是否有该域名的IP地址。
  2. 如果缓存中没有,浏览器会向本地DNS服务器发起请求。
  3. 如果本地DNS服务器没有缓存,它会向根DNS服务器、顶级域名服务器(如.com)和权威DNS服务器逐级查询,最终获取IP地址。

3. 建立TCP连接

浏览器通过IP地址与服务器建立TCP连接。这个过程通常包括以下步骤:

  1. 三次握手

    • 浏览器发送SYN包到服务器。
    • 服务器回复SYN-ACK包。
    • 浏览器发送ACK包,连接建立。
  2. 如果使用HTTPS,还会进行TLS/SSL握手,建立加密连接。

4. 发送HTTP请求

浏览器向服务器发送HTTP请求,请求行中包含请求方法(如GET)、URI(如/index.html)和HTTP版本(如HTTP/1.1)。请求头中可能包含HostUser-AgentAccept等信息。

示例:

GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0
Accept: text/html

5. 服务器处理请求

服务器接收到请求后,根据请求的URI和请求方法进行处理:

  1. 如果是静态资源(如HTML、CSS、JS文件),服务器直接返回文件内容。
  2. 如果是动态资源(如PHP、Node.js),服务器会执行相应的后端逻辑,生成HTML内容。
  3. 服务器将处理结果封装在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响应后,开始解析响应内容:

  1. 解析HTML:浏览器将HTML文档解析为DOM(文档对象模型)树。
  2. 加载外部资源:如果HTML中包含外部资源(如CSS、JS、图片),浏览器会发起额外的HTTP请求来获取这些资源。
  3. 解析CSS:浏览器将CSS解析为CSSOM(CSS对象模型)树。
  4. 执行JavaScript:浏览器执行JavaScript代码,可能会修改DOM或CSSOM。

7. 构建渲染树

image.png image.png

浏览器将DOM树和CSSOM树合并为渲染树(Render Tree),渲染树只包含需要显示的节点及其样式信息。

8. 布局(Layout)

浏览器根据渲染树计算每个节点的几何信息(如位置、大小),这一过程称为布局或重排(Reflow)。

9. 绘制(Painting)

浏览器将渲染树的每个节点绘制到屏幕上,这一过程称为绘制或重绘(Repaint)。

10. 页面呈现

最终,页面内容显示在浏览器窗口中,用户可以与之交互。

WebSockets

WebSockets 是一种在单个 TCP 连接上进行全双工通信的协议,适用于需要实时数据传输的场景(如聊天应用、实时通知、在线游戏等)

1. WebSockets 的基本概念

  • 全双工通信:客户端和服务器可以同时发送和接收数据。
  • 持久连接:建立连接后,连接会一直保持,直到客户端或服务器主动关闭。
  • 低延迟:相比 HTTP 轮询,WebSockets 的延迟更低,适合实时应用。

2. WebSockets 的工作流程

  1. 握手:客户端通过 HTTP 请求发起 WebSocket 连接,服务器返回确认信息。
  2. 通信:连接建立后,客户端和服务器可以通过 send 方法发送数据,通过事件监听接收数据。
  3. 关闭:客户端或服务器可以主动关闭连接。

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>
  1. 处理消息: 在onmessage中处理接收到的消息,根据需要更新状态或执行操作。
  2. 优雅关闭: 在onBeforeUnmount中关闭WebSocket连接,以清理资源。确保组件卸载时不会有未处理的连接。
  3. 错误处理: 在连接的onerroronclose事件中处理错误和连接关闭逻辑,可以进行重连机制等。
  4. 状态管理: 可以结合Vuex或Pinia等状态管理库来管理应用中的WebSocket状态,例如连接状态、接收到的消息等。
  5. 优化性能: 根据实际情况,可以考虑使用节流(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 的工作原理

  1. 简单请求:对于某些简单的请求(如 GET 或 POST 请求,且不包含自定义头信息),浏览器会直接发送请求,并在请求头中包含 Origin 字段,表示请求的来源。服务器可以根据 Origin 字段决定是否允许该请求,并在响应头中包含 Access-Control-Allow-Origin 字段来指示允许的源。
  2. 预检请求:对于复杂的请求(如 PUT、DELETE 请求,或包含自定义头信息的请求),浏览器会先发送一个 OPTIONS 请求(称为预检请求),询问服务器是否允许该跨域请求。服务器在响应中通过 Access-Control-Allow-OriginAccess-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 发送到服务器以验证用户身份。
  • 跨域资源共享(CORS)

    • 如果前端应用和后端 API 部署在不同的域名下(如前端在 https://example.com,后端在 https://api.example.com),并且需要共享凭据,则需要启用此选项。
  • 需要携带凭据的请求

    • 某些 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。

网络监控与调试

浏览器开发者工具

image.png 大多数现代浏览器都提供强大的开发者工具,用于网络监控与调试。

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 的工作原理
  1. 内容缓存:将静态资源(如图片、CSS、JavaScript)缓存到 CDN 的边缘节点。
  2. 就近访问:用户请求资源时,CDN 会根据用户的地理位置,选择最近的节点提供服务。
  3. 负载均衡:CDN 会自动分配流量,避免单个服务器过载。
对前端的影响
  • 加速资源加载

    • 使用 CDN 可以显著减少静态资源的加载时间,提升页面性能。
  • 提高可用性

    • CDN 的分布式架构可以提高资源的可用性,即使某个节点故障,其他节点仍可提供服务。
  • 优化用户体验

    • 通过减少延迟,提升用户访问速度,尤其是在全球范围内分发内容时。
前端如何使用 CDN
  • 静态资源托管:将前端静态资源(如 JavaScript、CSS、图片)托管到 CDN。
  • 配置缓存策略:通过设置 HTTP 缓存头(如 Cache-Control)优化 CDN 缓存行为。

负载均衡

什么是负载均衡?

负载均衡(Load Balancing)是一种将网络流量分配到多个服务器的技术,目的是提高系统的可用性、扩展性和性能。

负载均衡的工作原理
  1. 流量分配:负载均衡器根据预设的算法(如轮询、最小连接数、哈希等)将请求分配到后端服务器。
  2. 健康检查:负载均衡器会定期检查后端服务器的健康状态,自动剔除故障服务器。
  3. 动态扩展:在流量增加时,负载均衡器可以动态添加新的服务器。
对前端的影响
  • 提高系统可用性

    • 负载均衡可以避免单点故障,确保即使某个服务器宕机,服务仍可正常运行。
  • 优化性能

    • 通过将流量分配到多个服务器,减少单个服务器的负载,提升响应速度。
  • 支持高并发

    • 负载均衡可以扩展系统的处理能力,支持更多的并发用户。
前端如何与负载均衡配合
  • 无状态设计

    • 前端应用应尽量设计为无状态,避免依赖特定服务器的会话数据。
  • 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-ControlETag)缓存静态资源。
  • Service Worker:使用 Service Worker 实现离线缓存和资源预加载。
  • CDN 缓存:将静态资源托管到 CDN,并配置缓存策略。

 优化加载顺序

  • 延迟加载(Lazy Load) :延迟加载非关键资源(如图片、视频),直到用户滚动到它们的位置。
  • 异步加载 JavaScript:使用 async 或 defer 属性异步加载 JavaScript 文件,避免阻塞页面渲染。
  • 预加载关键资源:使用 <link rel="preload"> 预加载关键资源(如字体、CSS、JavaScript)。

优化渲染性能

  • 减少重绘和回流

    • 避免频繁操作 DOM。
    • 使用 transform 和 opacity 实现动画,而不是 topleft 等属性。
  • 使用虚拟 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);

总结

前端性能优化是一个持续的过程,需要从多个角度(如网络、渲染、资源加载等)进行优化。通过合理使用缓存、减少请求、优化代码和监控性能,可以显著提升用户体验和应用性能。