前端面试题整理
整理前端首屏优化、浏览器原理、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函数