Fork me on GitHub

前端面试套题系列(第六篇)

1、详细展开讲讲前端的渲染过程

前端的渲染过程主要涉及到以下几个步骤:

  1. 构建 DOM(Document Object Model)树:浏览器将 HTML 文件解析成 DOM 树的过程称为构建 DOM 树。DOM 树表示了 HTML 文档的结构,每个 HTML 元素都被表示为一个节点,它们根据嵌套关系形成层次结构。

  2. 构建 CSSOM(CSS Object Model)树:浏览器解析 CSS 样式表,并将其转换为 CSSOM 树。CSSOM 树表示了样式规则的层次结构,每个 CSS 规则对应一个节点。

  3. 合并 DOM 和 CSSOM 生成 Render 树:将 DOM 树和 CSSOM 树合并生成 Render 树(也称为渲染树)。Render 树只包含需要显示在页面上的可见元素,它是一种将 DOM 元素和 CSS 样式进行匹配的结构。

  4. 布局(Layout):根据 Render 树中每个元素的大小、位置等信息,计算出它们在页面上的准确位置。布局过程也被称为回流(Reflow)。

  5. 绘制(Painting):根据布局阶段得到的准确位置,将 Render 树中的元素绘制到屏幕上,形成最终的页面呈现效果。

  6. 栅格化(Rasterization):将页面划分为一系列的像素,并对页面进行分层处理。每个图层都会被单独栅格化,并最终合成到屏幕上。

  7. 合成与显示:浏览器将栅格化后的图层按照正确的顺序合成,最终显示在用户的屏幕上。这个过程使用 GPU(Graphics Processing Unit)来加速绘制和合成操作。

需要注意的是,渲染过程是一个逐步执行的过程,每个阶段都依赖于前一个阶段的结果。如果发生 DOM 或 CSSOM 的修改,浏览器可能需要重新执行从布局到合成的整个渲染流程。

此外,现代浏览器还优化了渲染过程,引入了一些概念,如异步渲染、增量渲染和懒加载等,以提高页面加载和渲染的性能。

 

2、页面上放一个按钮,做了一个点击事件,浏览器是怎么处理这个过程的

当页面上的按钮被点击时,浏览器会按照以下过程处理该事件:

  1. 捕获阶段(Capture Phase):浏览器首先从文档的根节点开始,向下逐级检查DOM树中是否存在绑定了点击事件的元素。这个过程从根节点开始,一直传播到触发事件的最具体的元素。

  2. 目标阶段(Target Phase):一旦找到了绑定了点击事件的目标元素,浏览器会执行该元素上与点击事件相关的监听函数。这个阶段表示事件已经到达了目标元素。

  3. 冒泡阶段(Bubble Phase):事件继续向上冒泡回到文档的根节点。在冒泡过程中,如果父元素也绑定了相同类型的点击事件,则会执行父元素上的相应监听函数。这样一直冒泡到根节点,直到结束。

注意事项:

  • 如果在捕获或冒泡过程的某个阶段中,某个元素的事件监听函数调用了 event.stopPropagation() 方法,那么事件传播将会被终止,后续的监听函数将不再执行。
  • 如果在某个阶段绑定了多个相同类型的事件监听函数,它们会按照绑定的顺序进行执行。
  • 默认情况下,点击事件会在冒泡阶段才触发。如果想要在目标阶段触发事件,可以使用 addEventListener 方法的第三个参数设为 true

总结起来,浏览器通过捕获阶段、目标阶段和冒泡阶段来处理页面上按钮的点击事件。这一过程使得事件可以在DOM树中进行传播,并且可以在多个元素上进行监听和响应。

3、WebRTC的低时延是怎么做到的

WebRTC(Web实时通信)通过几种方式实现低时延:

  1. P2P(点对点)通信:WebRTC通过直接建立点对点连接,绕过传统的中间服务器,减少了数据传输的中间环节,从而降低了时延。这意味着数据可以直接从发送方发送到接收方,而不需要经过额外的中转节点。

  2. UDP传输:WebRTC使用UDP(用户数据报协议)作为底层传输协议,而不是常用的TCP(传输控制协议)。相比TCP,UDP具有较低的传输延迟,因为它不需要进行连接建立、数据分段和确认等操作。UDP更适合实时通信场景,可以提供更低的时延。

  3. 优化的编解码器:WebRTC使用优化的音频和视频编解码器,如VP8、VP9和Opus等,这些编解码器具有高效的压缩算法,可以在保证较好质量的情况下减小数据传输量,从而减少了传输的延迟。

  4. ICE(Interactive Connectivity Establishment):WebRTC使用ICE框架来建立点对点连接。ICE利用STUN(会话遍历工具)和TURN(中继转发)服务器,自动选择最佳的传输路径,避免了网络中断和连接问题,从而减少了时延。

  5. 非阻塞I/O:WebRTC使用非阻塞I/O操作,这意味着在进行网络通信时,不会因为等待响应而阻塞其他操作,有效提高了数据传输的效率和响应速度。

综上所述,WebRTC通过P2P通信、UDP传输、优化的编解码器、ICE框架和非阻塞I/O等方式实现了较低的时延,使其非常适合实时通信和视频会议等场景。

 

4、什么情况下会用loadbalance?什么场景下会用nginx

在前端开发中,load balancing(负载均衡)和 Nginx 在以下情况下被广泛应用:

  1. 负载均衡(Load Balancing):

    • 高并发流量:当网站或应用程序面对大量请求时,单个服务器可能无法处理全部的负载。这时可以使用负载均衡来分发请求到多个服务器上,以平衡负载并提高系统的性能和可伸缩性。
    • 高可用性:通过将流量分发到多个服务器上,即使其中一台服务器出现故障或不可用,其他正常运行的服务器仍可继续提供服务。这样可以提高系统的可用性和容错能力。
  2. Nginx 的使用场景:

    • 反向代理(Reverse Proxy):Nginx 可以作为反向代理服务器,将客户端的请求转发给后端的多个服务器,实现负载均衡、缓存、SSL加密等功能。它能够根据不同的请求进行智能的流量分发,提高网站的性能和稳定性。
    • 静态文件服务:Nginx 非常擅长处理静态文件,比如图片、CSS、JavaScript等。通过配置 Nginx 作为静态文件服务器,可以提供高效的静态资源访问,减轻后端服务器的负载。
    • 缓存代理(Caching Proxy):Nginx 可以缓存后端服务器返回的内容,并在后续请求中直接提供缓存的响应,减少了对后端服务器的访问压力,提高了响应速度。
    • SSL/TLS 终结代理(SSL/TLS Termination):Nginx 可以用作负责处理客户端和服务器之间的 SSL/TLS 加密和解密。这样,后端服务器可以专注于处理非加密的请求,提高了性能并简化了服务器配置。

需要注意的是,负载均衡和 Nginx 并不是互斥的,实际场景中常常结合使用。Nginx 可以作为负载均衡器,将请求分发给后端多台服务器,实现负载均衡的功能。

 

5、相同域名下,HTTP2为什么能突破浏览器的并发连接数限制?

在不改变HTTP1.x的语义、方法、状态码、URL以及首部字段的情况下,HTTP2.0是怎样突破HTTP1.1的性能限制,改进传输性能,实现低延迟高吞吐量的呢?
关键之一就是在应用层(HTTP)和传输层(TCP)之间增加一个二进制分帧层。
在整理二进制分帧及其作用的时候我们先来铺垫一点关于帧的知识:
帧:HTTP2.0通信的最小单位,所有帧都共享一个8字节的首部,其中包含帧的长度、类型、标志、还有一个保留位,并且至少有标识出当前帧所属的流的标识符,帧承载着特定类型的数据,如HTTP首部、负荷、等等。
消息:比帧大的通讯单位,是指逻辑上的HTTP消息,比如请求、响应等。由一个或多个帧组成
流:比消息大的通讯单位。是TCP连接中的一个虚拟通道,可以承载双向的消息。每个流都有一个唯一的整数标识符
HTTP2.0中所有加强性能的核心是二进制传输,在HTTP1.x中,我们是通过文本的方式传输数据。基于文本的方式传输数据存在很多缺陷,文本的表现形式有多样性,因此要做到健壮性考虑的场景必然有很多,但是二进制则不同,只有0和1的组合,因此选择了二进制传输,实现方便且健壮。
在HTTP2.0中引入了新的编码机制,所有传输的数据都会被分割,并采用二进制格式编码。
 

6、HTTP2相对HTTP1做了哪些优化的工作,HTTP2首部压缩压缩了什么

HTTP/2 相对于 HTTP/1 做了以下几个主要的优化工作:

  1. 多路复用(Multiplexing):HTTP/1 使用序列化和阻塞的方式进行请求响应,每个请求需要等待前一个请求的响应完成才能发送下一个请求。而 HTTP/2 使用二进制分帧层,将请求和响应拆分为多个帧,并对这些帧进行并行传输。这样可以实现多个请求和响应的并发传输,避免了 HTTP/1 的队头阻塞问题,提高了性能和效率。

  2. 头部压缩(Header Compression):HTTP/2 使用 HPACK 算法对请求和响应的头部进行压缩。在传输过程中,使用静态字典和动态字典来减少重复的头部信息,从而减小了传输大小,降低了延迟。

  3. 服务器推送(Server Push):HTTP/2 允许服务器主动向客户端推送与当前请求相关的资源,无需客户端明确请求。这样可以减少客户端发起的额外请求,提前将一些可能需要的资源推送给客户端,减少页面加载时间。

  4. 流量控制(Flow Control):HTTP/2 引入了流量控制机制,允许客户端和服务器控制数据的传输速率,避免了发送方过载接收方的问题。

  5. 二进制传输(Binary Framing):HTTP/2 使用二进制协议传输数据,与 HTTP/1 的文本协议相比,二进制传输更高效,解析和处理速度更快。

  6. 首部优先级(Header Prioritization):HTTP/2 允许为每个请求设置优先级,以确保重要资源的优先加载,提高用户体验。

综上所述,HTTP/2 在多路复用、头部压缩、服务器推送、流量控制、二进制传输和首部优先级等方面进行了优化,通过减少延迟、提高并发性能,从而提升了网页加载速度和用户体验。

 

HPACK压缩主要针对以下两个方面进行了优化:

  1. 首部字段重复数据:在HTTP请求和响应中,往往会出现大量的重复首部字段,例如User-Agent、Cookie等。HPACK使用静态表和动态表来存储这些重复的首部字段,将其转换为索引值,然后传输索引值来代替原始的首部字段,从而减少了数据的传输量。

  2. 首部字段名称压缩:HTTP首部字段名称在每个帧中都需要发送,而字段名称通常是相对较长的字符串。HPACK通过使用静态表和动态表来编码首部字段名称,并使用索引值来表示这些字段名称。这样可以大大减小传输的数据量。

HPACK算法通过使用静态表和动态表来存储和传输首部字段的索引值,从而实现了高效的首部压缩。这种压缩机制可以显著减小数据包的大小,降低网络延迟,并提升网络性能。

需要注意的是,HPACK只压缩了HTTP请求和响应中的首部信息,而不会对消息体进行压缩。消息体的压缩通常交由其他压缩算法(如Gzip)来处理,以进一步减小数据传输的大小。

 

7、假如打开一个网页,内存和CPU占用极高,你会怎么排查并解决

按照以下步骤进行排查和解决:

  1. 使用浏览器的开发者工具:打开浏览器的开发者工具(一般是通过按F12键或右键选择"检查"打开),切换到"性能"或"任务管理器"选项卡,观察CPU和内存使用情况。检查是否有异常的资源请求、内存泄漏或大量的计算操作。

  2. 检查页面中的代码和资源:查看页面中是否存在大量的JavaScript代码、复杂的CSS样式或大图像等资源。这些可能会导致资源加载过多、渲染耗时或占用过多的内存。优化这些问题可以通过代码压缩、减少资源请求、图片优化等方式来处理。

  3. 检查JavaScript性能问题:可能存在JavaScript代码执行效率低下的情况,导致CPU占用过高。可以通过浏览器的开发者工具的"性能"选项卡进行性能分析,找出执行时间较长的函数或代码块。优化JavaScript代码可以使用合适的数据结构、避免重复计算、尽量减少DOM操作等方式。

  4. 检查内存泄漏问题:内存泄漏可能导致页面占用过多的内存而不释放,最终导致浏览器崩溃或卡顿。可以使用开发者工具的"内存"选项卡来识别是否存在内存泄漏问题。需要注意及时释放不再使用的对象、避免循环引用、合理使用闭包等。

  5. 使用性能分析工具:除了浏览器自带的开发者工具,还可以使用一些第三方性能分析工具来帮助排查问题。例如,Google Chrome提供了Lighthouse、PageSpeed Insights等工具,可以评估页面性能并给出优化建议。

  6. 逐步排查和测试:根据以上步骤进行问题排查时,可以逐步注释或删除代码段,然后重新打开页面,观察是否仍然出现高内存和CPU占用。通过逐步排查可以找出具体引起问题的代码片段或资源。

  7. 优化和改进:根据排查结果,对出现问题的代码进行优化和改进。可以采取各种手段,如减少资源请求、异步加载、懒加载、节流防抖等,以降低内存和CPU占用。

通过以上步骤的排查与解决,可以找到具体问题的源头,并对问题进行优化和改进,以提升网页的性能和用户体验。

 

8、讲下什么是冒泡,捕获?它们有什么区别?如何取消事件冒泡、事件捕获、默认事件行为。

 

冒泡(Bubbling)和捕获(Capturing)是JavaScript事件模型中的两个基本概念,用于描述事件在DOM树中的传播方式。

冒泡:当一个元素触发了某个事件(如点击事件),该事件将从最具体的元素开始向上冒泡,逐级触发父级元素的相同事件,直到达到DOM树的根节点或手动停止冒泡。

捕获:与冒泡相反,捕获是从DOM树根节点开始,按照从外到内(由上至下)的顺序,依次触发各个祖先元素上相同的事件,然后再到最具体的子元素上触发相同事件。

区别:

  • 冒泡是由事件目标元素向父级元素逐级触发事件,而捕获是由DOM树的根节点向目标元素逐级触发事件。
  • 冒泡是从内向外的传播方式,捕获是从外向内的传播方式。

取消事件冒泡、事件捕获、默认事件行为的方法如下:

  1. 取消事件冒泡: 在事件处理函数中调用 event.stopPropagation() 方法可以阻止事件进一步向上冒泡,即停止其传播到父级元素。注意,该方法只能阻止冒泡,无法阻止捕获阶段的触发。

  2. 取消事件捕获: 在事件处理函数中调用 event.stopPropagation() 方法同样可以阻止事件在捕获阶段的传播。和冒泡一样,该方法只能阻止捕获阶段的传播,无法阻止冒泡阶段的触发。

  3. 取消默认事件行为: 在事件处理函数中调用 event.preventDefault() 方法可以取消事件的默认行为。例如,点击链接时阻止跳转、按下键盘时阻止输入等。需要注意的是,并非所有的事件都有默认行为。

需要注意的是,事件处理函数中使用 event.stopPropagation()event.preventDefault() 方法时,event 参数是事件对象,它是在事件发生时自动传递给事件处理函数的。

 

9、绑定事件的方法有多少种;怎么理解事件委托,事件派发?

绑定事件的方法有多种,常见的包括:

  1. HTML属性:可以在HTML元素上使用内联事件处理程序,例如<button onclick="handleClick()">点击</button>

  2. DOM属性:可以通过给DOM元素对象的属性赋值来添加事件处理程序,例如element.onclick = handleClick

  3. addEventListener():是一种更灵活和推荐的方法,它可以在一个元素上添加多个事件处理程序,还可以控制事件的捕获或冒泡阶段。例如:

    element.addEventListener('click', handleClick);
  4. jQuery的事件绑定:如果你使用jQuery库,可以使用它提供的事件绑定方法,如$(element).on('click', handleClick)

事件委托(Event Delegation)是一种利用事件冒泡原理的技术,将事件的处理委托给其父级元素或更高层级的元素。通过将事件绑定到父级元素上,可以避免给每个子元素都绑定事件处理程序,减少内存消耗,并且对于动态生成的元素也能有效地处理事件。

事件委托的实现步骤如下:

  1. 将事件绑定到父级元素上,可以使用addEventListener()等方法。
  2. 在事件触发时,通过事件对象的target属性获取真正触发事件的元素。
  3. 根据触发元素的特征或其他条件,执行相应的操作。

事件派发(Event Dispatching)是指通过手动触发事件的方式来模拟实际的用户交互。可以使用JavaScript中的 dispatchEvent() 方法手动创建并触发事件对象。

例如,要触发一个点击事件:

var event = new MouseEvent('click', {
  bubbles: true,
  cancelable: true,
  view: window
});
element.dispatchEvent(event);

通过事件派发,可以在程序中模拟各种交互场景,方便进行自动化测试、事件模拟等操作。 

 

10、讲下什么是强缓存,协商缓存,它们之间有什么区别?协商缓存返回的状态码是多少?什么情况下进行协商缓存?用户请求的资源在强缓存未过期的情况下,如何更新?

缓存(Strong Caching)和协商缓存(Conditional Caching)是HTTP中用于控制缓存的机制。

  1. 强缓存:浏览器在接收到服务端返回的响应时,根据响应头中的缓存标识(如Cache-Control、Expires)判断是否命中强缓存。如果命中强缓存,浏览器直接从本地缓存中获取资源,不发送请求到服务端。

  2. 协商缓存:如果未命中强缓存,浏览器将向服务端发送请求,并携带上一次请求返回的响应头信息(如If-Modified-Since、If-None-Match)。服务端通过比较这些请求头与当前资源的相关信息,来决定是否命中协商缓存。如果命中协商缓存,服务端返回304 Not Modified状态码,并在响应头中指示浏览器可以使用本地缓存;否则,返回新的资源内容和200 OK状态码。

区别:

  • 强缓存由响应头中的Cache-Control和Expires字段控制,不需要发送请求到服务端进行验证。
  • 协商缓存则需要发送请求到服务端,服务端通过比较请求头和资源的相关信息来确定是否命中缓存。

协商缓存返回的状态码是304 Not Modified。

进行协商缓存的情况包括:

  • 第一次请求该资源,没有强缓存的情况。
  • 强缓存已过期(即Cache-Control或Expires字段指定的时间已到),但资源可能仍然有效的情况。
  • 强缓存已过期,但浏览器设置了忽略强缓存,或者用户按下了强制刷新按钮。

当用户请求的资源在强缓存未过期的情况下,可以通过以下方法更新:

  • 修改资源的URL:通过修改URL的方式可以绕过浏览器的缓存机制,强制浏览器重新请求资源。
  • Cache-Control:通过服务端返回的响应头中的Cache-Control字段,设置合适的缓存策略,例如设置max-age=0或no-cache等,告诉浏览器立即验证资源。
  • 版本号控制:通过在URL中添加版本号或者资源路径中添加文件哈希值等方式,每次更新资源时改变对应的版本号或哈希值,从而使浏览器认为是新的资源,重新请求并更新本地缓存。
posted @ 2023-08-26 13:35  广东靓仔-啊锋  阅读(33)  评论(0编辑  收藏  举报