跳到内容
当前位置:首页>面试真题>前端面试终极36题!Vue+React+TS+JS算法全套高频真题(附可运行代码)

前端面试终极36题!Vue+React+TS+JS算法全套高频真题(附可运行代码)

  • 2026-09-18 17:16:44
前端面试终极36题!Vue+React+TS+JS算法全套高频真题(附可运行代码)
很多前端面试翻车,核心原因不是不会技术,而是知识点零散、同类问题混乱、手写代码不熟练、答不到面试官得分点
目前初/中级前端面试固定四大核心板块:Vue核心全家桶、React+Hooks工程化、JS底层原理+手写算法、TypeScript高频考点
今天我把所有零散考点统一归类重组,同类知识点集中讲解,复杂题型附带可运行代码示例,直接背诵、默写即可通关面试!
适合:秋招春招突击、跳槽面试复盘、前端基础查漏补缺、技术面刷题冲刺

一、Vue 全家桶核心面试题(1-10题)

1. Vue2 和 Vue3 核心区别

标准答案:

响应式原理不同:Vue2 基于 Object.defineProperty劫持对象属性;Vue3 基于 Proxy劫持整个对象,支持数组、新增/删除属性监听。

编程范式不同:Vue2 选项式API,代码分散;Vue3 组合式API,逻辑聚合、复用性更强。

生命周期差异:Vue3 废弃 beforeCreate/created,统一在setup中编写,生命周期函数需手动导入。

性能优化升级:Vue3 新增静态提升、PatchFlag标记、事件缓存,大幅减少DOM比对开销。

TS支持:Vue3 原生TS重构,类型推导完善;Vue2对TS适配简陋。

2. Vue3 Proxy 对比 Vue2 defineProperty 的核心优势

标准答案:

可监听数组所有原生变更(新增、删除、下标修改),无需Vue2重写数组方法的hack方案。

可监听对象新增、删除属性,无需 $set手动处理响应式。

  • 一次性劫持整个对象,无需遍历所有属性,初始化性能更好。
  • 支持 Map、Set 等复杂数据结构监听,适配复杂业务场景。

3. Vue 双向绑定原理 + 极简手写代码

核心原理:数据劫持 + 视图更新 + 事件监听,数据驱动视图,视图反向更新数据。
// 极简Vue双向绑定实现class MiniVue {  constructor() {    this.data = { name'' }    this.observe(this.data)  }  // 数据劫持  observe(obj) {    Object.keys(obj).forEach(key => {      let value = obj[key]      Object.defineProperty(obj, key, {        get() {          return value        },        set(newVal) {          value = newVal          // 数据更新,同步刷新视图          document.getElementById('input').value = newVal          document.getElementById('text').innerText = newVal        }      })    })  }}// 实例化使用const vm = new MiniVue()// 视图变更同步更新数据document.getElementById('input').addEventListener('input'(e) => {  vm.data.name = e.target.value})

4. Vue 完整生命周期及业务使用场景

高频背诵版:

onBeforeMount:DOM未挂载,无法操作DOM

onMounted:DOM挂载完成,适合接口请求、初始化定时器、操作DOM

onBeforeUpdate:数据更新前触发

onUpdated:数据&DOM更新完成,可获取最新DOM结构

onBeforeUnmount:组件卸载前,清除定时器、解绑事件,防止内存泄漏

onUnmounted:组件完全卸载

5. Vue 所有组件通信方式(全覆盖)

父子通信:父传子 props、子传父 emit

祖孙跨级通信:provide / inject

兄弟/全局跨级:Vuex / Pinia 全局状态管理

简单轻量跨级:mitt 事件总线

特殊通信:$refs 获取子组件实例、$attrs 透传属性

6. Vue3 setup 语法糖核心特点

  • 无需手动 return 变量、方法,模板可直接使用,代码极简
  • 默认开启严格模式,规避不规范写法
  • 业务逻辑高度聚合,适合复杂组件拆分
  • 对TS类型推导极其友好,适配大型项目开发

7. v-if 和 v-show 核心区别与使用场景

v-if:真实销毁/重建DOM节点,切换开销大、初始渲染开销小,适合极少切换、一次性渲染场景

v-show:仅通过CSS display属性切换显隐,DOM不销毁,初始开销大、切换开销小,适合频繁切换场景

8. Vuex 五大核心属性 + 完整实操代码

核心属性作用

state:全局数据源,存储公共状态

mutations:唯一同步修改state的入口

actions:处理异步逻辑,通过commit调用mutations

getters:全局计算属性,缓存加工state数据

modules:模块化拆分,解决state臃肿问题

完整执行流程:组件dispatch actions → 异步处理 → commit mutations → 修改state → 视图更新
// store/index.js Vuex完整配置import Vue from 'vue'import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({  state: {    userName'Vuex测试用户',    count0  },  mutations: {    setCount(state, num) {      state.count = num    },    setUserName(state, name) {      state.userName = name    }  },  actions: {    // 异步模拟接口请求    async getUserInfo({ commit }) {      const res = await Promise.resolve('后端异步获取用户名')      commit('setUserName', res)    }  },  getters: {    userText(state) => `${state.userName},当前计数:${state.count}`  },  modules: {}})
// 组件中使用Vuexexport default {  methods: {    // 同步修改状态    changeCount() {      this.$store.commit('setCount'100)    },    // 异步修改状态    async getUser() {      await this.$store.dispatch('getUserInfo')    }  }}

9. Vuex 与 Pinia 核心区别(面试必考对比)

对比维度

Vuex

Pinia

适配版本

Vue2主力,Vue3兼容

Vue3官方推荐,完全替代Vuex

核心结构

五大核心,结构繁琐冗余

舍弃mutations,仅保留state/getters/actions,极简

同步异步

同步、异步强制拆分

actions支持同步+异步,无需拆分

TS支持

类型推导繁琐,需额外适配

原生TS重构,自动类型推导

模块化

需手动开启命名空间,嵌套复杂

天然模块化,每个仓库独立隔离

使用体验

代码冗余、写法繁琐

API极简,支持直接修改state

10. Pinia 极简完整实操代码(可直接默写)

// stores/user.ts Pinia仓库import { defineStore } from 'pinia'export const useUserStore = defineStore('user', {  // 全局状态  state() => ({    userName'前端开发者',    age22  }),  // 计算属性  getters: {    userInfo(state) => `${state.userName}-${state.age}岁`  },  // 同步/异步方法统一存放  actions: {    // 同步修改    setName(namestring) {      this.userName = name    },    // 异步修改    async fetchUser() {      const res = await Promise.resolve('异步请求用户数据')      this.userName = '异步更新用户名'    }  }})

二、React + Hooks 核心面试题(11-20题)

11. React 类组件与函数组件核心区别

类组件:基于class定义,自带生命周期、存在this指向问题、代码冗余复杂

函数组件:纯函数设计,无this干扰,配合Hooks实现状态和生命周期,代码简洁轻量化

现状:目前企业级React项目全部采用函数组件+Hooks开发

12. React 常用Hooks作用及使用场景

useState:定义组件响应式状态,实现数据更新视图刷新

useEffect:处理组件副作用(接口请求、定时器、DOM操作、事件监听)

useRef:获取真实DOM、保存组件不变的变量

useMemo:缓存复杂计算结果,避免每次渲染重复计算

useCallback:缓存函数,避免子组件无效重渲染

useContext:实现跨层级组件状态共享

13. useEffect 依赖项规则及高频坑点

执行规则

空依赖数组:组件挂载、卸载时各执行一次

有依赖项:依赖项数值/引用变化,重新执行副作用

无依赖参数:组件每次渲染都会执行

高频坑点:依赖项为对象、数组等引用类型时,每次渲染地址不同,会导致无限循环渲染

14. React 组件更新机制及性能优化方案

更新机制:React默认父组件更新,所有子组件无条件重渲染,极易产生性能浪费。
三大优化方案

React.memo:组件缓存,props未变化时,组件不重渲染

useCallback:缓存父组件传递的函数props,保证引用地址不变

useMemo:缓存复杂计算结果,避免重复计算

15. 虚拟DOM与Diff算法、key作用、Vue/React差异(标准精简版)

一、虚拟DOM原理
虚拟DOM是用JS对象模拟真实DOM的树形结构,是DOM的抽象描述。核心作用是减少频繁的真实DOM操作:数据变化生成新虚拟DOM,通过Diff算法对比新旧差异,只更新局部变化DOM,减少浏览器重排重绘,提升页面渲染性能。
二、React Diff算法核心优化(O(n)复杂度)
Diff算法放弃低效的树形全量比对,通过三大策略大幅优化性能:

同层比对:仅对比同级节点,不跨层级比对,贴合业务场景

类型优先校验:节点类型不同直接销毁重建,不做复用

Key唯一匹配:通过key绑定节点身份,实现节点精准复用、移位、删除

三、Key的作用 & index作为key的弊端

Key作用:标记节点唯一性,辅助Diff精准匹配节点,减少无效DOM渲染,提升更新性能。

index弊端:列表存在删除、前置新增、排序时,index会偏移,导致节点复用错乱、视图数据错位、产生多余DOM更新。

四、Vue与React虚拟DOM核心差异

Diff粒度:Vue Diff更精细,可对比属性、样式、文本;React仅做节点层级比对。

更新机制:Vue依靠响应式精准定位更新组件;React自上而下全量渲染,需手动优化。

编译优化:Vue3有PatchFlag、静态提升等编译优化,只比对动态节点;React无编译优化,纯运行时Diff。

五、为什么不推荐用 index 作为 key?(高频面试坑点)

造成性能损耗,违背Diff复用节点的设计初衷。

最佳实践:优先使用后端返回唯一ID、uuid作为列表key。

16. Vue 与 React 全方位核心区别

编程思想:Vue 模板驱动、声明式编程;React JSX语法、函数式编程

响应式机制:Vue 自动依赖收集,精准更新;React 自上而下强制渲染,需手动优化

更新粒度:Vue 组件级精准更新;React 整树自上而下渲染

上手难度:Vue低门槛、易上手;React工程化更强、门槛更高

17. React 受控组件与非受控组件(带代码)

核心区别:数据控制权不同

受控组件:表单值由state管控,完全可控,企业开发主流

非受控组件:表单值由DOM管控,通过ref取值,适合简单一次性场景

import { useState, useRef } from 'react'// 受控组件function ControlledInput() {  const [value, setValue] = useState('')  return (    <input      value={value}       onChange={(e) => setValue(e.target.value)}       placeholder="受控组件"    />  )}// 非受控组件function UnControlledInput() {  const inputRef = useRef(null)  const getValue = () => {    console.log(inputRef.current.value)  }  return (    <>      <inputref={inputRef}placeholder="非受控组件"/>      <buttononClick={getValue}>获取值</button>    </>  )}

18. React Props 只读特性及正确修改方式

核心考点:React 单向数据流规则,子组件绝对不能直接修改props,会破坏数据稳定性。
正确方案:子组件触发父组件传递的修改函数,由父组件修改原始state数据。
import { useState } from 'react'// 父组件function Parent() {  const [msg, setMsg] = useState('父组件原始数据')  return <Childmsg={msg}changeMsg={setMsg} />}// 子组件function Child({ msg, changeMsg }) {  return (    <div>      <p>接收数据:{msg}</p>      <buttononClick={() => changeMsg('父组件数据已更新')}>修改数据</button>    </div>  )}

19. 自定义Hooks 规范 + 防抖实战示例

开发规范:必须以use开头命名、仅能在组件/其他Hooks中调用、用于复用通用逻辑。
import { useState, useEffect } from 'react'// 自定义防抖Hooksexport default function useDebounceValue(value, delay = 500) {  const [debounceVal, setDebounceVal] = useState(value)  useEffect(() => {    const timer = setTimeout(() => {      setDebounceVal(value)    }, delay)    return () => clearTimeout(timer)  }, [value, delay])  return debounceVal}// 业务组件使用function SearchInput() {  const [val, setVal] = useState('')  const debounceVal = useDebounceValue(val)  return <inputvalue={val}onChange={e => setVal(e.target.value)} placeholder="防抖搜索"/>}

20. useMemo 与 useCallback 核心作用

底层本质:依赖项对比机制,依赖不变则复用缓存值/函数,依赖变化则重新生成。

useMemo:缓存计算结果,避免复杂计算重复执行

useCallback:缓存函数本身,配合React.memo避免子组件无效更新

三、JS 底层原理 + 手写算法(21-28题)

21. this指向底层原理 + 五大绑定规则

核心铁律this指向由函数调用方式决定,和定义位置无关,谁调用指向谁。
优先级从低到高:默认绑定 < 隐式绑定 < 显式绑定 < new绑定 < 箭头函数绑定
let name = 'window'const obj = {  name'obj对象',  fnfunction() {    console.log(this.name)  },  arrowFn() => {    console.log(this.name)  }}obj.fn() // 隐式绑定:obj对象const f = obj.fnf() // 默认绑定:windowobj.arrowFn() // 箭头函数:继承外层this = window

22. call / apply / bind 区别 + 实操案例

共同点:均为显式修改函数this指向。

call:立即执行,参数逐个传入

apply:立即执行,参数以数组形式传入

bind:不立即执行,返回新函数,支持柯里化传参

function test(a, b) {  console.log(this.name, a, b)}const obj = { name'测试对象' }test.call(obj, 1020)test.apply(obj, [3040])const newTest = test.bind(obj, 5060)newTest()// 经典妙用:数组取最大值const arr = [1,9,3,7,5]console.log(Math.max.apply(null, arr))

23. 手写防抖函数(面试必考)

适用场景:搜索输入、窗口resize、表单高频输入
function debounce(fn, delay = 300) {  let timer = null  return function(...args) {    if(timer) clearTimeout(timer)    timer = setTimeout(() => {      fn.apply(this, args)      timer = null    }, delay)  }}

24. 手写节流函数(面试必考)

适用场景:页面滚动、拖拽、高频点击
function throttle(fn, interval = 300) {  let flag = false  return function(...args) {    if(flag) return    flag = true    fn.apply(this, args)    setTimeout(() => {      flag = false    }, interval)  }}

25. 数组去重(最优解+兜底写法)

// ES6最优最简写法function uniqueArr(arr) {  return [...new Set(arr)]}// 传统遍历兜底写法function uniqueArr2(arr) {  const res = []  arr.forEach(item => {    if(!res.includes(item)) res.push(item)  })  return res}

26. 数组扁平化(递归手写+原生API)

// 递归手写扁平化function flatArr(arr) {  let res = []  arr.forEach(item => {    Array.isArray(item) ? res = res.concat(flatArr(item)) : res.push(item)  })  return res}// 原生极简APIarr.flat(Infinity)

27. 手写深拷贝(进阶面试题)

function deepClone(obj) {  // 基础类型直接返回  if(typeof obj !== 'object' || obj === nullreturn obj  // 区分数组和对象  const newObj = Array.isArray(obj) ? [] : {}  // 递归遍历拷贝  for(let key in obj) {    if(obj.hasOwnProperty(key)) {      newObj[key] = deepClone(obj[key])    }  }  return newObj}

28. 冒泡排序 + 快速排序(两大必考排序)

// 1. 冒泡排序(基础必背)function bubbleSort(arr) {  const len = arr.length  for(let i = 0; i < len - 1; i++) {    for(let j = 0; j < len - 1 - i; j++) {      if(arr[j] > arr[j+1]) {        [arr[j], arr[j+1]] = [arr[j+1], arr[j]]      }    }  }  return arr}// 2. 快速排序(进阶最优排序 O(nlogn))function quickSort(arr) {  if(arr.length < 2return arr  const pivot = arr[0]  const left = [], right = []  for(let i = 1; i < arr.length; i++) {    arr[i] < pivot ? left.push(arr[i]) : right.push(arr[i])  }  return quickSort(left).concat(pivot, quickSort(right))}

四、TypeScript 高频面试题(29-36题)

29. interface 和 type 核心区别

扩展性:interface 支持声明合并;type 不支持重复声明

能力范围:type 支持联合、交叉、字面量等复杂类型;interface仅用于定义对象结构

继承方式:interface 通过extends继承;type 通过&交叉继承

使用场景:组件Props、对象结构用interface;复杂类型、工具类型用type

// interface声明合并interface User { namestring }interface User { agenumber }// 最终合并:{name:string, age:number}// type联合类型type Status = 'success' | 'error' | 'loading'

30. TS 泛型作用 + 泛型约束(extends)

泛型作用:定义动态类型,实现类型复用、规避any,保证类型安全。
// 基础泛型function getArr<T>(arr: T[]): T[] {  return arr}// 泛型约束:限制必须包含length属性function getLength<T extends {lengthnumber}>(val: T): number {  return val.length}getLength('123'// 合法getLength([1,2,3]) // 合法getLength(123// 不合法,number类型没有length属性

31. any / unknown / never / void 四类型区别

void:函数无返回值,仅修饰函数返回

never:永远无返回值(报错、死循环),代表类型不存在

any:关闭TS类型校验,项目禁止滥用

unknown:安全的any,必须类型校验后才能使用

32. 可选链 ?. 与 空值合并 ?? 详解

const obj = { name'前端' }// 可选链:安全读取深层属性,不报错console.log(obj?.age?.num)// 空值合并:仅null/undefined生效,0、''、false不覆盖const n1 = 0 ?? 100 // 0const n2 = null ?? 100 // 100// 对比||:会误判0、空字符串const n3 = 0 || 100 // 100

33. 联合类型 | 和 交叉类型 & 区别

联合类型 |或,满足其一即可(多选一)

交叉类型& 且,合并所有属性,必须全部满足

// 联合类型type Status = 'success' | 'error'// 交叉类型interface A { namestring }interface B { agenumber }type C = A & Bconst person: C = { name'test'age22 }

34. TS 类型断言作用

用于TS类型推导不准确时,手动告知变量类型,仅编译阶段生效,不改变运行时代码。
语法:变量 as 类型

35. TS 类型守卫作用

通过逻辑判断缩小变量类型范围,让TS自动精准推导类型,替代粗暴的类型断言,代码更安全。

36. TS 在项目中的核心价值

  • 编译阶段校验类型,规避线上低级BUG
  • 类型约束,统一团队代码规范
  • 自带代码提示,提升开发效率
  • 适配大型项目,方便重构和维护

面试高分答题总结

1、框架类题目:先讲原理、再讲区别、最后讲业务场景,不要单纯罗列知识点;
2、手写代码题目:优先写最简最优解,面试官追问再补充底层写法;
3、底层原理题:紧扣核心机制(this绑定、响应式、渲染机制),答出底层逻辑即可拿满分;
4、TS题目:重点突出类型安全、复用性、约束性三大核心价值。
全套36道面试真题全覆盖,吃透即可稳过初级、中级前端技术面试!
最后依然是推广我刚上线的小程序——原材性能计算助手,专为工程试验检测从业者打造的效率神器,感兴趣的可以点击体验一下