2023年的面试

262 阅读11分钟

前言

最近也是经历了一些面试(机会太少了),感受了一下2023年的寒意,碰到了几道面试题,总结了一下。希望大家都能找到心仪的工作吧。

HTTP

http用户等待时间

http用户等待的总时长通常会分为三段:

网络时间(请求和响应在网络上传输的时间)

网络时间主要由用户与服务器的网络状况决定,网络连接较慢或网络带宽较小,网络时间会较长。

服务器端时间(请求到达服务器,服务器做一些运算等再响应所需的时间)

服务器端时间主要由服务器性能决定,服务器性能较差,服务器端时间会较长。

浏览器端时间(浏览器解析、渲染绘制的时间)

浏览器端时间主要由用户使用的浏览器和设备性能决定,性能较差的浏览器或设备,浏览器端时间会较长。
其中,网络时间服务器端时间通常会较长,占较大比重。所以,要提高http用户等待时间,主要还是要提高网络质量和服务器性能。

如何使浏览器端的加载时间更短,优化方法? 一般哪些数据会被缓存

常浏览器端时间较长的原因在于:

  1. 页面资源(JS、CSS、IMG等)较多,浏览器需要下载和解析较多资源,占用较长时间;
  2. 页面DOM结构复杂,浏览器需要构建DOM树和渲染DOM元素,占用较长时间;
  3. 设备性能较差,浏览器的JS引擎、渲染引擎性能较差,占用较长时间;
  4. 页面未做缓存优化,每次加载页面时浏览器需要重新下载所有资源,占用较长时间;
  5. 页面JS逻辑较复杂,浏览器执行JS代码所需时间较长;
    因此,要优化浏览器端加载时间,可以:
  6. 合理优化页面资源,减少不必要的资源;
  7. 优化页面结构,减少DOM元素数量;
  8. 使用浏览器缓存,使资源可以持久化存储并重用;
  9. 优化JS逻辑,减少浏览器执行JS代码的时间;
  10. 选择性地使用懒加载等技术,合理控制资源的加载时机和顺序。

浏览器中通常会缓存如下的数据:

  1. 页面资源:JS、CSS、IMG等,通过缓存可以直接读取而无需重新下载,加快页面加载速度;
  2. XHR请求数据:AJAX请求返回的数据可以缓存,避免重复请求;
  3. 页面DOM结构:浏览器会缓存页面的DOM树,可以直接重用而无需重新构建,加快页面渲染速度;
  4. 本地存储数据:浏览器会持久存储localStorage和sessionStorage中的数据,可以直接读取而无需重新设置或获取;
  5. 服务端返回的Etag或Last-Modified:可以用于判断资源是否发生变化,避免不必要的重新请求,提高加载效率;
  6. DNS解析结果:浏览器会缓存DNS解析得到的IP地址,避免重复解析域名,减少请求延迟;
  7. 页面渲染结果:浏览器会缓存页面的渲染结果(比如图片),可以直接显示而无需重新渲染,加快页面交互速度。

性能优化都做过哪些处理

前端性能优化指的是在前端开发过程中,采取的各种方法来优化网页的加载速度,提高用户的访问体验。优化前端性能的方法有很多,主要包括:

  1. 减少HTTP请求次数。可以通过合并CSS、JS文件,使用CSS Sprites等方法来减少请求次数。
  2. 减少网页总体积。压缩HTML、CSS、JS代码,合理使用图片格式和大小等可以减少网页总体积。
  3. 使用浏览器缓存。通过设置合理的expires头和cache控制可以使浏览器缓存资源,减少不必要的重新加载。
  4. CSS放在文档底部。将CSS放在页面底部可以使页面快速渲染内容,提高页面首次加载速度。
  5. 使用异步加载JS。通过async和defer属性可以异步加载JS文件,避免JS文件的加载阻塞页面的其他加载和渲染。
  6. 使用CSS3和HTML5新特性。CSS3和HTML5提供了很多新特性可以通过简单的声明代替复杂的JS动画和交互,可以提高性能。
  7. 使用前端模板。使用模板可以在服务端生成HTML字符串,返回给浏览器,减少前端处理工作,提高性能。
  8. 优化图片加载。通过合理的图片格式、大小和使用图片懒加载等方法可以优化图片加载,提高页面性能。
  9. 前端JS和CSS优化。通过减少DOM操作、选择符、重绘重排等来优化JS和CSS代码,提高执行效率,优化性能。

浏览器

了解v8引擎吗,一段js代码如何执行的

V8引擎是谷歌Chrome浏览器里面用于执行JavaScript代码的引擎。它把JavaScript代码编译成机器码,然后执行机器码。
V8引擎的工作流程大致如下:

  1. 解析(Parsing):把JavaScript字符串代码转换成Abstract Syntax Tree,AST是源代码的抽象语法结构表示。
    解析阶段会进行词法分析、语法分析和抽象语法树生成。
    词法分析将JavaScript字符串代码转换成令牌(tokens)序列。
    语法分析将令牌序列转换成抽象语法树。
    AST包含变量、函数声明、表达式、语句等信息,它表示代码的结构,但不包含具体值。
  2. 编译(Compilation):把AST编译成机器码,这一步又分为几个小步骤:
    A. 变量声明:找出所有的变量,函数声明并存储在对应的作用域中。
    B. 变量分配:给所有的变量分配存储空间。
    C. 函数编译:对所有的函数体进行编译。其中包括将参数和局部变量加入对应的作用域,然后编译函数体内的语句。
    D. 优化:对生成的机器码进行优化,比如常量折叠、死代码消除、编译器优化等。
  3. 执行(Execution):直接执行已经生成的机器码,并管理运行时的环境,如内存管理、垃圾回收等。
    执行阶段,V8引擎会在调用栈中执行函数,管理作用域链和闭包,执行Bytecode指令,并实时进行垃圾回收。
    当运行到一段JavaScript代码时,V8引擎会首先把这段代码解析成AST,然后编译成机器码执行,整个过程非常快,所以我们写JavaScript代码的时候感觉就像解释执行一样流畅。

浏览器性能检测可以通过对以下指标的监测来实现:

  1. 页面加载时间:从用户点击链接访问页面,到页面所有的内容(文本、图片、CSS、JS 等)完全加载完成,这个过程所消耗的时间称为页面加载时间。

  2. 页面渲染时间:页面加载完成后,浏览器将 DOM 树构建好,然后将其渲染为 pixels 的过程称为页面渲染,这个过程消耗的时间称为页面渲染时间。

  3. JavaScript 执行时间:从页面加载完成开始执行 JavaScript,到所有 JS 执行完成,这个过程消耗的时间称为 JS 执行时间。

  4. 其他指标:CSS 选择器匹配时间、布局时间、绘制时间、页面交互响应时间等。

通过对上述指标的定期监测和对比,可以了解浏览器性能表现,及时发现性能问题。常用的监测方法有:使用浏览器自带的开发者工具的时间线,使用 Navigation Timing API,使用性能检测库如 WebPageTest 等。

ReactRouter

路由从a页面切换到b页面,从左到右切换 ,当切换到b页面时候 a页面跑哪里去了?

当在react-router中从a页面切换到b页面,从左到右切换的时候,a页面并没有真正的销毁或者消失。react-router采用的技术叫做单页应用(single page application)。这种技术的特点就是,浏览器地址栏中的hash值发生变化,但是页面本身并不会发生刷新。

react-router会通过监听浏览器地址栏hash值的变化,然后动态的切换组件的显示和隐藏,从而实现页面的切换。所以当从a页面切换到b页面的时候,a页面的组件实例并没有被销毁,只是通过css的display属性被隐藏了起来。当再从b页面切回a页面的时候,a页面的组件实例因为一直存在,所以可以很快的再显示出来,没有重新渲染和初始化的过程,这样就实现了快速的页面切换和良好的用户体验。

切换的时候reactRouter做了什么?

  1. 监听浏览器地址栏hash值的变化
  2. 根据hash值切换组件的显示和隐藏,实现页面的切换
  3. 页面组件实例并没有被销毁,只是通过css的display属性隐藏起来
  4. 当再切回该页面时,由于组件实例一直存在,可以快速显示出来,无需重新渲染和初始化
  5. 这样实现了快速的页面切换和良好的用户体验

切换的时候跟react的diff算法有关系吗?

是的,reactRouter的快速切换和react的diff算法(通过对比新旧虚拟DOM树来决定如何高效更新真实DOM)有很大关系。reactRouter实现快速切换的原理正是利用react的diff算法,使得切换时不需要销毁重建组件,而是仅更新组件的显示与隐藏,从而达到快速响应的效果

useHistory实现的原理是什么?

useHistory是react-router提供的一个hook,它可以让我们在函数组件中使用history对象。它的原理是在组件挂载时,useHistory会订阅history对象,并返回history的导出方法(如push、replace、go、goBack等)。当history发生变化时,useHistory会调用setState触发组件重新渲染,我们就可以获取到最新的history状态,从而实现页面跳转。参考链接浅谈前端路由原理hash和history

设计模式

代码的设计模式是用来提高代码的可重用性、扩展性,以及可读性的工具。设计模式是对软件设计中普遍存在的问题的抽象与总结,描述了在不同情况下能够应用的一套成功的解决方案。采用设计模式可以让代码更加优雅、简洁,同时也更加健壮。 设计模式广泛应用于各种语言和平台,它是软件工程中非常重要的内容。熟悉常用的设计模式,可以帮助我们写出更高质量的代码。常见的设计模式有:单例模式、工厂模式、适配器模式、装饰器模式、观察者模式、策略模式等等。

if else 大量使用会使代码变得繁杂难懂,如何优化?

  1. 三元运算符(?:):把 if-else 语句简写,使语句更加简洁
  2. guard 语句:把 if 语句的判断条件提前返回,避免不必要的判断
  3. 复合判断:&& 或 || 连接多个判断条件,减少 if 嵌套
  4. 策略模式:不同的判断条件采用不同的策略,通过策略来处理具体逻辑
  5. 重构:逻辑判断过多时,可以考虑函数拆分,提高代码可读性
  6. 缓存:避免重复判断相同条件,通过缓存已判断过的结果

webpack

websocket心跳机制,断线重连机制

websocket心跳机制:客户端和服务端通过websocket建立连接后,定期发送ping消息来检测连接是否还存活,这就是心跳机制。通过心跳机制可以及时发现连接是否中断,避免资源的浪费。

断线重连机制:当客户端通过心跳机制检测到连接已断开时,需要重连。客户端会重新发起websocket连接请求,服务端收到请求后先关闭已断开的连接,然后建立一个新的连接。这样就实现了websocket的断线重连。

异步加载方式

异步加载是webpack实现代码分割的一种手段。webpack通过import()动态导入语法来实现异步加载。使用import()语法,

import('./module').then(module => { ... })

可以在运行时,异步地加载某个模块。webpack会自动把通过import()语法导入的模块,单独打包成一个JS文件。当代码执行到import()时,会自动加载这个JS文件,然后执行 .then()里的回调函数。 通过import()可以很好地实现代码的按需加载,提高页面加载速度。这也是webpack实现懒加载的方式之一。