2023高频前端面试题-浏览器

news2024/9/29 1:24:35

1. 浏览器是如何解析 CSS 选择器的?

在生成渲染树的过程中,渲染引擎会根据选择器提供的信息来遍历 DOM 树,找到对应的 DOM 节点后将样式规则附加到上面。

来看一段样式选择器代码、以及一段要应用样式的 HTML:

.mod-nav h3 span {
  font-size: 16px;
}
<div class="mod-nav">
  <header>
    <h3>
      <span>标题</span>
    </h3>
  </header>

  <div>
    <ul>
      <li><a href="#">项目一</a></li>
      <li><a href="#">项目一</a></li>
      <li><a href="#">项目一</a></li>
    </ul>
  </div>
</div>

<div class="box">...</div>

渲染引擎是怎么根据以上样式选择器去遍历这个 DOM 树的呢?是按照从左往右的选择器顺序去匹配,还是从右往左呢?

为了更直观的观查,我们先将这棵 DOM 树先绘制成图:

在这里插入图片描述
然后我们来对比一下两种顺序的匹配:

从左往右:.mod-nav => h3 => span

  1. 遍历所有的元素, 找有 .mod-nav 类的节点

  2. .mod-nav 开始遍历所有的⼦孙节点 headerdivh3ul

    遍历所有的后代元素后, 知道了, 整个子孙后代只有一个 h3

  3. 找到 h3 , 还要继续重新遍历 h3 的所有子孙节点, 去找 span

问题: 会进行大量树形结构子孙节点的遍历, 这是非常消耗成本的!

这在真实页面中⼀棵 DOM 树的节点成百上千的情况下,这种遍历方式的效率会非常的低,根本不适合采用。

从右往左:span => h3 => .mod-nav

  1. 先找到所有的 span 节点 ,然后基于每⼀个 span 再向上查找 h3

  2. h3 再向上查找 .mod-nav 的节点

  3. 最后触及根元素 html 结束该分⽀遍历

从右向左的匹配规则, 只有第一次会遍历所有元素找节点, 而剩下的就是在看父辈祖辈是否满足选择器的条件, 匹配效率大大提升!

因此,浏览器遵循 “从右往左” 的规则来解析 CSS 选择器!

2. 浏览器是如何进行界面渲染的?

不同的渲染引擎的具体做法稍有差异,但是大体流程都是差不多的,下面以 chrome 渲染引擎 的 渲染流程来说明:
在这里插入图片描述
上图展示的流程是:

  1. 获取 HTML ⽂件并进⾏解析,生成一棵 DOM 树(DOM Tree)

  2. 解析 HTML 的同时也会解析 CSS,⽣成样式规则(Style Rules)

  3. 根据 DOM 树和样式规则,生成一棵渲染树(Render Tree)

  4. 进行布局(Layout)(重排),即为每个节点分配⼀个在屏幕上应显示的确切坐标位置

  5. 进⾏绘制(Paint)(重绘),遍历渲染树节点,调⽤ GPU(图形处理器) 将元素呈现出来

3. 重绘(repaint)和重排(回流 reflow)是什么?

重排

重排是指部分或整个渲染树需要重新分析,并且节点的尺⼨需要重新计算。

表现为 重新⽣成布局,重新排列元素。

重绘

重绘是由于节点的⼏何属性发⽣改变,或由于样式发⽣改变(例如:改变元素背景⾊)。

表现为某些元素的外观被改变。或者重排后, 进行重新绘制!

两者的关系

重绘不⼀定会出现重排,重排必定会触发重绘。

每个页面至少需要一次回流+重绘。(初始化渲染)

重排和重绘的代价都很⾼昂,频繁重排重绘, 会破坏⽤户体验、让界面显示变迟缓。

我们需要尽可能避免频繁触发重排和重绘, 尤其是重排

4. 何时会触发重排?

重排什么时候发生?

1、添加或者删除可见的 DOM 元素;

2、元素位置改变;

3、元素尺寸改变——边距、填充、边框、宽度和高度

4、内容改变——比如文本改变或者图片大小改变而引起的计算值宽度和高度改变;

5、页面渲染初始化;

6、浏览器窗口尺寸改变——resize 事件发生时;

5. 浏览器对重绘重排的优化

思考下述代码的重绘重排过程!

let s = document.body.style;
s.padding = "2px"; // 重排 + 重绘
s.border = "1px solid red"; // 再一次 重排 + 重绘
s.color = "blue"; // 再一次重绘
s.backgroundColor = "#ccc"; // 再一次 重绘
s.fontSize = "14px"; // 再一次 重排 + 重绘
document.body.appendChild(document.createTextNode("abc!")); // 添加node,再一次 重排 + 重绘

聪明的浏览器:

从上个实例代码中可以看到几行简单的 JS 代码就引起了 4 次重排、6 次重绘。

而且我们也知道重排的花销也不小,如果每句 JS 操作都去重排重绘的话,浏览器可能就会受不了!

所以浏览器会优化这些操作,浏览器会维护 1 个队列,把所有会引起重排、重绘的操作放入这个队列

等队列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会 flush 队列,进行一个批处理

这样就会让多次的重排、重绘变成了一次重排重绘。

虽然有了浏览器的优化,但有时候我们写的一些代码可能会强制浏览器提前 flush 队列,这样浏览器的优化可能起不到作用了。

比如当你请求向浏览器获取一些样式信息的时候(保证获取结果的准确性),就会让浏览器 flush 队列

  1. offsetTop, offsetLeft, offsetWidth, offsetHeight
  2. scrollTop/Left/Width/Height
  3. clientTop/Left/Width/Height
  4. 请求了 getComputedStyle()

猜一猜, 页面效果是什么:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      div {
        width: 200px;
        height: 200px;
        background-color: pink;
        transition: all 1s;
      }
    </style>
  </head>
  <body>
    <div id="box"></div>

    <script>
      const div = document.getElementById("box");
      // console.log(div.offsetWidth)
      div.style.width = "400px";
      div.style.height = "400px";
    </script>
  </body>
</html>

6. 重绘重排角度, 我们应该如何优化页面渲染性能 ?

优化页面渲染性能的角度: 尽可能减少重绘和重排的次数

主要有几大方式来避免:

  • 1 集中修改样式 (这样可以尽可能利用浏览器的优化机制, 一次重排重绘就完成渲染)

  • 2 尽量避免在遍历循环中, 进行元素 offsetTop 等样式值的获取操作, 会强制浏览器刷新队列, 进行渲染

  • 3 利用 transform 实现动画变化效果, 去代替 left top 的变换 (轮播图等)

    transform 变换, 只是视觉效果! 不会影响到其他盒子, 只触发了自己的重绘

  • 4 使用文档碎片(DocumentFragment)可以用于批量处理, 创建元素

文档碎片的理解:

documentFragment是一个保存多个元素的容器对象(保存在内存)当更新其中的一个或者多个元素时,页面不会更新。

当 documentFragment 容器中保存的所有元素操作完毕了, 只有将其插入到页面中才会更新页面。

<ul id="box"></ul>

<script>
  let ul = document.getElementById("box");
  for (let i = 0; i < 20; i++) {
    let li = document.createElement("li");
    li.innerHTML = "index: " + i;
    ul.appendChild(li);
  }

  // let ul = document.getElementById("box")
  // let fragment = document.createDocumentFragment()
  // for (let i = 0; i < 20; i++) {
  //     let li = document.createElement("li")
  //     li.innerHTML = "index: " + i
  //     fragment.appendChild(li)
  // }
  // ul.appendChild(fragment)
</script>

vue 底层渲染更新, 就用了 document.createDocumentFragment

7. 前端如何实现即时通讯?websocket

严格意义上: HTTP协议只能做到客户端请求服务器, 服务器做出响应, 做不到让服务器主动给客户端推送消息!

那么如果服务器数据更新了, 想要即时通知到客户端怎么办呢 ? (即时通信需求)

即时通信需求: 服务器数据一有更新, 希望推送给到浏览器

提问的回答重心:

  1. 即时通信有哪些方案?
  2. 为什么使用了其中某一个方案! websocket

基于 Web 的前端,存在以下几种可实现即时通讯的方式:

  • 短轮询 (历史方案)

    开个定时器, 每隔一段时间发请求 (实时性不强)

  • Comet - ajax 长轮询(历史方案)

    发送一个请求, 服务器只要数据不更新, 就一直阻塞 (服务器压力过大)

  • SSE

    (利用了 http 协议, 流数据的传输, 并不是严格意义的双向通信, 无法复用连接)

  • WebSocket (主流)

    性能和效率都高!

短轮询 (历史方案)

短轮询就是客户端定时发送请求,获取服务器上的最新数据。不是真正的即时通讯,但一定程度上可以模拟即时通讯的效果。
在这里插入图片描述
优缺点:

  • 优点:浏览器兼容性好,实现简单 setInterval
  • 缺点:实时性不高,资源消耗高,存在较多无用请求,影响性能

Comet - ajax 长轮询 (历史方案)

短轮询的实时性, 着实太差, 所以 Comet 技术方案应运而生, 用以实现即时通讯

使用 Ajax 长轮询(long-polling)

  • 浏览器发出 XMLHttpRequest 请求,服务器端接收到请求后,会阻塞请求直到有数据或者超时才返回

  • 浏览器 JS 在处理返回信息(有数据或者超时) 后再次发出请求。服务器收到请求后, 会再次阻塞到有数据或者超时


    在这里插入图片描述
    优缺点:

  • 优点:浏览器兼容性好,即时性好不存在⽆⽤请求

  • 缺点:服务器压力较大(维护⻓连接会消耗较多服务器资源)

SSE

服务端推送事件(Server-Sent Event),它是⼀种基于 HTTP 协议, 允许服务端向客户端推送新数据的 HTML5 技术。

问题: HTTP 协议 不是 无法做到服务器主动向客户端推送消息么?

这些 SSE 采用了一些小技巧! 详细见 [参考文章:SSE 教程]

在这里插入图片描述
优缺点:

  • 优点:基于 HTTP,无需太多改造就能使⽤;相比 WebSocket 要简单一些
  • 缺点:基于⽂本传输,效率没有 WebSocket ⾼;基于 HTTP 协议, 不是严格的双向通信

WebSocket (目前主流)

这是基于 TCP 协议的全新、独⽴的协议,作⽤是在服务器和客户端之间建⽴实时的双向通信。

WebSocket 协议与 HTTP 协议保持兼容,但它不会融⼊ HTTP 协议,仅作为 HTML 5 的⼀部分。

优缺点:

  • 优点:真正意义上的双向实时通信,性能好、延迟低

  • 缺点:由于是独⽴于 HTTP 的协议,因此要使用的话需要对项⽬作改造;

    使⽤复杂度会⾼一些,通常需要引⼊成熟的库 (如: Socket-io );并且⽆法兼容低版本的浏览器

HTTP 和 WebSocket 的连接通信比较图:
在这里插入图片描述

8. 什么是浏览器同源策略?

首先,同源是指资源地址的 “协议 + 域名 + 端⼝” 三者都相同,即使两个不同域名指向了同⼀ IP 地址,也被判断为⾮同源。

下面是一些地址的同源判断示例:

以下不同地址的页面, 去请求一个接口: http://store.company.com/getInfo

在这里插入图片描述
同源策略是 浏览器 的一种⽤于隔离潜在恶意⽂件的重要安全保护机制 !!! (服务器没有这个策略限制)

在浏览器中,⼤部分内容都受同源策略限制,除了以下三个资源获取类型的标签:

  • <img>
  • <link>
  • script

9. 如何实现跨域获取数据?

历史上出现过的跨域⼿段有很多,主要了解 3 种跨域⽅案:

  • JSONP

  • CORS

  • 服务器代理(webpack 代理, Nginx 反向代理)

JSONP

这是一种非常经典的跨域方案,它利用了<script> 标签不受同源策略的限制的特性,实现跨域效果。

优点:

  • 实现简单
  • 兼容性好

缺点:

  • 只支持 GET 请求 (因为 <script> 标签只能发送 GET 请求)
  • 存在被 XSS 攻击的可能,缺乏安全性保证
  • 需要服务端配合改造

axios 中不支持 JSONP, 如果在开发中, 需要发送 JSONP 请求, 可以用 jsonp 插件

参考文档: Vue 中 JSONP 插件的使用

CORS (主流)

跨域资源共享(CORS),这是⽬前比较主流的跨域解决⽅案,

它利用一些额外的 HTTP 响应头来通知浏览器, 允许访问来自指定 origin 的非同源服务器上的资源。

Node.js 的 Express 框架的设置代码 (Java, PHP 等, 配置代码差不多):

// 创建一个 CORS 中间件
function allowCrossDomain(req, res, next) {
  res.header("Access-Control-Allow-Origin", "http://example.com");
  res.header("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE");
  res.header("Access-Control-Allow-Headers", "Content-Type");
  next();
}

//...

app.configure(function () {
  // ...

  // 为 Express 配置 CORS 中间件
  app.use(allowCrossDomain);

  // ...
});

所以, 开发中或上线时遇到跨域, 如果不考虑兼容性问题 (IE10+)

优先让后台配置个 CORS 解决即可, 简单快捷!

代理服务器

说明: 同源策略, 是浏览器的安全策略, 服务器于服务器之间, 没有跨域问题! 所以可以利用代理服务器转发请求!

  1. 开发环境的跨域问题 (使用 webpack 代理服务器解决)

    配置 devServer 的 proxy 配置项

    module.exports = {
      devServer: {
        // 代理配置
        proxy: {
          // 这里的api 表示如果我们的请求地址有/api的时候,就出触发代理机制
          "/api": {
            target: "www.baidu.com", // 我们要代理请求的地址
            // 路径重写
            pathRewrite: {
              // 路径重写  localhost:8888/api/login  => www.baidu.com/api/login
              "^/api": "", // 假设我们想把 localhost:8888/api/login 变成www.baidu.com/login 就需要这么做
            },
          },
        },
      },
    };
    
  2. 生产环境的跨域问题 (使用 nginx 服务器代理)

    博客参考: https://www.cnblogs.com/elfpower/p/8818759.html
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1140516.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI口语APP第三方接口

AI口语练习应用程序通常可以通过第三方接口来集成语音识别、自然语言处理和其他相关功能。以下是一些常见的第三方接口及其特点&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1.Google Cloud Speech…

如何设计元宇宙展厅,元宇宙展厅的展示和交互形式有哪些?

引言&#xff1a; 元宇宙是当下科技界最为炙手可热的话题之一&#xff0c;被誉为数字世界的未来。将元宇宙与展厅结合&#xff0c;展示产品信息成了很多人关注的热点&#xff0c;那么元宇宙展厅应该如何设计呢&#xff1f; 一&#xff0e;元宇宙展厅设计理念 1.创造虚拟与现实…

为什么选择Codigger静态分析?

开发每个阶段的安全代码 Codigger是一种很流行的静态分析工具&#xff0c;用于持续检查代码库的代码质量和安全性&#xff0c;并在代码评审期间指导开发团队。代码审查是一种系统的软件质量保证技术&#xff0c;通过审查开发人员的代码来发现和修复问题、提高代码质量并执行编码…

九月手游出海三黑马,营销上如何做到成功?

从2022年开始&#xff0c;手游出海红利几乎完全消失&#xff0c;在经历高速发展的黄金阶段过渡到竞争激烈的精品化阶段&#xff0c;再到目前“不上不下”的存量竞争时代。 现在手游出海到底面临怎么样的困境&#xff1f;是全球经济下行、隐私政策更新频繁、国际形势变化莫测、…

【工具使用】使用Audition增加增益的方法

一&#xff0c;简介 本文主要介绍如何在Adobe Audition 2020中改变波形的幅值。供参考。 二&#xff0c;操作方法 这里使用1KHz&#xff0c;-120dB信号为例。 2.1 方法一&#xff1a;直接使用悬浮窗口 窗口中输入6&#xff0c;波形的幅值就变成了-114dB。 注意&#xff1a…

ChatGLM系列五:Lora微调

目前主流对大模型进行微调方法有三种&#xff1a;Freeze方法、P-Tuning方法和Lora方法 LoRA: 在大型语言模型上对指定参数&#xff08;权重矩阵&#xff09;并行增加额外的低秩矩阵&#xff0c;并在模型训练过程中&#xff0c;仅训练额外增加的并行低秩矩阵的参数,冻结其他参数…

Java中级面试题记录(四)

一面面试题 1.Innodb的行数据存储模式 https://baijiahao.baidu.com/s?id1775090633458928876&wfrspider&forpc 2.行数据包含哪些信息&#xff1f; https://baijiahao.baidu.com/s?id1775090633458928876&wfrspider&forpc 3.MySQL在进行存储VARCHAR的时…

qq怎么发长视频?超级好用!

在平时的工作和生活中&#xff0c;我们会想分享一些比较长的内容。但是我们会发现视频文件过大&#xff0c;可能会超过腾讯规定的单次发送文件的大小限制&#xff0c;导致无法发送成功。这时候就需要借助一些视频压缩工具&#xff0c;下面介绍了四种方法&#xff0c;一起来看看…

浅谈信息化与数字化

一、信息化/数字化的概念 信息化、数字化按字面意思理解&#xff0c;这两个词的确代表了不同的含义。但是也不可否认&#xff0c;在目前我们可以接触到的信息平台来看。信息化、数字化很多时候都被混在一起了。 那么&#xff0c;既然今天要聊这个话题。我们得先把这两个词分清…

【C++】:拷贝构造函数与赋值运算符重载的实例应用之日期类的实现

C实现日期类 ├─属性&#xff1a; │ ├─年份 │ ├─月份 │ └─日期 ├─方法&#xff1a; │ ├─构造函数 │ ├─拷贝构造函数 │ ├─析构函数 │ ├─设置年份 │ ├─设置月份 │ ├─设置日期 │ ├─获取年份 │ ├─获取月份 │ ├─获取日期 │ ├…

mysql-linux归档版安装

什么是归档版安装&#xff1f;简单来说就是编译好的软件压缩打包版。 说明&#xff1a;我这里服务器之前已经装过一个不同版本的mysql&#xff0c;已经占用了3306端口&#xff0c;所以这里我用3307端口来演示&#xff0c;命令和官方的稍有不同&#xff0c;不过步骤都是差不多的…

next项目部署到云服务器上(手动)

准备环境: 云服务器 ECS,服务器安装好了docker 自己的next项目 开始: 1.在next项目根目录下创建Dockerfile文件 FROM node:18-alpine AS base# Install dependencies only when needed FROM base AS deps # Check https://github.com/nodejs/docker-node/tree/b4117f9333d…

SpringMVC Day 05 : Spring 中的 Model

前言 欢迎来到 SpringMVC 系列教程的第五天&#xff01;在之前的教程中&#xff0c;我们已经学习了如何使用控制器处理请求和返回视图。今天&#xff0c;我们将深入探讨 Spring 中的 Model。 在 Web 应用程序开发中&#xff0c;数据的传递和展示是非常重要的。SpringMVC 提供…

flutter版本选择

使用命令dart --version查看dart版本 使用命令flutter doctor查看flutter版本 Flutter 有 3 个发布渠道&#xff0c;分别是 stable、beta 和 master。我们推荐使用 stable 渠道除非你需要体验最新更新的 Flutter 特性。 要查看你当前使用的哪个渠道&#xff0c;使用下面的命令&…

山西电力市场日前价格预测【2023-10-28】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2023-10-28&#xff09;山西电力市场全天平均日前电价为324.42元/MWh。其中&#xff0c;最高日前电价为601.09元/MWh&#xff0c;预计出现在18:15。最低日前电价为0.00元/MWh&#xff0c;预计出…

微信native支付对接

简介 Native支付是指商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。 应用场景 Native支付适用于PC网站、实体店单品或订单、媒体广告支付等场景,用户扫描商户展示在各种场景的二维码进行支付。聚体步骤如下: 1.商户根据微信支付的规则,…

【EI会议投稿】第三届电气、控制与信息技术国际学术会议(ECITech 2024)

第三届电气、控制与信息技术国际学术会议&#xff08;ECITech 2024&#xff09; 2024 3rd International Conference on Electrical, Control and Information Technology 继往届ECITech年度系列会议的成功举办&#xff0c;第三届电气、控制与信息技术国际学术会议&#xff08…

JTAG 详解

10.1 JTAG简介 JTAG接口的基本工作原理是&#xff1a;在芯片内部定义一个TAP&#xff08;Test Access Port&#xff0c;测试访问端口&#xff09;&#xff0c;开发人员使用连接到芯片的JTAG外部接口上的JTAG调试器&#xff0c;通过访问芯片内部的TAP端口来扫描芯片内部各个扫…

数据驱动决策:大数据分析如何塑造业务成功

文章目录 大数据分析的定义大数据分析如何影响业务1. 洞察业务趋势2. 提高决策质量3. 优化运营效率4. 个性化客户体验5. 发现新商机 如何利用大数据分析实现业务成功1. 收集和整合数据2. 选择适当的工具和技术3. 制定数据策略4. 建立数据分析团队5. 进行实验和反馈 大数据分析的…