浏览器页面加载过程,越详细越好
浏览器加载页面的过程可以分为以下几个详细步骤:
- DNS 解析:
- 浏览器首先会解析域名,找到对应的服务器 IP 地址。
- TCP 连接:
- 浏览器与服务器建立 TCP 连接,通常通过三次握手完成。
- 发送 HTTP 请求:
- 浏览器向服务器发送 HTTP 请求,获取页面资源。
- 服务器响应:
- 服务器处理请求并返回 HTML 文档和其他资源(如 CSS、JavaScript、图片等)。
- 解析 HTML:
- 浏览器开始解析 HTML 文档,构建 DOM 树(Document Object Model)。
- 处理 CSS:
- 浏览器解析 CSS 文件,构建 CSSOM 树(CSS Object Model),并将 CSS 规则应用到相应的 DOM 节点上,生成渲染树(Render Tree)。
- 处理 JavaScript:
- 浏览器解析并执行 JavaScript 代码。JavaScript 可以操作 DOM 树和 CSSOM 树,影响页面的内容和样式。
- 布局(Layout):
- 浏览器根据渲染树计算每个节点的几何信息(位置和大小),这个过程称为布局或回流(Reflow)。
- 绘制(Painting):
- 浏览器将渲染树的每个节点绘制到屏幕上,显示最终的页面。
- 处理其他资源:
- 浏览器继续下载和处理页面中的其他资源,如图片、字体等。
- 执行异步任务:
- 浏览器处理异步任务,如异步 JavaScript(通过
async或defer属性加载的脚本)、延迟加载的图片等。
- 用户交互:
- 页面加载完成后,用户可以与页面进行交互,浏览器会根据用户操作更新页面内容。
通过以上详细步骤,浏览器将一个 HTML 文档解析并渲染成用户可以看到和交互的网页内容。每个步骤都可能涉及复杂的内部处理和优化,以确保页面能够快速、正确地加载和渲染。
浏览器是怎么解析 HTML 资源的
浏览器解析 HTML 资源的过程可以分为以下几个步骤:
-
读取 HTML 文件:浏览器从服务器获取 HTML 文件,并开始逐行读取文件内容。
-
词法分析 (Lexical Analysis):将 HTML 内容分解成一个个的标记 (Token),这些标记包括标签、属性、文本等。
-
语法分析 (Syntax Analysis):将标记转换成一个树状结构,称为“DOM 树”(Document Object Model)。在这个过程中,浏览器会根据 HTML 规范来验证标记的正确性,并处理错误。
-
构建 DOM 树:浏览器根据解析的标记构建 DOM 树,表示 HTML 文档的结构和内容。
-
处理 CSS:在解析 HTML 的过程中,浏览器会同时解析 CSS 文件,并将 CSS 规则应用到相应的 DOM 节点上,构建“渲染树”(Render Tree)。
-
处理 JavaScript:如果 HTML 中包含
<script>标签,浏览器会暂停 HTML 解析,先执行 JavaScript 代码。JavaScript 代码可以操作 DOM 树,修改页面内容。 -
布局 (Layout):浏览器根据渲染树计算每个节点的几何信息(位置和大小),这个过程称为布局或回流 (Reflow)。
-
绘制 (Painting):浏览器将渲染树的每个节点绘制到屏幕上,显示最终的页面。
通过以上步骤,浏览器将 HTML 资源解析并渲染成用户可以看到和交互的网页内容。
针对不同资源会怎么处理,比如遇到 CSS 资源、JS 资源
浏览器在解析 HTML 文档时,会根据不同的资源类型采取不同的处理方式:
- CSS 资源:
- 浏览器在遇到
<link>标签或嵌入的<style>标签时,会暂停构建 DOM 树,开始下载和解析 CSS 文件。 - 解析完成后,浏览器会将 CSS 规则应用到相应的 DOM 节点上,构建渲染树(Render Tree)。
- 渲染树构建完成后,浏览器会继续构建 DOM 树。
- JavaScript 资源:
- 浏览器在遇到
<script>标签时,会暂停 HTML 解析,开始下载和执行 JavaScript 文件。 - 如果
<script>标签没有async或defer属性,浏览器会在执行完 JavaScript 代码后继续解析 HTML。 - 如果
<script>标签有async属性,浏览器会异步下载 JavaScript 文件,并在下载完成后立即执行,不会阻塞 HTML 解析。 - 如果
<script>标签有defer属性,浏览器会异步下载 JavaScript 文件,并在 HTML 解析完成后再执行。
通过这些处理方式,浏览器能够有效地管理不同类型的资源,确保页面能够快速、正确地加载和渲染。
遇到图片会怎么处理
浏览器在解析 HTML 文档时,遇到图片资源会进行以下处理:
-
解析
<img>标签:浏览器在解析 HTML 时,遇到<img>标签会提取其src属性的值,这是图片的 URL。 -
请求图片资源:浏览器会根据图片的 URL 发送 HTTP 请求,从服务器获取图片资源。
-
占位符:在图片资源加载完成之前,浏览器会在页面上保留一个占位符,通常是一个空白区域,表示图片的位置。
-
加载图片:当图片资源下载完成后,浏览器会将图片解码并显示在占位符位置。
-
优化加载:为了优化图片加载,浏览器和开发者可以采取以下措施:
- 延迟加载 (Lazy Loading):使用
loading="lazy"属性,只有当图片进入视口时才加载,减少初始加载时间。 - 适应性图片:使用
srcset和sizes属性,根据设备的屏幕分辨率和视口大小加载合适的图片。 - 图片格式:使用现代图片格式如 WebP,减少文件大小,提高加载速度。
通过这些处理方式,浏览器能够有效地管理图片资源,确保页面能够快速、正确地加载和渲染。
DOM 解析?CSS 解析?JS 也需要解析,你觉得这几个是什么样的流程
- DOM 解析:
- 读取 HTML 文件:浏览器从服务器获取 HTML 文件,并开始逐行读取文件内容。
- 词法分析 (Lexical Analysis):将 HTML 内容分解成一个个的标记 (Token)。
- 语法分析 (Syntax Analysis):将标记转换成一个树状结构,称为“DOM 树”(Document Object Model)。
- 构建 DOM 树:浏览器根据解析的标记构建 DOM 树,表示 HTML 文档的结构和内容。
- CSS 解析:
- 读取 CSS 文件:浏览器在解析 HTML 时,遇到
<link>标签或嵌入的<style>标签,会暂停构建 DOM 树,开始下载和解析 CSS 文件。 - 解析 CSS:将 CSS 文件解析成 CSSOM(CSS Object Model)。
- 应用 CSS 规则:将 CSS 规则应用到相应的 DOM 节点上,构建“渲染树”(Render Tree)。
- JavaScript 解析:
- 读取 JavaScript 文件:浏览器在遇到
<script>标签时,会暂停 HTML 解析,开始下载和执行 JavaScript 文件。 - 执行 JavaScript:如果
<script>标签没有async或defer属性,浏览器会在执行完 JavaScript 代码后继续解析 HTML。 - 异步执行:如果
<script>标签有async属性,浏览器会异步下载 JavaScript 文件,并在下载完成后立即执行,不会阻塞 HTML 解析。如果有defer属性,浏览器会异步下载 JavaScript 文件,并在 HTML 解析完成后再执行。
通过这些解析流程,浏览器能够有效地管理和解析不同类型的资源,确保页面能够快速、正确地加载和渲染。
HTML,是怎么解析成 DOM 树
浏览器将 HTML 解析成 DOM 树的过程包括以下几个步骤:
-
读取 HTML 文件:浏览器从服务器获取 HTML 文件,并开始逐行读取文件内容。
-
词法分析 (Lexical Analysis):将 HTML 内容分解成一个个的标记 (Token),这些标记包括标签、属性、文本等。
-
语法分析 (Syntax Analysis):将标记转换成一个树状结构,称为“DOM 树”(Document Object Model)。在这个过程中,浏览器会根据 HTML 规范来验证标记的正确性,并处理错误。
-
构建 DOM 树:浏览器根据解析的标记构建 DOM 树,表示 HTML 文档的结构和内容。
-
处理 CSS:在解析 HTML 的过程中,浏览器会同时解析 CSS 文件,并将 CSS 规则应用到相应的 DOM 节点上,构建“渲染树”(Render Tree)。
-
处理 JavaScript:如果 HTML 中包含
<script>标签,浏览器会暂停 HTML 解析,先执行 JavaScript 代码。JavaScript 代码可以操作 DOM 树,修改页面内容。 -
布局 (Layout):浏览器根据渲染树计算每个节点的几何信息(位置和大小),这个过程称为布局或回流 (Reflow)。
-
绘制 (Painting):浏览器将渲染树的每个节点绘制到屏幕上,显示最终的页面。
通过以上步骤,浏览器将 HTML 资源解析并渲染成用户可以看到和交互的网页内容。
解析文档到渲染过程优化的点
-
减少阻塞渲染的资源:将 CSS 放在
<head>中,JavaScript 放在页面底部或使用async或defer属性,避免阻塞 HTML 解析和渲染。 -
使用内容分发网络 (CDN):将静态资源托管在 CDN 上,减少加载时间,提高资源的可用性和可靠性。
-
启用压缩:使用 Gzip 或 Brotli 压缩传输的 HTML、CSS 和 JavaScript 文件,减少文件大小,加快传输速度。
-
优化图片:使用适当的图片格式和分辨率,启用延迟加载 (lazy loading),减少初始加载时间。
-
减少 HTTP 请求:合并 CSS 和 JavaScript 文件,使用图标字体或 CSS Sprites 减少请求数量。
-
使用浏览器缓存:设置适当的缓存头,利用浏览器缓存机制,减少重复加载资源的时间。
-
优先关键渲染路径:优化首屏内容的加载顺序,确保关键内容尽快呈现给用户。
-
异步加载非关键资源:将非关键资源异步加载,避免阻塞关键渲染路径,提高页面加载速度。
-
使用服务端渲染 (SSR):对于动态内容,使用服务端渲染减少客户端渲染时间,提高首屏加载速度。
-
减少 DOM 操作:尽量减少和优化 DOM 操作,避免频繁的重排和重绘,提高渲染性能。
通过以上优化措施,可以显著提高网页的加载速度和渲染性能,提升用户体验。
performance 对象
performance 对象是浏览器提供的一个接口,用于获取关于当前页面性能的信息和测量时间。它提供了多种方法和属性,可以帮助开发者分析和优化网页性能。
常用属性和方法
performance.now():
- 返回一个高精度时间戳,表示从某个固定时间点(通常是页面加载开始)到当前时间的毫秒数。
- 示例:
const startTime = performance.now() // 执行一些操作 const endTime = performance.now() console.log(`操作耗时: ${endTime - startTime} 毫秒`)
performance.timing:
- 返回一个包含各种页面加载时间点的对象,可以用来分析页面加载的各个阶段耗时。
- 示例:
const timing = performance.timing console.log( `DNS 查询耗时: ${timing.domainLookupEnd - timing.domainLookupStart} 毫秒` ) console.log( `页面加载总耗时: ${timing.loadEventEnd - timing.navigationStart} 毫秒` )
performance.mark()和performance.measure():
performance.mark()用于创建一个命名的时间戳。performance.measure()用于测量两个标记之间的时间间隔。- 示例:
performance.mark('startTask') // 执行一些操作 performance.mark('endTask') performance.measure('taskDuration', 'startTask', 'endTask') const measure = performance.getEntriesByName('taskDuration')[0] console.log(`任务耗时: ${measure.duration} 毫秒`)
performance.getEntriesByType():
- 返回一个包含指定类型的性能条目的数组,如
navigation、resource、mark、measure等。 - 示例:
const resources = performance.getEntriesByType('resource') resources.forEach((resource) => { console.log(`资源 ${resource.name} 加载耗时: ${resource.duration} 毫秒`) })
通过使用 performance 对象,开发者可以更好地了解和优化网页的性能,提升用户体验。
const start = performance.now()
// 执行一些操作
const end = performance.now()
console.log(`操作耗时: ${end - start} 毫秒`)
performance.timing:返回一个 PerformanceTiming 对象,包含与页面加载相关的各种时间点信息,如重定向时间、DNS 查询时间、TCP 连接时间、请求响应时间等。
const timing = performance.timing
console.log(
`页面加载时间: ${timing.loadEventEnd - timing.navigationStart} 毫秒`
)
performance.mark()和performance.measure():用于创建自定义的性能标记和测量。performance.mark()用于创建一个标记点,performance.measure()用于测量两个标记点之间的时间间隔。
performance.mark('startTask')
// 执行一些操作
performance.mark('endTask')
performance.measure('taskDuration', 'startTask', 'endTask')
const measure = performance.getEntriesByName('taskDuration')[0]
console.log(`任务耗时: ${measure.duration} 毫秒`)
performance.getEntriesByType():返回一个包含指定类型的性能条目的数组,如 "navigation"、"resource"、"mark" 和 "measure" 等。
const resources = performance.getEntriesByType('resource')
resources.forEach((resource) => {
console.log(`资源 ${resource.name} 加载时间: ${resource.duration} 毫秒`)
})
通过使用 performance 对象,开发者可以深入了解网页的性能瓶颈,并采取相应的优化措施,提高用户体验。
优化长列表滚动效果
答: 优化长列表滚动效果可以通过以下几种方式实现:
- 虚拟滚动:只渲染可视区域内的元素,随着滚动动态加载和卸载元素。推荐使用如 React Virtualized 或 Vue Virtual Scroll List 等库。
- 懒加载:延迟加载列表中的图片或其他资源,减少初始加载时间。可以使用 Intersection Observer API 实现懒加载。
- 分片渲染:将长列表分成多个小块,逐步渲染,避免一次性渲染导致的卡顿。
- GPU 加速:使用 CSS 的
will-change属性或transform属性,启用 GPU 加速,提高滚动性能。
cookie/sessionStorage/localStorage 区别
- Cookie:
- 存储大小:约 4KB。
- 生命周期:可以设置过期时间,默认在浏览器关闭时失效。
- 作用域:同源的所有页面和服务器端都可以访问。
- 用途:主要用于会话管理、个性化设置和跟踪用户行为。
- 安全性:容易被劫持,建议使用
HttpOnly和Secure属性。
- sessionStorage:
- 存储大小:约 5MB。
- 生命周期:仅在当前会话(页面关闭)期间有效。
- 作用域:同源的单个页面。
- 用途:用于临时保存页面状态和数据。
- 安全性:相对安全,不会被跨页面访问。
- localStorage:
- 存储大小:约 5MB。
- 生命周期:永久保存,除非手动删除。
- 作用域:同源的所有页面。
- 用途:用于持久化保存较大数据量。
- 安全性:相对安全,但数据量大时需注意性能影响。
通过以上对比,可以根据具体需求选择合适的存储方式。
浏览器缓存
浏览器缓存是一种在客户端存储资源的机制,用于减少网络请求次数,加快页面加载速度。以下是一些常见的浏览器缓存策略和相关概念:
- 强缓存:
- Expires:HTTP 响应头,指定资源的过期时间。浏览器在过期时间之前不会再次请求该资源。
- Cache-Control:HTTP 响应头,提供更灵活的缓存控制,如
max-age指定资源的最大缓存时间,no-cache表示每次请求都必须向服务器验证资源的有效性。
- 协商缓存:
- Last-Modified:HTTP 响应头,表示资源的最后修改时间。浏览器在请求资源时会发送
If-Modified-Since头,服务器根据资源是否修改返回 304 状态码(未修改)或 200 状态码(已修改)。 - ETag:HTTP 响应头,表示资源的唯一标识符。浏览器在请求资源时会发送
If-None-Match头,服务器根据资源是否匹配返回 304 状态码(未修改)或 200 状态码(已修改)。
- 缓存位置:
- 内存缓存:资源存储在内存中,生命周期较短,适用于频繁访问的小资源。
- 磁盘缓存:资源存储在磁盘中,生命周期较长,适用于较大的资源。
- 缓存清理:
- 浏览器会根据缓存策略和存储空间自动清理过期或不常用的缓存资源。
- 用户也可以手动清理浏览器缓存,通过浏览器设置或开发者工具进行操作。
通过合理利用浏览器缓存,可以显著提高网页的加载速度和性能,提升用户体验。
跨域问题及处理
跨域问题是指浏览器出于安全考虑,阻止网页从一个域请求另一个域的资源。常见的跨域问题及其处理方法如下:
- 同源策略 (Same-Origin Policy):
- 浏览器的安全机制,限制从一个源加载的文档或脚本与另一个源的资源进行交互。
- 同源指的是协议、域名和端口号都相同。
- 跨域资源共享 (CORS):
- 服务器通过设置 HTTP 响应头
Access-Control-Allow-Origin来允许跨域请求。 - 其他相关头部包括
Access-Control-Allow-Methods、Access-Control-Allow-Headers等。
- JSONP (JSON with Padding):
- 通过
<script>标签进行跨域请求,服务器返回 JavaScript 代码并执行。 - 仅支持 GET 请求,存在安全风险。
- 代理服务器:
- 通过服务器端代理请求目标资源,再将结果返回给客户端。
- 服务器端处理跨域请求,避免浏览器的同源策略限制。
- WebSocket:
- 通过 WebSocket 协议进行双向通信,不受同源策略限制。
- 适用于实时通信场景。
- PostMessage:
- 使用
window.postMessage方法在不同窗口或 iframe 之间传递消息。 - 需要确保消息来源的安全性。
通过以上方法,可以有效解决跨域问题,确保资源的安全和正确加载。
XSS(跨站脚本攻击)
XSS(跨站脚本攻击)防范措施:
- 输入验证:对用户输入进行严格验证,过滤或转义特殊字符,防止恶意脚本注入。
- 输出编码:对输出到 HTML、JavaScript、CSS 等中的数据进行适当的编码,防止脚本执行。
- 内容安全策略 (CSP):配置 CSP 头,限制页面可以加载的资源,防止外部脚本执行。
- HttpOnly 和 Secure Cookie:设置 Cookie 的
HttpOnly属性,防止 JavaScript 访问 Cookie;设置Secure属性,确保 Cookie 仅通过 HTTPS 传输。 - 避免内联脚本:尽量避免使用内联 JavaScript,改用外部脚本文件。
CSRF(跨站请求伪造)防范措施
CSRF(跨站请求伪造)防范措施:
- CSRF Token:在表单提交或 AJAX 请求中加入唯一的 CSRF Token,并在服务器端验证该 Token 的有效性。
- SameSite Cookie:设置 Cookie 的
SameSite属性,限制跨站请求携带 Cookie。 - Referer 验证:在服务器端验证请求的 Referer 头,确保请求来自合法的页面。
- 双重提交 Cookie:在请求中同时提交 Cookie 和表单参数,并在服务器端验证两者的一致性。
- 用户权限验证:对敏感操作进行严格的权限验证,确保只有合法用户才能执行。
通过以上措施,可以有效防范 XSS 和 CSRF 攻击,提升应用的安全性。
中间人攻击(MitM)
中间人 (MitM) 攻击迫使攻击者拦截和操纵在两方之间传输的信息 当攻击者利用不安全的通信通道(通常通过公共 WiFi)时,就会发生这些攻击。这种攻击的受害者不会觉得自己受到了攻击,因为他们认为自己正在与服务器进行完全正常且安全的对话,而他们共享的信息在此过程中遭到监视或更改。
如何防止中间人攻击 主要有三步:
使用安全的互联网连接并注销不再使用的应用程序。 不要连接到你不知道的网络。例如,不要连接到咖啡馆提供的免费 WiFi。 使用 HTTPS 和 TLS 等安全通信协议对传输中的所有数据进行加密。
点击劫持
点击劫持(又名 — UI 纠正攻击)是一种欺骗机制,它会诱骗用户点击与他们认为完全不同的内容。 如图所示,它将隐藏元素覆盖在网站上合法可点击组件的顶部。在这种情况下,用户实际上点击了一个无意的元素,这可能会在未经他们同意的情况下触发攻击者的期望操作(比如转账)等意外操作。 如何防止点击劫持 为了减轻点击劫持攻击的潜在风险,可以使用的一种机制是使用 X-Frame-Options 标头,它可以确保你的网站不会嵌入到其他网站或 IFrame 中。
依赖性利用
依赖性利用是指攻击者通过利用软件或系统中的第三方库、框架或模块中的漏洞,来进行攻击的行为。以下是一些常见的依赖性利用方式及其防范措施:
- 已知漏洞的利用:
- 攻击者利用已知的第三方库或框架中的漏洞,进行攻击。
- 防范措施:定期更新依赖项,使用依赖性管理工具(如 npm、yarn、pip 等)检查并修复已知漏洞。
- 供应链攻击:
- 攻击者通过污染或替换合法的第三方库,传播恶意代码。
- 防范措施:使用可信的源和镜像,验证依赖项的完整性(如使用哈希值或签名)。
- 依赖混淆:
- 攻击者创建与合法库名称相似的恶意库,诱骗开发者误用。
- 防范措施:仔细检查依赖项的名称和来源,避免使用不熟悉或可疑的库。
- 依赖项注入:
- 攻击者通过注入恶意依赖项,利用依赖解析中的漏洞进行攻击。
- 防范措施:限制依赖项的来源,使用锁定文件(如 package-lock.json、Pipfile.lock)确保依赖项的一致性。
通过以上防范措施,可以有效降低依赖性利用的风险,确保软件和系统的安全性。依赖性利用