1、会进行 url 解析
为什么需要对URL进行解析(也就是编码)
网络标准规定 URL 必须使用 ASCII 字符集,它必须仅包含 ASCII 字符,即在 ASCII 字符集范围内的字符。ASCII 字符集包括数字、大写字母、小写字母和一些特殊字符如 -、_、.、~ 等,除此之外,还包括了一些保留字 : / ? # [ ] @ ! $ & ' ( ) * + , ; =。非 ASCII 字符,如中文、日文、希腊字母等,需要进行 URL 编码。
URL 在一般情况下应该使用 UTF-8 编码来确保对多语言字符的支持,它是一种通用的字符编码标准,支持几乎所有语言字符,包括中文、日文、韩文等。采用 UTF-8 编码可以确保 URL 能够正确表示和传输各种语言的字符,促进全球范围内的多语言应用和交流。并且 UTF-8 编码是一种安全的编码方式,能够避免安全漏洞和误解释。通过对 URL 中的特殊字符和非 ASCII 字符进行 UTF-8 编码,可以确保 URL 在传输和解析过程中不会引发歧义、安全问题或错误解析。
因为 URL 的编码方式有多种,我们无法确保浏览器使用 UTF-8 编码,我们可以采取以下几种措施来尽量保证 URL 的编码一致性:
- 声明字符编码: 在
HTML文档的头部中使用<meta>标签明确指定字符编码为UTF-8,例如:
URL编码规则
URL 编码的规则:简单来说,如果需要对一个字符进行 URL 编码,首先需要判断该字符是否是 ASCII 字符:
- 如果一个字符是 ASCII 字符,那么对该字符进行 URL 编码,首先需要把该字符的 ASCII 的值表示为两个 16 进制的数字,然后在其前面放置转义字符 %,就得到了该字符的 URL 编码结果。
- 如果一个字符是非 ASCII 字符,那么对该字符进行 URL 编码,首先需要使用指定的字符编码方式(建议使用 UTF-8 字符编码),将 “非 ASCII 字符” 编码为字节序列(字节序列即二进制数据);然后对其字节序列进行 URL 编码。URL 编码 “二进制数据”,首先需要把 “二进制数据” 表示为 8 位组的序列,将每个 8 位组表示为两个 16 进制的数字,然后在其前面放置转义字符 %,就得到了 “二进制数据” 的 URL 编码结果。
URL 编码和解码方法简介
-
encodeURI(): 这个方法用于对整个 URL 进行编码,包括协议、域名、路径和查询参数。它将非 ASCII 字符和特殊字符转换为十六进制格式,确保它们可以在 URL 中安全传输。但需要注意,保留字符(如:、/、?、&等)不会被编码,因为它们在 URL 中具有特殊含义。 -
decodeURI(): 与encodeURI()对应,decodeURI()用于解码已经被编码的 URL。它将十六进制格式的字符恢复为原始字符,使 URL 可以正常解析。 -
encodeURIComponent(): 与前两者不同,encodeURIComponent()主要用于编码查询参数等特定部分。除了非 ASCII 字符,它还会对保留字符进行编码,以确保不会被误解为 URL 的一部分。 -
decodeURIComponent():decodeURIComponent()用于解码使用encodeURIComponent()编码的部分。它将编码的字符还原为原始字符,使我们能够正确获取查询参数等信息。
URL编码和解码详细知识见:juejin.cn/post/727301…
2、根据 dns 系统进行 ip 查找
解析域名的过程如下:
- 浏览器缓存:浏览器首先会检查自己的缓存中是否有该域名对应的 IP 地址。如果有,就直接使用缓存中的 IP 地址,跳过后续步骤。
- 操作系统缓存:如果浏览器缓存中没有找到对应的 IP 地址,浏览器会检查操作系统的缓存(host文件)。操作系统也会缓存最近解析过的域名和对应的 IP 地址。
- 本地 DNS 解析器:如果缓存中没有找到对应的 IP 地址,浏览器会向本地 DNS 解析器(通常由 ISP 或网络提供商提供)发送 DNS 查询请求。本地 DNS 解析器是一个位于本地网络中的服务器,负责处理 DNS 查询请求。
- 递归查询:如果本地 DNS 解析器没有缓存该域名对应的 IP 地址,它会向根域名服务器发送查询请求。根域名服务器是全球 DNS 系统的顶级服务器,
前端的dns优化,可以在html页面头部写入dns缓存地址,比如:
<meta http-equiv="x-dns-prefetch-control" content="on" />
<link rel="dns-prefetch" href="http://bdimg.share.baidu.com" />
DNS 的三种查询方式:递归查询、迭代查询和混合查询
DNS查询模式知识见:juejin.cn/post/684490…
3、当查找到 IP 之后,接下来进行的就是 TCP 连接,以获取相关资源,如 HTML 和 CSS。
因为 TCP 是一种可靠的传输协议,而三次握手是建立 TCP 连接的过程。它确保了通信双方之间的同步和可靠性。下面是 TCP 三次握手的详细过程和原因:
- 第一次握手: 客户端向服务端发送一个带有
SYN标记的TCP数据包。该数据包中包含客户端的初始序列号和其他连接信息。 - 第二次握手: 服务器收到客户端请求后,将确认信号
ACK和SYN标记一起发送回客户端。服务器还会为客户端分配自己的初始序列号,并将其发送给客户端。 - 第三次握手: 客户端收到服务器的响应后,向服务器发送一个确认信号
ACK。客户端的确认信号中包含服务器分配的初始序列号加1的值。服务器收到客户端的确认信号后,进入已建立状态,客户端也进入已建立状态。现在,TCP连接已经建立,双方可以开始进行数据传输。
TCP 之所以采用三次握手,目的是确保客户端和服务器都能正确地建立起可靠的连接,并同步序列号以进行数据传输。通过三次握手,服务器可以确认客户端的接收能力和可用性,而客户端也可以确认服务器的接收能力和可用性。双方都能确保对方愿意接收和处理数据。
为什么两次握手不行
因为第二次握手,主机B还不能确认主机A已经收到确认请求,也是说B认为建立好连接,开始发数据了,结果发出去的包一直A都没收到,那攻击B就很容易了,我专门发包不接收,服务器很容易就挂了。
从网卡把数据包传输出去到服务器发生了什么
-
先从局域网把数据发送到公司的交换机(如果交换机没有缓存本地mac地址和IP地址的映射,此时会通过ARP协议来获得),交换机的好处是可以隔离冲突域(因为以太网用的是CSMA/CD协议,这个协议规定网线上同一时刻只能有一台机器发送数据),这样就可以不仅仅同一时刻只有一台机器发送网络包了
-
然后交换机再将数据发送到路由器,路由器相当于公司网关(我们公司小),路由器具有转发和分组数据包的功能(路由器通过选定的路由协议会构造出路由表,同时不定期的跟相邻路由器交换路由信息),然后这算是经过了物理层,数据链路层(以太网),开始到网络层进行数据转发了
-
然后路由器转发IP数据报,一般公司的IP地址都会经过NAT转换,让内网的ip也能够访问外网,我们公司我注意了一下是192.168打头的内网ip地址。通过路由器的分组传输,所有数据到达服务器。
-
然后服务器的上层协议传输层协议开始发挥作用,根据tcp包里的端口号,让服务器特定的服务来处理到来的数据包,并且tcp是面向字节流的(tcp有四大特性,可靠传输、流量控制、拥塞控制、连接管理),所以我们node的request对象,它的监听事件data事件为什么要用字符串一起拼接起来呢(buffer),就是因为tcp本身就是字节流,request对象使用的data(http层面)是tcp传来的数据块。
-
最后数据由传输层转交给应用层,也就是http服务(或者https),后端经过一系列逻辑处理,返回给前端数据。
4、三次握手之后建立完链接,就该请求html文件了,如果html文件在缓存里面浏览器直接返回,如果没有,就去服务器获取
HTTP 缓存:浏览器会缓存已经访问过的网页资源,如 HTML、CSS、JavaScript 文件、图像等。当再次请求相同的资源时,浏览器可以直接从缓存中获取,而不需要再次下载。
- 强缓存:通过设置响应头中的
Cache-Control或Expires字段,服务器可以指定资源的缓存时间。在缓存时间内,浏览器会直接从缓存中获取资源,而不发送请求到服务器。 - 协商缓存:通过设置响应头中的
Last-Modified和ETag字段,服务器可以提供资源的标识符。在下次请求资源时,浏览器会发送If-Modified-Since或If-None-Match字段到服务器,如果资源未发生变化,服务器会返回 304 状态码,告诉浏览器可以使用缓存的资源。
在 HTTP 缓存中,"from disk cache" 和 "from memory cache" 是两种不同的缓存位置,用于存储已经请求过的资源。
- "from disk cache"(来自磁盘缓存):当浏览器第一次请求一个资源时,服务器会返回该资源,并将其缓存到磁盘上。当下次请求相同的资源时,浏览器会首先检查磁盘缓存中是否存在该资源的副本。如果存在,浏览器会直接从磁盘缓存中读取资源,而不需要再次向服务器发送请求。这样可以减少网络请求,提高页面加载速度。
- "from memory cache"(来自内存缓存):当浏览器第一次请求一个资源时,服务器返回该资源,并将其缓存到内存中。当下次请求相同的资源时,浏览器会首先检查内存缓存中是否存在该资源的副本。如果存在,浏览器会直接从内存缓存中读取资源,而不需要再次向服务器发送请求。由于内存的读取速度比磁盘快得多,因此从内存缓存中读取资源的速度更快。
通常情况下,浏览器会优先从内存缓存中读取资源,因为内存缓存的读取速度更快。如果资源在内存缓存中不存在,浏览器会尝试从磁盘缓存中读取资源。如果资源在磁盘缓存中也不存在,浏览器才会向服务器发送请求获取资源。
需要注意的是,缓存的有效性是由服务器端的响应头控制的。服务器可以通过设置响应头中的缓存相关字段(如 Cache-Control、Expires、Last-Modified、ETag 等)来指定资源的缓存策略和有效期。浏览器在接收到响应时会根据这些字段来确定是否缓存该资源以及缓存的位置(内存缓存或磁盘缓存)。
当资源在内存缓存或磁盘缓存中被命中时,浏览器会在网络请求中显示 "from memory cache" 或 "from disk cache"。这表示浏览器从相应的缓存位置中获取了资源,而不是从服务器重新下载。
总结起来,"from disk cache" 表示资源来自磁盘缓存,"from memory cache" 表示资源来自内存缓存。浏览器会根据服务器的响应头和缓存策略来决定是否缓存资源以及缓存的位置。通过合理的缓存设置,可以提高页面加载速度和减少网络请求。
启发式缓存
启发式缓存(Heuristic caching)是一种基于启发式算法的缓存策略,用于在缺乏明确缓存指令的情况下,根据一些启发式规则来决定是否缓存资源以及缓存的有效期。启发式缓存是一种基于经验和启发规则的近似缓存策略,并不是一种精确的缓存控制方法。
当服务器未提供明确的缓存相关字段(如 Cache-Control、Expires、Last-Modified、ETag 等)时,浏览器可以使用启发式缓存来尝试根据一些规则来判断资源是否可以缓存以及缓存的有效期。
启发式缓存通常基于以下规则进行判断:
- 文件类型:某些文件类型(如图片、CSS、JavaScript 等)通常具有较稳定的内容,可以被长时间缓存。而动态生成的内容(如 HTML 页面)则可能需要更频繁地从服务器获取最新数据,因此不适合长时间缓存。
- 文件大小:较大的文件可能需要更频繁地从服务器获取最新版本,以确保及时更新。因此,较大的文件可能会被设置为不缓存或缓存时间较短。
- 用户行为:浏览器可能会根据用户的浏览行为来调整缓存策略。例如,如果用户经常访问某个网站的某个资源,浏览器可能会将该资源缓存更长时间,以提高用户体验。
5、返回html之后,会解析html
-
构建DOM树(DOM tree):从上到下解析HTML文档生成DOM节点树(DOM tree),也叫内容树(content tree);
-
构建CSSOM(CSS Object Model)树:加载解析样式生成CSSOM树;
-
执行JavaScript:加载并执行JavaScript代码(包括内联代码或外联JavaScript文件);
-
构建渲染树(render tree):根据DOM树和CSSOM树,生成渲染树(render tree);
-
渲染树:按顺序展示在屏幕上的一系列矩形,这些矩形带有字体,颜色和尺寸等视觉属性。
-
布局(layout):浏览器会根据渲染树中的节点计算它们在屏幕上的准确位置和大小。
-
绘制(painting):遍历渲染树绘制所有节点,为每一个节点适用对应的样式,这一过程是通过UI后端模块完成
页面渲染优化
以下是一些常见的前端页面渲染优化技巧:
- 减少 HTTP 请求:合并和压缩 CSS 和 JavaScript 文件,使用 CSS Sprites 将多个图像合并为一个,减少页面中的外部资源请求次数。
- 优化图像加载:使用适当的图像格式(如 JPEG、PNG、WebP)和压缩级别,根据需要加载适当大小的图像,使用懒加载或延迟加载技术加载图像。
- 使用浏览器缓存:通过设置适当的缓存相关字段(如
Cache-Control、Expires)来启用浏览器缓存,减少重复请求和资源下载时间。 - 异步加载资源:将不影响页面渲染的 JavaScript 文件和其他资源(如广告、分析脚本等)使用异步加载或延迟加载技术,以避免阻塞页面渲染。
- 优化 CSS 和 JavaScript:合理组织和压缩 CSS 和 JavaScript 代码,避免不必要的重复代码和冗余资源,使用代码分割和按需加载技术,只加载当前页面所需的代码。
- 使用缓存技术:使用缓存技术(如 Redis、Memcached)缓存动态生成的内容,减少数据库查询和服务器计算,提高响应速度。
- 优化页面布局和渲染:避免使用过多的嵌套和复杂的布局结构,减少不必要的 DOM 操作,使用 CSS3 动画代替 JavaScript 动画,避免频繁的重绘和回流。
- 使用响应式设计:使用响应式设计技术,根据设备的屏幕大小和分辨率,自动调整页面布局和样式,提供更好的用户体验。
- 使用服务端渲染(SSR) :对于需要较快的首次加载和更好的 SEO 的应用程序,可以考虑使用服务端渲染。SSR 将页面的初始渲染工作放在服务器端完成,将渲染好的 HTML 直接发送给客户端,减少客户端的渲染时间。
- 懒加载和无限滚动:对于页面中的长列表或大量图片,可以使用懒加载和无限滚动技术。懒加载延迟加载页面中的内容,只在用户滚动到可见区域时加载,减少初始加载时间。无限滚动则是在用户滚动到页面底部时自动加载更多内容,提供流畅的浏览体验。
- 使用 Web Workers:对于需要进行复杂计算或处理大量数据的任务,可以使用 Web Workers 将这些任务放在后台线程中进行,避免阻塞主线程,提高页面的响应性能。
- 优化字体加载:使用适当的字体格式(如 WOFF、WOFF2)和字体子集,避免加载不必要的字体文件,使用字体预加载技术,以提高字体加载的效率。
- 使用 CDN 加速:使用内容分发网络(CDN)来加速静态资源的加载,将资源缓存在离用户更近的服务器上,减少网络延迟和提高加载速度。
- 监测和优化性能:使用性能监测工具(如 Lighthouse、WebPageTest)来评估页面的性能指标,包括加载时间、渲染时间、资源大小等。根据监测结果,针对性地进行优化,找出性能瓶颈并进行改进。
- 使用缓存技术:利用浏览器缓存、CDN 缓存、服务器端缓存等技术,缓存静态资源和动态内容,减少重复请求和数据传输,提高页面加载速度。
- 优化网络请求:减少不必要的网络请求,合并和压缩文件,使用 HTTP/2 或 HTTP/3 协议来提高请求的并发性和效率,使用资源预加载和预连接技术来提前获取关键资源。
- 使用响应式图片:根据设备的屏幕大小和分辨率,使用适当大小和格式的图片,避免加载过大的图片,使用
<picture>元素和srcset属性来提供不同尺寸和分辨率的图片选择。 - 避免重复渲染:避免在每次数据变化时重新渲染整个页面,使用虚拟 DOM 或轻量级的状态管理库来进行局部更新,提高渲染效率。
- 延迟加载和按需加载:将非关键的资源延迟加载或按需加载,只在需要时才加载,减少初始加载时间和资源占用。
- 优化动画和过渡效果:使用 CSS3 动画或硬件加速的 CSS 属性来实现平滑的动画效果,避免使用 JavaScript 实现复杂的动画,以提高性能和流畅度。
写代码时的优化点:
-
HTML文档结构层次尽量少,最好不深于六层;
-
脚本尽量后放,放在前即可;
-
少量首屏样式内联放在标签内;
-
样式结构层次尽量简单;
-
在脚本中尽量减少DOM操作,尽量缓存访问DOM的样式信息,避免过度触发回流;
-
减少通过JavaScript代码修改元素样式,尽量使用修改class名方式操作样式或动画;
-
动画尽量使用在绝对定位或固定定位的元素上;
-
隐藏在屏幕外,或在页面滚动时,尽量停止动画;
-
尽量缓存DOM查找,查找器尽量简洁;
-
涉及多域名的网站,可以开启域名预解析
前端性能优化之利用 Chrome Dev Tools 进行页面性能分析
具体可看: zhuanlan.zhihu.com/p/105561186
上图是 Chrome Dev Tools 的一个截图,其中,我认为能用于进行页面性能快速分析的主要是图中圈出来的几个模块功能,这里简单介绍一下:
- Network : 页面中各种资源请求的情况,这里能看到资源的名称、状态、使用的协议(http1/http2/quic...)、资源类型、资源大小、资源时间线等情况
- Performance : 页面各项性能指标的火焰图,这里能看到白屏时间、FPS、资源加载时间线、longtask、内存变化曲线等等信息
- Memory : 可以记录某个时刻的页面内存情况,一般用于分析内存泄露
- JavaScript Profiler : 可以记录函数的耗时情况,方便找出耗时较多的函数
- Layers : 展示页面中的分层情况
前端性能优化工具 Lighthouse
详细可查看:cloud.tencent.com/developer/a…
Lighthouse 是 Google 提供的开源工具,用于评估和优化网站性能。它可以分析网站的多个方面,包括性能、可访问性、最佳实践以及 SEO 等。
结果分析与优化建议
-
Performance(性能) :
- Largest Contentful Paint (LCP) :确保主要内容在 2.5 秒内加载。优化图像大小,使用延迟加载。
- First Input Delay (FID) :确保交互响应时间小于 100 毫秒。减少第三方脚本的影响。
- Cumulative Layout Shift (CLS) :确保页面布局稳定,避免使用未定义尺寸的图像和广告。
-
优化资源加载:
- 压缩和最小化:压缩 HTML、CSS、JS 文件,使用工具如 Terser、CSSNano。
- 使用现代格式:例如 WebP 格式的图片。
- 开启 Gzip 或 Brotli 压缩。
-
提高网络性能:
- 使用 CDN:分发内容以缩短传输距离。
- 减少 HTTP 请求:合并 CSS 和 JS 文件,使用 CSS sprites。
-
代码拆分:
- 通过 Webpack 等工具进行代码拆分,确保只加载必要的代码。
-
缓存策略:
- 使用浏览器缓存和服务端缓存,配置合适的缓存头。
-
懒加载:
- 对图像和非关键性资源使用懒加载技术。
-
异步加载脚本:
- 使用
async和defer属性异步加载非关键性 JavaScript 文件。
- 使用