2025年8月遇到前端面试题

309 阅读15分钟

8、9月份陆陆续续面试了接近10家公司,拿了2个offter,总结了下面试题,基本上都是远程面试,面试多了题目都有些相同,都自己回答的,后面总结一些。大家多准备准备,避免被问到卡壳。没有coding,也欢迎大家指正。

1.首屏加载指标

FP (First Paint)首次绘制 ,首次向屏幕绘制像素点,可以理解白屏时间

FCP(First Contentful Paint)首次内容绘制 可以是文本、img标签 ,一般来说FCP >= FP 非常接近LCP(Largest Contentful Paint)最大内容绘制,可视区域最大可见元素,衡量加载性能的核心指标

TTI(Time to Interactive)可交互时间,0~3.8s是一个较优秀的指标

基础指标使用web-vitals 结合Performance API performance.now()

2.虚拟列表实现除了滚动

1.监听scroll事件,获取滚动元素scrollTop滚动的距离,动态计算可视区域

2.使用IntersectionObserver的API,监听元素是否在视图交叉,性能比实施监听滚动事件好

3.SSE实现原理

使用node写一个setInterval定时的推送数据,数据安全base64加密

4.nestjs OCI控制反转

NestJS 是通过依赖注入的方式来实现控制反转,有利于模块之间的解藕

如果类A需要类B,类A中并不直接控制创建类B的实例。与之相反,我们从类A外部控制类B实例的创建,类A之中只负责使用类B的实例,完全无需关心类B实例是如何创建的

5.vite编写CSS注入插件实现原理

在生产环境中使用vite的钩子函数generateBundle在生成bundle时获取asset文件中的css文件,将其代码注入到打包文件中

6.动画有那些实现方式及requestAnimationFrame

一般来说动画的实现方式有一下,

1.JS控制结合requestAnimationFrame使用

2.CSS3 animation 结合@keyframes 、或者是逐帧动画steps实现更加平滑动画效果 transition

3.canvas 结合定时器动画

4.lottie

  1. Vue3.6中 vapor mode生产环境编译实现

vue3.6 vapor模式主要取消虚拟DOM,在编译阶段转换成精确的DOM指令,减少虚拟dom对内存占用,生产阶段也需要进行模版编译

8.打包工具除了webpack\vite ,还有那些

Turbopack、Rspack使用rust重写,turbopack 在 Next.js dev 环境下稳定运行,只构建实际被请求的代码,生产阶段测试版本。Rspack是字节跳动重新开发构建工具支持ts

9.markdownIt渲染原理

使用插件markdown-it实现AI在VUE中流失渲染,实际是将markdown转换成html,利用v-html实现的全量渲染。

如果要实现多模态增量渲染,使用render函数将将markdown的token转换成vnode,利用vue的diff等渲染

markdown- it渲染原理

  1. Parse:将 Markdown 文件 Parse 为 Tokens

  2. Render:遍历 Tokens 生成 HTML

10.lerna替代工具有哪些

pnpm workspace 和Changesets是Lerna的替代工具 

11.JS bridge原理

实现H5与Native之间的高效双向通信

Native -> Web:Native可以通过拼接JavaScript代码字符串,使用WebView的evaluateJavascript

Web -> Native:Web调用Native主要有以下两种方式

URL Schema:通过自定义的URL格式,将请求发送给Native

注入****JS API:通过WebView将Native功能注入到JavaScript上下文中

12.前端设计模式

1.工厂模式

通过函数或者类,封装各种方法,用户并不关心具体实现,直接调用对应的方法,例如各种公共方法实现、封装axios的实例,封装弹窗等

2.单例模式

要求一个类在整个系统中只能有一个实例,且提供一个全局访问点,例如(Vuex/Pinia):整个应用只有一个 Store 实例


3.观察者模式

观察者模式定义了对象间的一对多依赖关系:当 “目标对象” 的状态发生改变时,所有依赖它的 “观察者对象” 会自动收到通知并更新

 Vue 响应时原理,事件监听addEventListener

4.发布订阅模式

发布订阅模式是观察者模式的 “升级版”,它通过一个 事件中心 中间件,让发布者和订阅者完全解耦 

如 跨组件通信(如 Vue 的 EventBus、React 的 Context)状态管理库(如 Redux):Action(发布者)触发后,Store(事件中心)通知 Reducer 更新状态,组件(订阅者)感知状态变化


5.代理模式

代理 对象,外界通过代理对象访问原对象,代理可以在访问前后添加额外逻辑

Vue3的响应式

  1. vuex、pina、redux状态机设计原则

1.单一数据源,所有的数据存放到统一的store中

2.状态是只读的,不能直接修改 Store 中的状态。改变状态的唯一方法是触发( dispatch )一个动作( Action ****

3.纯函数修改  Reducer/Mutation处理

14.JS词法作用域

1.全局作用域,顶层全局使用,window对象

2.函数内部作用域,函数内部词法环境访问生效

3.块级作用域,{ } let const 中括号内部使用

15.JS模块化设计,commonJs实现treeShake

Tree-shaking的消除原理是依赖于ES6的模块特性,ES6模块依赖关系是确定的,和运行时的状态无关,可以进行可靠的静态分析,这就是tree-shaking的基础

import 都是在顶层引入,依赖分析编译阶段进行treeShake,

commonJs模块关系是动态引入的,本身不支持treeShake,只能通过插件或者打包转换成ES module根式进行treeShake

16.rollup原理

第一步:从入口点开始,构建模块依赖图

第二步:作用域提升和 Tree-Shaking

第三步:代码生成和输出

16.v-model实现原理

v-model作用于组件上本质就是一个语法糖,就是往组件传入了一个名为modelValue 的 prop,它的值是往组件传入的数据 data,另外它还在组件上监听了一个名为 update:modelValue 的自定义事件,事件的回调函数接受一个参数,执行的时候会把参数 $event 赋值给数据 data

17.vue中key作用

Key的作用主要是为了高效的更新虚拟dom,其原理是vue在patch过程中通过key可以精准判断两个节点是否是同一个,从而避免频繁更新不同元素

18.vue2和vue3区别

1.响应式区别

vue2使用Object.definedProperty对对象属性进行数据劫持,不支持数组,数组pop、push方法重写,对象属性新增、删除不支持响应式

Vue3通过proxy代理整个对象,不需要遍历每一个属性,有利于性能提升,支持多种数据类型map\Set

2.Diff算法不同

Vue3给每个虚拟节点添加一个 patchFlags,patchFlags 就是优化的标识。 只会比较 patchFlags 发生变化的 vnode,进行更新视图,对于没有变化的元素做静态标记

3.写法不同

vue2 是选项式 API,代码里分割了不同得属性:data,methods,computed 等  vue3 是组合式 API相同的业务的数据方法写在同一块代码区域

4.性能优化提升

Vue3在编译阶段进行静态提升,缓存事件处理函数,在运行时体积更小,支持TS、结合VITE支持treeShake

  1. vue和react 区别 ,react中fiber

1.写法上

模版语法和JSX

react偏向于函数式编程

2.响应式及状态数据

Vue支持双向绑定数据,更加精细化控制组件数据更新变化、颗粒度更新

React 自顶向下更新数据,父组件数据更新,会导致子组件重新渲染,数据不可变,需要手动显式调用setState数据更新

3.适用范围

Vue中小型项目、需要快速原型

React超大型复杂应用、需要高度定制架构

react16提出fiber概念

Fiber 是 React 16 中采用的新协调(reconciliation)引擎,主要目标是支持虚拟 DOM 的渐进式渲染

解决阻塞问题:将同步递归渲染改为异步可中断任务,避免长时间占用主线程导致页面卡顿。

优先级调度:区分高/低优先级任务(如用户交互 vs 数据加载),确保高优先级任务优先执行。

增量渲染:将渲染任务拆分为多个小单元(时间片),在浏览器空闲时逐步完成。 下面是一段JSX 代码到 DOM 节点的转换过程

原理将树形虚拟DOM结构,改成链表结构,增加父节点、子节点、兄弟节点,每次渲染节点指向对应的链表节点,结合屏幕刷新率,更加流畅进行渲染

20.vite打包优势

开发环境:

不用打包,支持ES module ,直接在浏览器运行编写模块

增量hot更新

Esbuild 预构建nodemoudlemo模块,go语言编写执行效率比webpack高

生产环境 rollup打包 支持treeShake

  1. react优化性能useMemo 和useCallback区别

useMemo根据依赖数组变化缓存的计算数值

useCallback根据依赖数组变化缓存的函数

22.vue ref和reactive区别,reactive响应式失效

Ref 更多使用基础数据,需要.value获取值

Reactive使用于对象复杂数据,数据解构或者直接赋值就失去响应式

23.vue3 setup语法 defineComponent

defineComponent 主要用于定义 Vue 组件,支持 Options API 和 Composition API 两种风格,兼容vue2的写法,支持TS类型推导

setup是编译时语法糖,能大幅简化 Composition API 的写法,编译之后转换成defineComponent形式

24.nextTick 原理,一定是微任务吗? setTimeOut不行吗?为什么有了setTimeOut,还要有nextTick

在DOM更新之后执行某些操作的需求。Vue.js 提供了一个非常有用的工具叫做 nextTick,它能确保我们的回调函数在所有数据更改被应用到 DOM 之后执行

nextTick实际上是利用JS的事件循环机制,将传入的回调函数作为异步任务执行,nextTick 的核心是利用了如 Promise 、MutationObserver、setImmediate、setTimeout的原生 JavaScript 方法来模拟对应的微/宏任务的实现,本质是为了利用 JavaScript 的这些异步回调任务队列来实现 Vue 框架中自己的异步回调队列

Vue 的 DOM 更新队列本身也是基于微任务的。nextTick 会将回调加入到同一个微任务队列中,严格保证在 DOM 更新循环结束后执行

Vue.nextTick 也是将回调推入微任务队列。这就保证了:数据变更 -> DOM 更新任务(微任务)-> nextTick 回调(微任务) 这个顺序是严格且高效的

为什么要有nextTick,不使用setTimeout,因为 nextTick 在效率、时机和可靠性上都优于 setTimeout,Vue 的内部更新机制和 nextTick 都是基于微任务实现的,setTimeout 是一个宏任务,它会被排到下一个事件循环。当它执行时,虽然 DOM 也确实更新了,但它等得太久了,错过了当前循环的最佳时机

Vue2 实现方案

nextTick 的实现与降级策略

它的优先级顺序通常是:

  1. Promise.then (最理想的微任务)
  2. MutationObserver (也是一个微任务,可用于监听 DOM 变化)
  3. setImmediate (IE 和 Node.js 中的宏任务,但比 setTimeout 快)
  4. setTimeout(fn, 0) (最后兜底的宏任务)

这意味着,在绝大多数现代浏览器中,nextTick 使用的是 Promise,它是一个微任务。只有在某些旧浏览器中,它才会降级到 setTimeout

Vue 3 彻底拥抱了现代浏览器,其源代码中直接使用 Promise.then() 来创建异步更新队列

25.前端适配不同尺寸页面

移动端适配

使用rem, 以根源素font-size的为基准,控制元素大小

Vm或者vh视口的大小按照百分比控制

PC端

@media 适配不同大小,写兼容CSS样式

使用flex 、grid弹性布局样式等

26.JS事件循环机制

JavaScript是一门单线程脚本语言,这意味着JavaScript中的所有代码都是按照顺序一行一行执行的。

为了处理异步操作,JavaScript引入了事件循环机制(Event Loop)来协调异步操作和同步代码的执行。

事件循环的基本流程如下:

  1. 主线程执行同步任务,直到遇到异步任务时,将其回调函数添加到任务队列中,然后继续执行同步任务。

  2. 当所有同步任务执行完成后,主线程会立即去任务队列中查找是否有已经完成的异步任务的回调函数需要执行,如果有,则会按照回调函数添加的先后顺序执行它们。

  3. 执行完所有已经完成的异步任务回调函数后,重复步骤2,直到任务队列中没有任何任务

在任务队列中,异步任务被分为微任务和宏任务两种类型。宏任务通常由定时器任务、I/O任务、UI渲染任务等操作触发,而微任务通常由 Promise、MutationObserver 等异步操作触发的回调函数组成

Event Loop(事件循环)中,每一次循环称为 tick, 每一次tick的任务如下:

  • 执行栈选择最先进入队列的宏任务(通常是script整体代码),如果有则执行
  • 检查是否存在 Microtask,如果存在则不停的执行,直至清空 microtask 队列
  • 更新render(每一次事件循环,浏览器都可能会去更新渲染)
  • 重复以上步骤

宏任务 > 所有微任务 > 宏任务

27.http缓存机制

HTTP的缓存属于客户端缓存,缓存分为强制缓存和协商缓存

强缓存命中的话不会发请求到服务器

Cache-Control 

max-age: 设置缓存的存在时间

no-store: 禁止缓存

no-cache 表示请求必须先与服务器确认缓存的有效性

Expires

Exprires的值为服务端返回的数据到期时间,当再次请求时的请求时间小于返回的此时间,则直接使用缓存数据。但由于服务端时间和客户端时间可能有误差,这也将导致缓存命中的误差


协商缓存一定会发请求到服务器,通过资源的请求首部字段验证资源是否命中协商缓存,如果协商缓存命中,服务器会将这个请求返回,但是不会返回这个资源的实体,而是通知客户端可以从缓存中加载这个资源(304 not modified)

Last-Modified


服务器在响应请求时,会告诉浏览器资源的最后修改时间。

if-Modified-Since: 浏览器再次请求服务器的时候,请求头会包含此字段,后面跟着在缓存中获得的最后修改时间。服务端收到此请求头发现有if-Modified-Since,则与被请求资源的最后修改时间进行对比

Etag

服务器响应请求时,通过此字段告诉浏览器当前资源在服务器生成的唯一标识

If-None-Match: 再次请求服务器时,浏览器的请求报文头部会包含此字段,后面的值为在缓存中获取的标识。服务器接收到次报文后发现If-None-Match则与被请求资源的唯一标识进行对比

28.前端性能优化

JavaScript优化策略

使用Web Worker处理复杂计算

requestAnimationFrame优化动画

JS文件异步加载 defer async 

DOM操作优化

减少重排和重绘

使用CSS3 transform和opacity属性实现动画(触发合成层,不引起重排)

网络优化

使用HTTP/2

减少、合并HTTP请求,通过合并CSS、JS文件、精灵图等方式减少请求数量。压缩文件, 开启nginx,Gzip对静态资源压缩

使用HTTP缓存,如强缓存、协商缓存使用CDN,将网站资源分布到各地服务器上,减少访问延迟

打包工具优化

Code splitting 、treeShake

首屏加速优化 

SSE渲染

Vue框架

路由的懒加载、组件异步动态加载等

极致优化 引入wasm文件

WebAssembly 提供高效的二进制格式,优化了代码的执行速度和资源利用率

虚拟列表使用

29.promise原理,async, await会阻塞吗?

Promise的核心思想是解决“回调地狱”,它代表一个异步操作的最终完成(或失败)及其结果值。它本质上是一个状态机,状态转换是不可逆的:一旦从Pending变为Fulfilled或Rejected,状态就凝固了,不会再改变,并提供了链式调用的能力。

then 方法:用于注册Promise状态确定后的回调函数。它返回一个新的Promise

async / await不会阻塞 JavaScript 的主线程

await 只会“暂停”其所在的特定 async 函数内部的执行流程,当主线程的调用栈清空后,事件循环会去执行微任务队列中的任务,这时才会回到 async 函数中,继续执行 await 之后的代码。

async/await 的本质是 Generator 生成器函数和 Promise 的语法糖