2024-11-01|
前端面试性能优化JavaScript网络

前端面试题整理

整理前端首屏优化、浏览器原理、JavaScript、网络和工程化等常见面试题。

面试题

页面首屏加载时间优化手段

构建优化(减少构建体积,分包)

  • 路由懒加载

  • Tree Shaking

  • 代码分割

网络优化

  • CND

  • http2

  • preload/prefetch/preconnect

渲染优化

  • 关键渲染路径

  • 图片懒加载+webP类型的图片

  • 骨架屏减少用户等待焦虑

运行时优化

  • 虚拟滚动

  • 防抖截流

  • web worker

React和Vue是如何描述UI的,有什么区别

React使用jsx

  • React团队提供一个类似的XML的ES语法糖,再经过Babel编译后变成

    • V17之前 React.createElement("h1",null,"hell") V17之后 const jsxRuntime = require("react/js-runtime") jsxRuntime.jsx("h1",{children:"hello"})

      • 无论哪个版本在代码执行以后,返回的都是一个对象

        • { type:"h1", key:null, ref:null, props:{ children:"hello" } _owner:null, _store:{} }
  • React团队觉得UI本质上和逻辑存在耦合部分,作为前端工程师,JS 使用时最多的,如果同时使用JS 来描述UI,就可以让UI 和逻辑更加紧密

Vue使用 模版

  • 模版语法的历史要从后端说起,不同的后端有不同的模版引擎,在HTML中添加了逻辑语法,之后再动态的填充数据进去,如果看了VUE的模版语法其实是和后端的各种模版引擎非常相似的

    • 模版语法的出发点,既然前端框架使用HTML 来描述UI ,那么就扩展js语法,使他既可以描述UI,又可以填充逻辑

现在前端框架不仅仅是React ,Vue,还有一些新框架有什么区别呢

元素级框架

  • 细分主题 1

组件级框架

  • Vue

应用级框架

  • React

虚拟DOM

虚拟DOM 追早是由React 团队提出的,它是一种编程概念,在JS 中是以对象来描述的虚拟DOM 层次,

为什么需要虚拟DOM

  • 相较于DOM 的体积优势和速递优势

    • JS 层的计算速度比DOM 层面的计算要快

      • DOM 对象在被浏览器渲染之前需要很多事情要做

      • DOM对象上有很多属性

      • 虚拟DOM 发挥优势是在更新的时候,虚拟DOM 能够做到针对DOM 节点最小程度的修改

  • 多平台的抽象能力

    • 浏览器使用ReactDOM

    • Native宿主环境使用React-Native包

    • Canvas,svg 或者VML(IE8)宿主环境使用ReactArt

    • ReactTest 包用于渲染出JS 对象,方便测试

  • React 中,通过JSX来描述UI,JSX仅仅是一个语法糖,会被Babel 编译为CreateElement 方法的调用,该方法会返回一个JS对象,该对象就是虚拟DOM,官方更倾向于成为React元素

是否了解过Fiber架构,相较于之前的Stack架构有什么优势

V15之前的架构(Stack 架构)

  • Reconciler(协调器): VDOM 的实现,负责根据自变量变化计算出UI的变化

    • 在Reconciler中,mount 的组件会调用mountComponent,update 的组件会调用updateComponent,这两个都是递归调用,一旦开始无法中断

      • CPU 瓶颈

        • VDOM 在比较差异时采用递归方式,js 计算消耗大量时间,从而导致画面卡顿,还有一些需要实时更新的内容产生卡顿
      • I/O 瓶颈

        • 各种自变量变化时,更新任务没有优先级,因此例如输入内容让用户感觉卡顿
  • Renderer(渲染器): 负责将UI的变化渲染到宿主环境中

React 的渲染流程

整体氛围两个阶段

  • Render阶段

    • 调度器(Scheduler)

      • 主要工作是调度任务,,让所有的任务有优先级的概念,紧急任务可以优先执行,调度器在浏览器只有类似的API (requestIdleCallback) 但是有兼容性问题,React团队自己实现来一个这个机制,后面会把这个Scheduler 包单独发布,,其他有任务调度需求也可以时间调度器
    • 协调器(Reconciler)

      • 协调器是Render 的第二阶段工作,该阶段采用深度优先的遍历方式,并创建出一个个的FiberNode,

        • 并将其串联起来,在遍历时采用递和归两个阶段

          • 递阶段 执行beginWork,该方法会根据传入的FiberNode 创建下一层FiberNode

          • 归阶段: 执行completeWork, 收集副作用

  • Commit阶段

    • 渲染器(Renderer)

      • 主要负责将各种副作用(flags)commit 到宿主环境的UI 中,这个阶段分为三个子阶段

        • BeforMutation 阶段

        • mutation阶段

        • Layout阶段

Fiber 是什么

是一种结构 称为Fiber

  • V16之后引入,各个FIber 通过链表方式串联起来的

是一种数据结构

  • 本质是一个JS 对象,在React元素基础上的升级版本,每一个FIberNode对象都会包含一个React元素类型,周围链接的FiberNode以及DOM 相关信息

动态的工作单元

  • 在每个FiberNode中,保存来本次更新中React元素变化的数据,还有就是需要执行的工作以及副作用信息(增加,删除,更新)

Fiber 双缓冲区

原理和显卡工作原理一样

  • 显卡分为前缓冲区和后缓冲区,前缓冲区会显示图像,之后合成新图像会被写入后缓冲区,一旦后缓冲区写入完毕,前后缓冲区进行互换,将这种数据保存在缓冲区,再进行互换称为双缓冲区

    • React中存在两颗Fiber Tree, 一颗是真实UI对应的FIberTree,类比显卡的前缓冲区 另一颗内存中构建的FiberTree

    • React源码中有很多使用双fiber的场景

    • 细分主题 3

谈谈Computed 机制,缓存来什么,为什么computed不支持异步

响应式系统

  • Track: 进行收集依赖,建立数据和函数的映射关系

  • Trigger: 触发更新,重新执行数据所映射的所有函数

缓存的是上一次Getter算出来的值

为什么不支持异步

  • 主要原因,设计上的理念和使用场景上的考虑

  • 细分主题 2

Watch 使用场景

用于监听数据变化,可以监听一个或者多个数据,当数据变化时执行一些用户的操作

副作用,监听器中的回到函数可以执行副作用操作,如发送网络请求,搜动DOM操作

支持异步

VUE3的diff和Vue2的有什么区别

Diff概念

  • diff算法用户比较两个虚拟DOM的算法,目标是寻找之间的差异,并根据差异高效的更新真实DOM,从而保证页面在数据变化后,进行最小程度的DOM操作

Diff 流程

  • 从跟节点对比,查看是否相同

  • VUE2

    • 对比细节

      • 层层对比

        • 旧头新头对比

          • index++
        • 旧尾新尾对比

          • index--
        • 旧头新尾对比

          • 将旧头移动到尾部,新尾--
        • 旧尾新头对比

          • 将尾部元素移动到头部,新头++
        • 暴力比较(查找新头对应的节点)

          • 找到之后,将元素移动到旧头之前,新头++

          • 没有找到,就创建新的DOM 插入到旧头之前 新头++

        • 最后,旧节点有剩余,全部删除旧Dom 新节点列表有剩余,都新增节点

  • VUE3

    • 快速查找,使用最长递增子序列(贪心算法+二分查找)

      • 头头对比

      • 尾尾对比

      • 非复杂情况处理

        • 经历了头头对比尾尾对比之后,新旧列表任意一方结束,测试还会出现两种秦光

          • 旧节点列表有剩余

            • 旧节点全部删除
          • 新节点有剩余

            • 创建DOM,放在新节点头之前
          • 新旧节点都有剩余的情况

            • 初始化一个 keyToNewIndexMap

              • 便利还未处理的新节点,将下标维护进容器中 key-index
            • 初始化一个newIndexToOldIndexMap

              • 新建一个和未处理新节点个数一样长度的数组,每一项都填充0

              • 是为了有面计算,新节点是否在旧节点中

              • 如果计算之后,还有0的,则表示旧节点中没有当前节点

            • 更新newIndexTOOldIndexMap

              • 超找旧节点中是否有新节点的内容,有的将0改为1

              • 细分主题 2

      • 复杂情况处理

React 和VUE中key 有什么做分别有什么区别

React

  • 细分主题 1

Vue3的响应式和Vue2的是否有改变,有哪些具体改变

1. 数据拦截的变化

  • VUE2 使用Object.definPropety 进行拦截

    • 针对对象的特定属性的读写操作进行拦截
  • VUE3 使用Proxy+ Object.definpropety

    • Proxy针对对象针对拦截,属性的读取,赋值,属性的删除增加

2. 创建响应式数据的变化

  • 通过data来创建响应式

  • VU3 通过Ref Reactive

3. 依赖收集的变化

  • VU2 Watcher+Dep

    • 每个响应式属性都有一个Dep 实例,用于依赖收集,内部包含一个数组存储这个属性的Watcher

    • 属性改变后,dep 会通知watcher 做更新操作

  • VUE3: WeakMap+Map+set

    • weakMap 键对应的事响应式对象,,值是一个Map,,这个Map 的键是对象的属性,值是Set,set中存储了这个属性的effect函数