高频题目

138 阅读19分钟

1. 从浏览器地址栏输入 url 到请求返回发生了什么?

答:

  1. 输入 URL 后解析出协议、主机、端口、路径等信息,并构造一个 HTTP 请求。

  2. 缓存判断:  浏览器会判断所请求的资源是否在缓存里,如果请求的资源在缓存里并且没有失效,那么就直接使用,否则向服务器发起新的请求。

    • 强制缓存:通过Cache-Control: max-age=3600的字段去设置强制缓存,当浏览器第一次发送请求到服务器时,服务器会在响应头中设置一个缓存有效期,下次再次请求资源时,游览器会先判断缓存有效期是否过期,如果没有过期,则直接使用本地缓存,否则在去浏览器中请求资源。
    • 协商缓存:通过If-Modified-Since或If-None-Match字段来实现,当浏览器第一次发送请求给服务器时,服务器会在响应头设置资源的Last-Modified时间或Etag唯一标识。当再次请求资源时,浏览器还是会发送请求到服务器,服务器会根据If-Modified-Since(值为上次响应的Last-Modified时间)或If-None-Match(值为上次响应的ETage)去判断资源是否更新,若未更新,则返回304 Not Modified状态码,此时,游览器就从本地获取资源。若更新,则会返回最新的资源文件。
  3. DNS 域名解析。(字节面试被虐后,是时候搞懂 DNS 了

    • 首先会先去浏览器和操作系统缓存中检查,查看最近的DNS查询结果,若未找到。
    • 本地域名服务器查询: 请求会被发送到配置的本地DNS服务器(通常是ISP提供)
    • 根服务器查询:= 如果本地DNS服务器没有缓存记录,它会向根DNS服务器查询,根服务器不会知道域名完整ip地址,但是他知道顶级域名(TLD)服务器地址。(例如:com、org等)
    • 顶级域名服务器查询: 本地DNS服务器接着会向顶级域名服务器查询,顶级域名服务器会返回该域名的权威DNS服务器地址。
    • 权威DNS服务器查询: 接着,本地DNS服务器会向权威DNS服务器查询,权威服务器存储了关于该域名的记录,并返回与请求域名关联的IP地址。
  4. TCP 连接。

    总是要问:为什么需要三次握手,两次不行吗?其实这是由 TCP 的自身特点可靠传输决定的。客户端和服务端要进行可靠传输,那么就需要确认双方的接收和发送能力。第一次握手可以确认客服端的发送能力,第二次握手,确认了服务端的发送能力和接收能力,所以第三次握手才可以确认客户端的接收能力。不然容易出现丢包的现象。

  5. http 请求。

  6. 服务器处理请求并返回 HTTP 报文。

  7. 浏览器渲染页面。

    1. 解析HTML,构建DOM树
    1. 解析CSS,生成CSS规则树
    1. 合并DOM树和CSS规则,生成render树
    1. 布局render树(Layout/reflow),负责各元素尺寸、位置的计算
    1. 绘制render树(paint),绘制页面像素信息 。

2.前端如何进行性能优化?

参考链接:juejin.cn/post/710837…

1. 网络方面:

a.静态资源使用CDN

当网站所挂服务器离用户越来越远时,访问网站的延迟也会越高,CDN(内容分发网络)在不同的地理位置部署Web服务器,根据用户位置分配最近的资源,缩短请求时间达到优化加载的速度的效果。

b.减少不必要的HTTP请求

一个完整的HTTP请求需要经历连接与释放的过程,需要一定的时间,减少HTTP可以节省一定时间。 比如在开发中,去减少在一些页面中根本没有用到的接口请求。

c.使用HTTP2.0

HTTP1.1版本存在线程阻塞的问题,在同一时间,同一域名的请求有一定的数量限制,超过限制数目的请求会被阻塞。

HTTP2.0特性:

  • **支持二进制传输:**http1.1采用的是文本传输的方式,http2.0采用二进制传输,传输更快。
  • **支持多路复用:**多个请求可以共用一个TCP连接,提高连接的利用率,降低延迟
  • **头部压缩:**将头部信息采用压缩算法进行压缩,数据量更小。
d.采用HTTP缓存

可以通过设置强制缓存或者协商缓存来进行控制,符合缓存条件时直接读取缓存,减少发送请求,提高加载速度。

e.使用Gzip压缩

Gzip  通过 LZ77 算法与 Huffman 编码来压缩文件,重复度越高的文件可压缩的空间就越大,对 JS、CSS、HTML 等文本资源均有效。当 Nginx 返回 js 文件的时候,会判断是否开启 gzip,然后压缩后再返回给浏览器。

该压缩方法需要 Nginx 配置 也开启 Gzip 压缩,单纯前端通过 webpack 插件开启 Gzip 压缩是不能达到优化效果的

2.图片优化方面

a.采用图片懒加载

先不给图片设置src路径,当图片出现在浏览器可视区域时,才去加载图片,这就是延迟/懒加载。

图片设置data-src属性在页面不可见时图片不会加载。

图片懒加载思路:

  • 先在img标签设置自定义属性data-src
  • 计算当页面可见时,src值替换为data-src,加载图片。
b.采用图片压缩

可以利用webpack的image-webpack-loader对图片进行压缩。

c.采用略缩图

点击或加载到之后再查看清晰大图

如果有一个1920 * 1080大小的图片,用略缩图的方式展示给用户,并且当用户鼠标悬停在上面时才展示全图,如果用户从未真正将鼠标悬停在缩略图上,则不浪费了下载图片的时间。

所以,可以用两张图片来进行优化。一开始,只加载缩略图,当用户悬停在图片上时,才加载大图

d.使用Web Workers

JavaScript语言是单线程,所有与浏览器UI无关的长时间运行脚本任务只能在一个线程完成,一次只能做一件事。如果是复杂的计算,则页面运行可能会被阻塞。

此时可以使用Web Worker进行处理与浏览器UI无关的长时间运行脚本。Web Worker为Web内容在后台线程中运行脚本提供了一种简单的方法,线程可以执行任务而不干扰用户界面。

e.服务端渲染SSR

客户端渲染: 客户端获取HTML,下载JS文件运行生成DOM,渲染页面。

服务端渲染: 服务端直接返回HTML文件,客户端只需解析HTML

当前使用 Vue/React 开发的网站,只要不是对 SEO 有要求,大部分都是采用的客户端渲染。而如果网站要追求 SEO 好和首屏速度快的话,那就可采用服务端渲染。因为它只需加载一个渲染完毕的 HTML 文件,比客户端渲染要更快。

3.减少打包体积

a.压缩资源的体积

打包体积减少,可以减少文件下载时间,可以让用户体验更好。

在webpack可以使用如下插件进行压缩:

  • HTML: html-webpack-plugin
  • CSS: css-minimizer-webpack-plugin
  • JavaScript: terser-webpack-plugin
b.使用tree shaking删除未使用的代码

Tree shaking 是一种优化 JavaScript 应用程序的技术,它通过静态代码分析和模块依赖关系来删除未使用的代码,从而减小最终打包后的代码体积。

4.使用事件委托

添加到页面上的事件数量会影响页面的运行性能,如果添加的事件过多,会导致网页的性能下降。采用事件委托的方式,可以大大减少注册事件的个数。

使用事件委托的好处:

  • 提高性能
  • 节省内存占用,减少事件注册
  • 实现当新增子对象时无需再次对其绑定

5.CSS方面优化

a.降低CSS选择器的复杂性
  1. 保持简单,不要使用嵌套过多过于复杂的选择器
  2. 通配符和属性选择器效率最低,需要匹配的元素最多,尽量避免使用
b.使用transform和opacity属性更改来实现动画

在CSS中,transforms和opacity这两个属性更改不会触发重排与重绘,他们由合成器单独处理的属性。

6.渲染优化方面

a.减少重绘重排
  • 用 JavaScript 修改样式时,最好不要直接写样式,而是替换 class 来改变样式

  • 需要对元素进行复杂操作时,可以先隐藏元素(display:none)操作完成后再显示

  • 需要创建多个 DOM 节点时,使用 DocumentFragment 创建完最后再一次性加入文档

b.采用防抖节流优化高频事件

当页面有一些事件频繁触发时,为了优化体验,需要对这类事件进行调用次数的限制,于是可以使用防抖与节流来减少调用频率。

  • 防抖:一段时间后只执行一次,将多次执行变为最后一次执行
  • 节流:在固定的频率执行,将多次执行变为在规定时间内只执行一次

c.优化动画

  • 优先使用 CSS 来实现动画效果
  • 使用 translateZ/translate3d 开启硬件加速
  • 合理使用 requestAnimationFrame 代替 setTimeout

6.预加载(加载顺序)

preload/prefetch  可控制 HTTP 优先级,从而达到关键请求更快响应的目的。

<link rel="prefetch" href="style.css" as="style" /> <link rel="preload" href="main.js" as="script" />
  1. preload 优先级较高,提前加载较晚出现,但对当前页面非常重要的资源

  2. prefetch 优先级较低,提前加载后继路由需要的资源。一般用以加载其它路由资源,如当页面出现 Link,可 prefetch 当前 Link 的路由资源

3.对虚拟DOM的理解?为什么要用虚拟DOM?

答: 我们打印出来虚拟DOM以后,实际上可以发现虚拟DOM是一个JS对象,对象内有type、props、children等属性。type是表示标签类型,props是标签内部的属性,children为节点中子节点的内容。

  • 1.可以减少DOM操作:

    • a.虚拟DOM可以将多次操作合并为一次操作,比如一个页面如果有500次变化,没有虚拟DOM的就会渲染500次,而虚拟DOM只需要渲染一次,从这点上来看,页面越复杂,虚拟DOM的优势越大
    • b.虚拟DOM可以借助DOM diff算法,对比新旧虚拟DOM树的差异,只将差异更新到DOM中,以最小化成本更新。
  • 2.支持跨平台:虚拟DOM本质上是一个对象,可以在兼容不同平台,抹平平台之间的差异,实现支持跨平台使用。

为什么要用虚拟DOM?

  • 保证性能下限,在不进行手动优化的情况下,提供过得去的性能
  • 虚拟DOM可以提供跨平台使用。

4.React Diff算法的原理是什么?

答: image.png

原理: diff算法就是通过对比新旧两株虚拟DOM树的变更差异,将更新补丁作用于真实DOM,以最小成本完成视图更新。

具体流程如下: 我们需要把每一种节点类型抽象成对象,每一种节点类型有自己的属性,也就是prop,每次进行diff的时候,react会先比较该节点类型,假如节点类型不一样,那么react会直接删除该节点,然后直接创建新的节点插入到其中,假如节点类型一样,那么会比较prop是否有更新,假如有prop不一样,那么react会判定该节点有更新,那么重渲染该节点,然后在对其子节点进行比较,一层一层往下,直到没有子节点。

5.虚拟DOM的引入与直接操作原生DOM相比,虚拟DOM一定比原生DOM更快吗?

答:不一定,虚拟DOM渲染是先将真实DOM转换为JS对象,然后通过DOM diff算法处理完毕以后,再去转换为真实DOM,也就是说他始终会创建JS对象。对于首次加载页面的情况,需要先创建所有的虚拟DOM,然后再把虚拟DOM转换为真实DOM,这样会多一层计算,不会比直接渲染真实DOM更快。

6.React Diff算法的优化策略有哪些?

答:

  • 1.虚拟DOM树节点比较时,只比较同一层的节点,不跨层进行比较。
  • 2.若发现虚拟DOM树当前比较的节点类型不一样,直接判断为脏组件,从而直接删除该节点以及它的所有子节点。
  • 3.同一层级的一组子节点,可以通过设置唯一的key来进行区分。

补充: React key是干嘛用的?为什么要加?key主要解决哪一类问题

答:为了方便react内部进行优化,我们必须给每一个react节点添加key,这个key是prop在设计值之初不是给开发者用的,而是给react用的,大概的作用就是给每一个react节点添加一个身份标识,方便react进行识别,在重渲染过程中,如果key一样,若组件属性有所变化,则react只更新组件对应的属性;没有变化则不更新,如果key不一样,则react先销毁该组件,然后重新创建该组件。

补充: 能使用数组的index作为虚拟DOM的节点的key吗? 答:不能,key是用于渲染对象的排序,所以必须是用唯一标记的值,假如使用数组的index的话,如果删除数组中间任意一个数,后面的数补上来,导致index发生变化,无法找到对应的虚拟DOM节点。可以使用UUID作为唯一的key。

7.对Redux的理解,主要解决了什么问题?

image.png

答:Redux提供了一个叫store的统一仓库,组件通过dispatch将state直接传入store,不用通过其他组件,并且组件通过subscribe从store获取state的改变。使用redux,所有的组件都可以从store中获取到所需的state,也能从store获取到state的改变。这比组件之间互相传递数据清晰的多。

解决了什么问题?

由于在react中组件间通信的数据流是单向的,顶层组件通过props属性向下层传递数据,而下层组件不能向上层组件传递数据,兄弟组件之间同样不能,Redex使用store仓库统一管理数据,减少了数据管理难度。

8.Redux的工作流程是怎么样的?

答:

  • 首先用户(通过view)发出Action,发出方式就用dispatch方法

  • 然后Store自动调用Reducer,并且传入两个参数,当前State和收到的Action,Reducer会返回新的State

  • state一旦有变化,Store就会调用监听函数,来更新View

9.同步和异步的执行顺序是怎么样的?(你能讲一下事件循环机制吗?)

答:

1.JavaScript将任务分为同步任务和异步任务,同步任务进入主线中,异步任务首先到Event Table进行回调函数注册。

2.当异步任务的触发条件满足,将回调函数从Event Table压入到Event Queue中。

3.主线程里面的同步任务执行完毕,系统会去Event Queue中读取异步的回调函数。

4.只要主线程空了,就会去Event Queue读取回调函数,这个过程被称为Event Loop。

10.CSS中实现元素居中的7种方式总结

答:

元素分类:

  • 1.块级元素(Block) 这些元素以块的形式显示在页面上,每个块级元素会独占一行(除非通过其他CSS属性进行修改)。块级元素可以设置宽度、高度、内边距和外边距。

常见的块级元素:<div>, <p>, <h1>-<h6>, <ul>, <li>, <section>, <footer>

  • 2.行内元素(Inline) 行内元素也称为内联元素,这些元素以行内的形式显示在页面上,它们不会独占一行,而是在同一行上与其他元素并排显示。行内元素的宽度和高度默认由其内容决定,无法设置宽度和高度。

常见的行内元素包括:<span>, <a>, <strong>, <em>, <img>, <input>

  • 3.行内块元素(Inline-block) 这些元素以行内块的形式显示在页面上,具有行内元素的特性,但可以设置宽度、高度、内边距和外边距,行内块元素会在同一行显示,但是他们之间会保留空白间隔。

一些常见的行内块元素包括:<button>, <label>, <select>, <textarea>, <img> 。

水平居中的方式:

1.使用text-align:center居中(适用于内联元素,而不适用于块级元素)

使用text-align:center,可以在CSS中实现内联元素的水平居中, 这个技术利用了CSS的text-align属性,通过对元素的文本对齐方式进行调整实现居中的效果。

<div class="container">
  <span>检测居中效果</span><br>
  <img src="1.jpg" alt=""><br>
  <input type="text" value="检测居中效果">
</div>

.container {
   text-align: center;
} 

image.png

在上述示例中,将容器的 text-align 属性设置为 center,使容器内的文本水平居中显示。由于内联元素的默认宽度与内容宽度一致,所以通过调整文本的对齐方式,元素就可以在容器中水平居中。

需要注意的是,这种方法适用于内联元素,而不适用于块级元素。对于块级元素,可以将其包裹在一个容器中,并对容器应用 text-align: center; 实现块级元素的水平居中。

这是一种简单而常用的方法,特别适用于文本、按钮、图标等内联元素的水平居中。然而,它只能实现水平居中,对于垂直居中需要采用其他的布局方法。若元素是单行文本, 则可设置 line-height 等于父元素高度来实现垂直居中。

2.margin:0 auto居中 (对于内联元素和块级元素都可以)

要将块级元素水平居中,可以使用margin属性将左右边距设置为auto

.container {
   width: 300px; /* 设置容器的宽度 */
   margin: 0 auto; /* 水平居中 */
}

将容器的宽度设置为一个固定值,然后使用 margin: 0 auto; 将左右外边距设置为 "auto",实现元素的水平居中。由于左右外边距都设置为 "auto",浏览器会自动将剩余的空间均匀分配给两侧的外边距,从而使元素居中显示。

image.png

3.使用Flex居中元素

Flex 弹性布局,通过将容器的 display 属性设置为 flex,并使用 justify-content 和 align-items 属性分别进行水平和垂直居中设置,元素将在容器中居中显示。

.container {
   display: flex;
   justify-content: center; /* 水平居中 */
   align-items: center;     /* 垂直居中 */
} 

Flexbox 还提供了其他属性,如 flex-direction、flex-wrap、align-content 等,可以根据具体需求进行进一步的布局调整。使用 Flexbox 可以轻松实现各种居中效果,并且具有很好的浏览器兼容性。

4.使用 Grid 居中元素

网格布局 Grid 是另一种强大的布局模型,也可以用于实现元素的居中布局。通过将容器的 display 属性设置为 grid,并使用 place-items 属性设置为 center,元素将在容器中居中显示。

.container {
    display: grid;
    place-items: center center; /* 水平和垂直居中*/
}

place-items 是一个简写属性,用于同时设置align-itemsjustify-items属性,place-items: center center; 将元素在单元格内垂直和水平方向上都居中对齐。

5.使用absolute和translateX(-50%)的方式(使用绝对定位和负边距可以适用于不同类型的元素,包括块级元素和内联元素)

水平居中: 首先将容器的左边距设置为50%(相对于父容器),然后使用transform: translateX(-50%),将元素向左平移50%的宽度,从而实现水平居中。

.container {
   position: absolute;
   left: 50%;
   transform: translateX(-50%);
}

水平垂直居中: 将要居中的元素的定位属性设置为 absolute。通过将元素的 top 和 left 属性都设置为 50%,元素的左上角将位于容器的中心。最后,通过 transform 属性和 translate 函数将元素向上和向左平移自身宽度和高度的一半,从而实现垂直居中的效果。

.container {
   position: absolute;
   top: 50%;
   left: 50%;
   transform: translate(-50%, -50%);
} 
6.使用 calc() 函数居中

calc() 函数通过执行简单的数学运算,并返回计算结果作为CSS属性值。使用 calc() 函数可以根据具体的需求进行灵活的计算和布局,实现元素在水平或垂直方向的居中。

对于水平居中,可以使用 calc() 函数结合百分比和像素值来计算元素的左右外边距。通过将50%(容器的一半宽度)减去150像素(元素宽度的一半)来计算得到。

.container {
   width: 300px;
   margin-left: calc(50% - 150px);
   margin-right: calc(50% - 150px);
   /* background-color: blue; */
} 

对于垂直居中,可以使用 calc() 函数结合百分比、像素值和视口单位(如vh)来计算元素的上下外边距。通过将50vh(视口高度的一半)减去200像素(元素高度的一半)来计算得到的。

.container {
   height: 400px;
   margin-top: calc(50vh - 200px);
   margin-bottom: calc(50vh - 200px);
} 
7.使用table居中(表格布局)

使用表格布局(Table Layout)可以实现元素的居中布局,需要创建一个包含一个单元格的表格,并将元素放置在该单元格中。

.container {
   display: table;
   width: 100%;
}
.content {
   display: table-cell;
   text-align: center;
}
<div class="container">
    <div class="content">
        <span>检测居中span效果</span><br>
        <input>
        <div>检测居中div效果</div><br>
        <p>检测居中p效果</p><br>
    </div>
</div>

垂直居中的方式:

1.父元素height高度等于line-height的高度
.parent {
    height: 高度;
}

.son {
    line-height: 高度;
}
2.使用table的垂直居中
.parent {
  display: table;
}
.son {
  display: table-cell;
  vertical-align: middle;
}
3.使用flex和align-item:center的方式
.parent {
    display: flex;
    align-items: center;
}
4.使用absolute的方式
.son {
    position: absolute;
    top: 50%;
    transform: translate( 0, -50%);
}

.son {
    position: absolute;
    top: 50%;
    height: 高度;
    margin-top: -0.5高度;
}

.son {
    position: absolute;
    top: 0;
    bottom: 0;
    margin: auto 0;
}