#1024程序员节 | 征文#
总结一些前端领域浏览器常用知识,浏览器跨域、缓存、渲染、存储、协议等。
目录
(4)安全设置Access-Control-Allow-Origin最佳实践
[2.Web Storage](#2.Web Storage)
[1)HTTP(超文本传输协议)-- 应用层](#1)HTTP(超文本传输协议)-- 应用层)
一、同源策略
1.定义
同源策略是Web开发中的重要概念,用于限制不同源之间的资源访问。它是浏览器的一种安全机制,旨在保护用户信息的安全和隐私。
2.原理
"源"由协议、域名和端口三部分组成。
如果两个URL的这三部分完全相同,它们被认为是同源的,否则被认为是不同源的。
3.作用
防止恶意网站通过跨域请求来获取用户的敏感信息,保护用户的隐私安全。
将不同源的网页隔离开来,提高浏览器的稳定性和安全性。
二、跨域问题
1.产生原因
由于同源策略的限制,不同源的网页之间无法进行直接的资源访问和交互,就产生了跨域问题。
2.解决方案
1)JSONP
利用<script>标签的跨域特性,通过动态创建<script>标签并设置其src属性为目标API接口的URL,绕过同源策略的限制。但JSONP只支持GET请求,不支持POST请求。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSONP Example</title>
<script type="text/javascript">
// 定义回调函数,用于处理服务端返回的数据
function handleResponse(data) {
console.log("Received data from server:", data);
}
// 创建并添加<script>标签到DOM中,用于发起JSONP请求
function fetchData() {
var script = document.createElement('script');
script.src = 'http://example.com/api/data?callback=handleResponse';
document.body.appendChild(script);
}
// 在页面加载完成后调用fetchData函数发起JSONP请求
window.onload = fetchData;
</script>
</head>
<body>
</body>
</html>
2)CORS
(1)定义
CORS是一种跨域请求的标准,通过在服务器的响应头中添加Access-Control-Allow-Origin等字段来指定允许跨域请求的源。服务器返回的响应头还可以设置其他字段,如Access-Control-Allow-Methods、Access-Control-Allow-Headers等,以进一步控制跨域请求的权限。
CORS需要客户端和服务器都进行配置才能正常使用。
(2)使用方式
请求头:在发送跨域请求时,浏览器会自动在请求头中添加origin字段,以指定当前页面的域名。
响应头:在服务器接收到跨域请求后,需要在响应头中添加如下字段:
- Access-Control-Allow-Origin:指定允许跨域的源。可以是具体的域名,可以是*,表示接收任意域名的请求。
- Access-Control-Allow-Methods:指定允许跨域请求的方法,GET、POST、PUT、DELETE等。
- Access-Control-Allow-Headers:指定允许跨域请求的头信息字段。
(3)设置允许多个源跨域
- Origin头部动态设置Access-Control-Allow-Origin
服务器可以根据请求的Origin头部动态地设置Access-Control-Allow-Origin的值,检查请求的Origin是否在服务器的白名单中,如果是,则将该Origin作为响应头的值。
例如在Java Servlet中,可以通过检查HttpServletRequest的getHead("Origin")方法返回的值,并在白名单中验证该值后,使用HttpServletReponse的setHeader方法来设置Access-Control-Allow-Origin。
如果使用的是Node.js和Express框架,可以使用中间件来动态设置Access-Control-Allow-Origin:
const express = require('express');
const app = express();
const allowedOrigins = ['https://example1.com', 'https://example2.com']; // 允许的源白名单
app.use((req, res, next) => {
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
} else {
// 可以选择返回一个错误响应或继续处理请求但不允许跨域
res.status(403).send('Forbidden');
}
next();
});
// 其他路由和中间件...
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
- 使用代理服务器(如Nginx或Apache)处理CORS请求
代理服务器可以在转发请求到实际服务器之前,根据请求的Origin头部动态地添加或修改Access-Control-Allow-Origin响应头。
通过配置代理服务器的CORS策略,可以允许或拒绝来自不同源的请求,则无需修改后端服务器的代码。
(4)安全设置Access-Control-Allow-Origin最佳实践
- 最小化允许的域名 :只将Access-Control-Allow-Origin设置为实际需要的域名,避免使用*。
- 使用HTTPS:确保跨域请求通过HTTPS进行,以增加数据传输的安全性。
- 定期审查:定期审查并更新允许的域名列表,确保不再使用的域名被及时移除。
- 使用代理服务器:在可能的情况下,使用代理服务器来处理CORS请求,以减轻后端服务器的负担并提高安全性。
- 实施其他安全措施:如验证请求的HTTP方法、请求头等,以确保请求的合法性。
补充:
当Access-Control-Allow-Origin设置为*时,通常不能与凭据设置一起使用。
如果跨域请求包含凭据(如Cookies或HTTP认证信息),则Access-Control-Allow-Origin不能设置为*。为了安全地发送凭据,Access-Control-Allow-Origin必须设置为一个具体的、与请求源匹配的域名,并且服务器还需要设置Access-Control-Allow-Credentials为true。
3)代理服务器
通过在客户端和服务器之间设置一个代理服务器,将客户端的跨域请求转发给代理服务器,再由代理服务器将请求转发给目标服务器,从而绕过同源策略的限制。开发过程中,可以使用Webpack Dev Server的proxy代理功能来实现。
const path = require('path');
module.exports = {
// 其他 Webpack 配置...
devServer: {
contentBase: path.join(__dirname, 'dist'),
compress: true,
port: 9000,
proxy: {
'/api': {
target: 'http://localhost:3000', // 后端服务器的地址
changeOrigin: true, // 如果目标服务器设置了 CORS 策略,需要将其设置为 true
pathRewrite: {'^/api': ''}, // 将请求的 /api 前缀重写为空,这样 /api/users 就会变成 http://localhost:3000/users
},
},
},
};
使用Vue3+Vite构建项目,使用代理时:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
base: '/', // 设置 base URL
server: {
host: "localhost",
cors: true, // 启用默认的 CORS 策略,或者在后端服务器上配置 CORS
port: 5173,
proxy: {
'/api': {
target: 'https://example.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
},
},
},
})
三、浏览器缓存机制
1.过程
浏览器缓存机制主要是根据HTTP报文的缓存表示进行的。当浏览器首次请求某个资源时,服务器会在响应报文中包含缓存相关的头部字段(如Expires、Cache-Control、Last-Modified、ETag等),告诉浏览器该如何缓存该资源以及缓存的有效期。在后续请求中,浏览器会根据这些缓存头部字段来判断是否可以直接使用本地缓存,从而避免想服务器发送请求。
2.缓存策略
1)强缓存
强缓存指浏览器在请求资源时,会先检查本地缓存是否存在该资源副本,并且该副本是否过期。如果资源副本未过期,浏览器就直接使用本地缓存,不会向服务器发送请求。
(1)实现方式
强缓存依赖于HTTP响应头中的Expires和Cache-Control字段。Expires是一个具体的过期时间,浏览器会根据该事件判断资源是否过期。Cache-Control是相对时间,可以指定资源的有效时间。
(2)优先级
Cache-Control的优先级高于Expires。如果同时存在,浏览器优先使用Cache-Control字段来判断缓存是否有效。
2)协商缓存
当资源的副本过期或者浏览器的缓存被清除时,浏览器会向服务器发送请求,询问该资源是否有更新。服务器会根据资源的最后修改时间或者ETag(实体标签)来判断资源是否有更新。如果资源没有更新,服务器会返回一个304 Not Modified响应,告知浏览器可以直接使用本地缓存。
(1)实现方式
协商缓存依赖于HTTP请求头中的If-Modified-Since和If-None-Match字段,以及HTTP响应头中的Last-Modified和ETag字段。
(2)工作原理
浏览器在请求资源时,会在请求头中包含If-Modified-Since(如果资源有Last-Modified头部)或If-None-Match(如果资源中有ETag头部)。服务器收到请求后,会比较请求头部中的字段值与资源当前的Last-Modified或ETag值。如果相同,说明资源没有更新,返回304响应;如果不同,则说明资源有更新,返回新的资源和相应的头部字段。
补充:
Expires:
Expires用于声明一个资源(如网页、图片、脚本文件等)不再被浏览器缓存的时间点
方式:通过指定一个具体的日期时间(通常是GMT格式),例如"Expires: Sat, 01 Jan 2023 00:00:00 GMT"。
Cache-Control:
Cache-Control字段是HTTP报文中的通用首部字段,用于控制缓存的行为,它既存在于请求报文中,也存在于响应报文中。
Cache-Control能覆盖其他缓存相关的设置,如Expires和Last-Modified,提供更可靠的缓存控制机制。
常见指令:
public:表示响应可以被任何缓存(包括浏览器缓存、代理服务器缓存等)缓存。
private:表示响应只能被客户端(如浏览器)缓存,代理服务器等中间缓存不能缓存该响应。
no-cache :表示客户端在使用缓存资源之前,必须先与服务器确认资源是否已更改。不是不使用缓存,而是每次使用缓存资源之前都要验证其有效性。
no-store:禁止缓存,每次请求都要向服务器重新获取数据。
max-age:指定一个时间长度,在这个时间段内缓存是有效的,单位为秒。
四、浏览器获取URL后
1.过程
1)URL解析与合法性检查
用户在浏览器地址栏输入URL并按下回车后,浏览器首先会解析用户输入并判断这是一个合法的URL。
2)DNS解析
浏览器需要获取URL对应的IP地址,以便与服务器建立连接,这个过程称为DNS解析。
浏览器首先查看本地DNS缓存中是否有对应IP地址,如果有,直接使用缓存中的IP地址,没有则一次查询操作系统的hosts文件、系统缓存中的DNS记录、路由器缓存,以及ISP(互联网服务提供商)的DNS服务器。如果以上都没有找到IP地址,ISP DNS服务器可能会进行递归查询,即向根DNS服务器、顶级域名服务器等逐步查询,直到找到对应的IP地址。
3)TCP连接建立(三次握手)
获取到IP地址后,浏览器会与服务器建立TCP连接,包括三次握手:
- 第一次握手 :客户端向服务端发送连接请求,并携带同步序列信号(SYN)。此时客户端进入SYN_SEND状态,等待服务端的确认。
- 第二次握手 :服务器收到请求后,会发送SYN+ACK(同步/应答)报文,表示同意连接请求,并确认收到客户的SYN报文。此时服务端进入SYN_RECV状态。
- 第三次握手 :客户端收到服务端的SYN和ACK报文后,会再次发送一个ACK(应答)报文,表示确认收到服务端的SYN报文,并同意建立连接。此时,客户端和服务端都进入ESTABLISHED状态,连接建立成功。
三次握手完成后,TCP连接建立成功,浏览器和服务器之间可以开始传输数据。
4)发送HTTP请求
TCP连接建立后,浏览器向服务器发送HTTP请求报文(请求头、请求体、请求行)。
5)服务器响应
服务器收到HTTP请求后,根据请求路径和参数生成HTTP响应,返回给浏览器(状态码、响应头、响应体)。
6)浏览器处理HTTP响应
如果响应中包含可以缓存的内容,浏览器会将其存入缓存中,以便下次访问时使用。
浏览器对HTML文档进行解析,构建DOM树、CSS树、渲染树等。
DOM树:表示HTML文档的结构
CSS树:表示CSS样式信息
渲染树:将DOM树和CSS树合并后形成的树形结构,用于表示页面上应该显示哪些元素以及它们的样式。
浏览器布局,为每一个节点分配一个应出现在屏幕上的坐标。
调用GPU进行绘制,遍历渲染树的节点,将元素呈现。
7)连接关闭(四次挥手)
浏览器从服务器接收到所有需要的数据后,会关闭TCP连接,包括四次挥手:
- 第一次挥手 :客户端向服务器发送FIN(结束)报文,表示数据已经发送完毕。此时,客户端进入FIN_AWIT_1状态。
- 第二次挥手 :服务器收到客户端的报文后,会发送一个ACK报文,表示已经收到关闭请求。此时,服务器进入CLOSE_AWIT状态,客户端收到ACK报文后,进入FIN_WAIT_2状态。
- 第三次挥手 :服务器在关闭数据传输前,会向客户端发送一个FIN报文,用来关闭服务器到客户端的数据传输。此时,服务器进入LAST_ACK状态。
- 第四次挥手 :客户端收到服务器的FIN报文后,会发送一个ACK报文,表示确认关闭连接。此时,客户端进入TIME_WAIT状态,等待一段时间后,确保服务器端的未传输完的数据都接收到后再关闭连接。服务器收到客户端的ACK报文后,立即关闭连接。
2.为什么要"三次握手""四次挥手"
"三次握手"和"四次挥手"确保了TCP连接的可靠性和稳定性。
1)三次握手
(1)三次握手能够确保客户端和服务端都具备发送和接收数据的能力,通过三次交互,双方能确认对方已经准备好进行数据传输。
(2)TCP协议使用序列号来标记已发送数据的位置,并通过确认号来表示数据已被接收。三次握手过程中,双方会交换序列号,从而确保后续数据传输的同步性。
2)四次挥手
(1)TCP连接是全双工的,即双方可以同时进行数据传输。因此,需要分别终止每个方向上的数据传输,四次挥手确保双方都能正确地关闭自己的数据传输通道。
(2)四次挥手中,服务端在发送FIN报文之前,会确保所有传输到客户端的数据已经发送完毕。同样,客户端在发送ACK报文之前,也会确保所有接收到的数据都已经处理完毕,可以确保数据的完整性和准确性。
(3)在TIME_WAIT状态下,客户端会等待一段时间(通常是2倍的MSL,即最长报文段寿命),以确保所有旧的报文都已经从网络中消失。这样可以防止旧的连接报文干扰新的连接建立。
五、浏览器数据存储
1.Cookie
Cookie是浏览器存储的一个小文本文件,内部以键值对的方式存储数据。
1)特点
- 会随每次HTTP请求发送到服务器,服务器可以读取并解析Cookie,从而获取客户端的状态信息。
- 容量较小,单个Cookie的大小通常不超过4KB。
- 可以设置过期时间和作用域,过期后或作用域外的Cookie将被浏览器删除。
- 安全性较低,容易受到攻击,如XSS、CSRF等。
2)用途
- 存储用户的身份信息、会话状态等。
- 在用户访问网站时,通过Cookie传递状态信息,实现自动登录、购物车等功能。
2.Web Storage
Web Storage分为localStorage(本地存储)和sessionStorage(会话存储)。
1)localStorage
(1)特点
- 数据存储在本地,永久有效,除非主动删除。
- 容量较大,每个域名下的存储空间通常为5MB。
- 数据以键值对的形式存储,支持字符串类型的值。
- 受同源策略限制,无法跨域访问。
(2)用途
- 可以存储一些内容稳定的资源,如官网的logo、Base64格式的图片资源等。
- 可以用于实现离线存储功能。
2)sessionStorage
(1)特点
- 数据存储在会话期间有效,关闭浏览器或会话结束后数据将被清除。
- 容量较大,每个域名下的存储空间是5MB。
- 数据以键值对的形式存储,支持字符串类型的值。
- 受同源策略限制,无法跨域访问。
(2)用途
- 用于存储会话期间的数据,如用户登录信息、临时缓存等。
- 可以用于实现页面间的数据共享等功能。
3.IndexedDB
IndexedDB是一种基于JavaScript的数据库,可以在浏览器中存储大量结构化数据。
1)特点
- 提供更强大的数据存储和查询功能,支持复杂的数据结构,如数组和对象。
- 没有固定的存储上限,可以根据需要创建多个数据库对象和数据对象。
- 需要通过复杂的API进行操作,使用相对复杂。
- 受同源策略限制,无法跨域访问。
2)用途
- 用于存储大量的结构化数据,如用户信息、订单信息等。
- 可以用于实现数据查询、增删查改等操作。
4.补充
XSS(跨站脚本攻击) 和CSRF(跨站请求伪造)
1)XSS
是一种代码注入攻击,攻击者通过在目标网站注入恶意脚本,当其他用户浏览该网站时,这些恶意脚本就会在用户的浏览器中执行,从而达到攻击的目的。
(1)攻击原理
- 攻击者利用网站对用户提交的数据进行转义处理或者过滤不足的缺点,进而添加一些代码,嵌入到web页面中去。
- 当其他用户访问该页面时,这些恶意脚本就会被加载到用户的浏览器中并执行,从而窃取用户信息、利用用户身份进行某种动作或进行病毒侵害。
(2)防御方法
- 对用户输入的内容进行严格的过滤和编码,确保恶意脚本无法被注入到网站上。
- 使用HTTPOnly属性来设置Cookie,防止XSS攻击者通过JavaScript访问Cookie。
- 定期更新和维护Web应用程序,及时修复已知的安全漏洞。
1)CSRF
跨站请求伪造是一种挟制用户在当前已登录的Web应用程序上执行非本意操作的攻击方法,通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并执行一些操作。
(1)攻击原理
- 攻击者诱导用户点击一个链接或访问一个网页,该链接或网页中包含了一个对目标网站的恶意请求。
- 由于该用户已经登录到目标网站,并且浏览器存储了相关的认证信息(比如Cookie),因此该恶意请求会被发送到目标网站,并以用户的身份执行。
(2)防御方法
- 使用Referer验证:检查请求的来源地址,确保请求来自合法的源。
- 使用Token验证:在关键操作中加入随机Token,确保请求的合法性。
- 验证用户身份:在执行敏感操作前,要求用户重新登录凭证或进行二次确认。
六、协议
1.主要协议
1)HTTP(超文本传输协议)-- 应用层
(1)作用
它使用TCP/IP作为传输层协议,通常运行在TCP之上,定义了客户端(浏览器)如何发起请求以获取Web页面其他资源,并指定服务器如何响应这些请求并传输所需的数据。
(2)特点
- 简单快速:客户端向服务器请求服务时,只需传送请求方法和路径,常用请求方法有GET、HEAD、POST、PUT、DELETE等。
- 灵活:允许传输任意类型的数据对象,传输类型由请求头部的Content-Type标注。
- 无连接:限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接,节省传输时间。
- 无状态:协议对于事务处理没有记忆能力,每个请求都是独立的。
(3)版本
常用版本是HTTP/1/1和HTTP/2。HTTP/1.1引入了持久连接、缓存控制、内容协商等特性,提高了传输效率和用户体验。HTTP/2进一步改进性能,通过多路复用、头部压缩等技术减少延迟和带宽占用。
2)HTTPS协议(安全超文本传输协议)
HTTPS是HTTP的安全版本,通过在HTTP协议的基础上加入SSL(安全套接层)/TLS(安全传输协议)加密层,提供了加密通信和数据完整性校验的功能。HTTPS协议广泛应用于在线支付、个人账户登录等需要保护隐私信息的场景。
HTTPS使用端口443,而HTTP使用端口80。
3)TCP/IP协议(传输控制协议/互联网协议)
TCP/IP是一个协议族,包含了多个网络协议。其中最核心的协议是IP(互联网协议)和TCP(传输控制协议),但除此之外,还包括诸如ICMP(互联网控制报文协议)、HTTP(超文本传输协议)、FTP(文件传输协议)、POP3(邮局协议)等多个协议。
TCP:传输控制协议,负责数据的分隔、传输和重组,确保数据在传输过程中的可靠性和有序性。
IP:互联网协议,负责将数据包送达目标设备。
(1)层次结构
TCP/IP协议族按照层次有上到下分成4层,分别是:
- 应用层:最高层,直接为用户的应用进程提供服务。常见的应用层协议有HTTP、FTP、SMTP、DNS等。
- 传输层:负责向两个主机中进程之间的通信提供服务。传输层协议主要有TCP和UDP(用户数据报协议)。TCP是面向连接的协议,提供可靠的报文传输和对上层应用的连接服务;而UDP则是面向无连接的协议,不保证报文的可靠传输,但具有较快的传输速度。
- 网络层(网际层):负责为数据包选择路由,并实现不同网络之间的逻辑地址(IP地址)的互访。网络层使用的协议主要是IP协议。
- 网络接口层(数据链路层):负责接收IP层的IP数据报,通过网络向外发送,或接收从网络上来的物理帧,抽出IP数据报,向IP层发送。该层是主机与网络的实际连接层,包括以太网、令牌环网等标准,负责网卡设备的驱动、帧同步、冲突检测、数据差错校验等工作。
(2)工作原理
- IP数据包的路由和转发:数据被分为数据包,每个数据包包含源和目标IP地址以及端口号。数据包从源计算机发送到目标计算机,中间经过路由器和交换机,路由器根据目标IP地址将数据包路由到下一跳,直到数据包到达目标计算机。
- TCP的可靠传输:TCP协议保证在IP数据包丢失时进行重发,能删除重复收到的IP数据包,还能准确地按原发送端的发送顺序重新组装数据。
- 数据封装与解封:在主机发送端,从传输层开始会把上一层的数据加上一个报头形成本层的数据,这个过程称为数据封装。在主机接收端,从最下层开始,每一层会去掉报头信息,该过程称为数据解封。
4)其余协议
(1)FTP(文件传输协议):客户端和服务器之间传输文件。
(2)SMTP(简单邮件传输协议):用于发送电子邮件。
(3)POP3(邮局协议):用于接收电子邮件。
(4)WebSocket:基于HTTP/3构建,提供全双工的实时数据传输,常用于在线聊天和实时应用。
七、内存泄漏
1.原因
1)意外的全局变量
全局变量生命周期最长,知道页面关闭前,它都存活着,所以全局变量上的内存一直都不会被回收,当全局变量使用不当,没有及时回收(手动复制null),或者拼写错误等将某个变量挂载到全局变量时,就会发生内存泄漏。
2)未清除的定时器
setTimeout和setInterval是由浏览器专门线程来维护其生命周期的。当在某个页面使用了定时器,而该页面销毁时,没有手动去释放清理这些定时器,那么定时器还是存活着的。定时器的生命周期并不挂靠在页面上,因此可能导致页面无法正常被回收,从而造成内存泄漏。
3)使用不当的闭包
当一个函数内再返回一个函数时,由于返回的函数持有外部函数的词法环境,而返回的函数又被其他生命周期更长的东西所持有,导致外部函数虽然执行完了,内存却无法回收。
4)DOM引用未被释放
当DOM元素被删除时,如果JavaScript中的引用仍然保留,那么这些DOM元素就无法被回收。例如,如果给某个DOM元素添加了事件监听器,但在元素被删除时没有移除监听器,那么这些监听器将继续引用该元素,导致内存泄露。
2.监控与调试
1)监控工具
Memory面板:用户捕捉和分析内存快照,查找未释放的内存对象。通过对比多个内存快照,可以发现哪些对象在内存中残留。
Timeline面板(Performance):用于检测页面性能问题,包括内存的使用趋势。通过内存使用的趋势图可以识别内存泄漏。
Console:通过手动调用JavaScript函数(如console.memory或performance.memory),查看内存使用情况。
3.常见解决方案
1)遗漏的事件监听器
给某个DOM元素添加了事件监听器,但在元素被删除时没有移除监听器。
解决:删除DOM元素前,移除对应的事件监听器。
2)闭包导致的内存泄露
闭包中引用了DOM元素或其他大对象,如果闭包函数长时间未被释放,这些引用对象也不会被回收。
解决:在不再需要使用闭包时,将变量设为null,以接触对大对象的引用。
3)全局变量和单例模式
解决:尽量减少全局变量的使用,将数据封装在模块或局部作用域中。使用ES6模块(import/export)组织代码,避免滥用全局变量。
4)DOM引用未被释放
解决:确保在删除DOM元素时,同时解除JavaScript对该元素的引用。