【前端面试】Vue篇

响应式原理

Vue2 的 Object.defineProperty vs Vue3 的 Proxy

  • Vue2 :按属性劫持,Object.defineProperty 给每个属性加 getter/setter,新增属性需 Vue.set,数组索引/length 不触发更新。
  • Vue3 :按对象代理,Proxy 拦截整个对象,支持新增/删除属性、数组索引/length,性能更好。

为什么 Vue3 用 Proxy 替代 defineProperty

  • 解决了 Vue 2 中无法直接检测到对象属性的新增和删除 (Vue.set/Vue.delete) 的问题。
  • 解决了 Vue 2 中无法直接监听数组索引和 length 属性变化的问题。
  • 性能更好,Proxy 是惰性监听,只有当访问对象属性时才会进行代理操作。

Proxy 可以拦截哪些操作

  • get:拦截对象属性的读取。
  • set:拦截对象属性的设置。
  • has:拦截 propKey in proxy 的操作。
  • deleteProperty:拦截 delete proxy[propKey]
  • apply:拦截函数的调用。

refreactive 的区别与使用场景

  • reactive(obj):将对象变成响应式对象,深度代理。
  • ref(value):包装基本类型或对象为响应式引用,访问需 .value
  • 使用场景
    • 基本类型用 ref
    • 对象/数组用 reactive
    • 想解构对象响应式时也用 ref + toRefs

ref 可以大量替换成 reactive 吗?

不能完全替换。

refreactive 虽然都能实现响应式,但用途、结构、行为不同。有些场景可以互换,有些则不行。

如果如果是基本类型就不能用reactive,因为它只代理对象。

computed 和 watch 的区别

  • 依赖对象:computed 依赖数据计算结果,watch 监听具体数据。
  • 缓存机制:computed 有缓存,只有依赖变化才重新计算;watch 每次变化都触发回调。
  • 异步支持:computed 不适合异步操作,watch 可处理异步逻辑。
  • 返回值 :computed 必须 return,watch 不需要。
  • 首次执行 :computed 首次访问才计算,watch 默认不立即触发,除非 immediate: true
  • 适用场景:computed 适合复杂计算和模板渲染;watch 适合在数据变化时执行特定操作。

computed 和 watch 的依赖收集机制区别

  1. 触发方式不同
  • computed:基于"惰性求值 + 缓存",只有当计算属性被访问时才触发依赖收集。
  • watch:立即执行回调或延迟执行,监听指定数据源,依赖收集发生在 watch 初始化时(或 getter 访问时)。
  1. 依赖收集对象不同
  • computed:收集计算属性内部访问的响应式数据作为依赖。
  • watch:收集 getter 或直接指定的响应式数据作为依赖。
  1. 执行时机不同
  • computed:依赖变化时标记为"脏",下一次访问时重新计算。
  • watch:依赖变化立即触发回调。
  1. 用途差异
  • computed:用于"模板或逻辑中依赖的计算值",缓存优化性能。
  • watch:用于"数据变化副作用",如 API 调用、手动 DOM 操作等。

总结记忆法

  • computed = 惰性 + 缓存 + 依赖内部访问
  • watch = 监听变化 + 回调副作用

Computed 计算属性原理,怎么实现惰性求值

Computed 计算属性原理

  • computed 本质上是 带缓存的 getter 函数,依赖 Vue 的响应式系统。
  • 每个 computed 属性内部有一个 Watcher 实例,用来收集依赖。
  • 当依赖的响应式数据变化时,Watcher 会被标记为 dirty(脏)。
  • 下次访问 computed 时,如果 dirty 为 true,则重新计算并缓存结果,否则直接返回缓存值。

惰性求值

  • computed 只有在访问时才执行 getter,而不是依赖变化时立即计算。
  • 通过 dirty 标记 + 缓存值 实现惰性和高效更新。

watch 的原理

  • watch 本质是 侦听器(Watcher) ,依赖 Vue 的响应式系统。
  • 当被 watch 的响应式数据变化时,Watcher 会触发回调函数。
  • 可以支持异步操作,并且可通过 immediate: true 立即触发一次回调。
  • 内部流程
    1. 创建 Watcher 实例,收集依赖。
    2. 数据变化时触发依赖通知(Observer -> Dep -> Watcher)。
    3. 执行回调,更新业务逻辑或触发副作用。

Vue3 watch 和 watchEffect 的区别

  1. 依赖收集方式
  • watch:显式指定要监听的数据或 getter 函数。
  • watchEffect:自动收集运行时访问的所有响应式数据。
  1. 执行时机
  • watch:数据变化后触发回调,可选择立即触发 (immediate)。
  • watchEffect:创建时立即执行一次,并在依赖变化时重新执行。
  1. 适用场景
  • watch:适合监听特定数据变化,执行副作用逻辑。
  • watchEffect:适合依赖多个响应式变量的副作用,减少手动指定依赖。

为什么大量数据变化只更新一次

多次改值 → watcher 入队列去重 → nextTick 批量更新一次页面

  • Vue 在数据频繁变化时只会更新一次,是因为它 采用异步更新 + watcher 去重
  • 每次数据变化,相关 watcher 会被推入一个队列,但同一个 watcher 只会存在一次(通过 id 去重)。
  • 队列会在 下一个事件循环 执行,批量更新视图。
  • 这样,无论数据变化多少次,页面只会触发一次更新,提升性能。

Vue3 的 effect / tracker / trigger 概念

effect = 副作用函数,tracker = 收集依赖,trigger = 数据变化触发更新

  • 可结合 Vue3 Proxy 响应式实现:gettracker 收集依赖,settrigger 通知更新。
  • 可以和 Vue2 的 Object.defineProperty 对比:Vue2 getter 收集依赖,setter 触发更新。
  1. effect
  • 响应式副作用函数,当依赖的响应式数据变化时自动重新执行
  • 在 Vue 中,模板渲染函数和 computed 本质上都是 effect
  1. tracker
  • 又叫 依赖收集
  • effect 执行时,访问了哪些响应式数据,这些数据会记录 effect 为依赖。
  • 作用是 建立数据和 effect 的映射关系
  1. trigger
  • 数据变化时触发 effect
  • 当响应式数据被修改,trigger 找到依赖该数据的 effect 并重新执行。

生命周期

Vue2 生命周期

  1. 初始化阶段的钩子函数(初始化 Vue 实例)
  • beforeCreate():实例创建前,模板和数据均未获取到。
  • created():实例创建后,最早可以获得 data 数据,模板还没拿到。
  • beforeMount():数据挂载前,模板已经获取到,但数据未挂载。
  • mounted():数据挂载后,数据已挂载到模板中。
  1. 更新阶段的钩子函数(数据渲染,更新 Dom)
  • beforeUpdate():模板更新前,data 改变后,更新数据模板前调用。
  • updated():模板更新后,将 data 渲染到数据模板中。
  1. 销毁阶段的钩子函数(销毁实例)
  • beforeDestroy():实例销毁前。
  • destroyed():实例销毁后。

Vue2 父子组件生命周期执行顺序

  1. 挂载阶段
    父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted

  2. 更新阶段
    父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated

  3. 销毁阶段
    父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed

created 和 mounted 区别

  • created :组件实例创建完成,但 DOM 还未挂载
    • 常用于初始化数据、发起 API 请求或设置定时器。
  • mounted :组件的 DOM 已挂载到页面 ,可以访问真实 DOM 元素。
    • 常用于操作 DOM、第三方库初始化或需要依赖真实节点的逻辑。

组件中的 data 为什么是函数

避免多个组件实例共享同一 data 对象,造成数据冲突。

v-if 和 v-for 的优先级

  • Vue2v-for 优先级更高。
  • Vue3 中,v-if 的优先级更高。

keep-alive 缓存的具体内容

  • <keep-alive> 缓存的是组件实例,包括响应式状态和方法,而不仅仅是 DOM。
  • 缓存的组件不会重新执行 createdmounted,只触发 activated / deactivated
  • 可以通过 include/exclude/max 控制缓存策略。

keep-alive 缓存原理

Vue 内部维护一个缓存对象(key → 组件实例),渲染时优先复用缓存实例,超过 max 或不在 include 范围时清理旧实例。

组件销毁和内存泄漏问题

组件销毁 = 清理依赖、事件、子组件和引用;漏掉任何清理都可能造成内存泄漏。

  1. 组件销毁流程
  • Vue 组件有生命周期钩子,如 beforeDestroy / beforeUnmountdestroyed / unmounted
  • 销毁时会做以下操作:
    1. 移除组件自身的响应式依赖。
    2. 清理事件监听(包括 $on、DOM 事件)。
    3. 卸载子组件。
    4. 从父组件中移除引用。
  1. 内存泄漏的常见原因
  • 全局事件或定时器未清理(window.addEventListenersetInterval)。
  • 外部库引用未解除。
  • 对 DOM 或组件实例的引用未释放。
  1. 防止方法
  • beforeDestroy / beforeUnmount 中手动清理事件和定时器。
  • 避免在全局对象中持有组件引用。
  • 使用 Vue 提供的 $offclearInterval 等 API。

数据绑定

v-model 原理和用途

  • v-model 是 Vue 提供的 双向数据绑定语法糖 ,用于在 表单元素或组件中同步数据和视图。
  • Vue2 :绑定 value + 监听 input 事件来实现双向绑定。
  • Vue3 :支持自定义 prop 和事件,组件内部通过 emit('update:modelValue', newValue) 更新父组件状态,可以多个 v-model,对应不同 prop。
  • 用途
    • 表单输入同步:inputtextareaselect
    • 自定义组件状态双向绑定。
    • 减少手动写事件和 props 的样板代码。

Vue2 双向绑定原理

  1. 数据劫持 :通过 Object.defineProperty 遍历 data 中所有属性,为每个属性添加 getter(收集依赖)和 setter(触发更新)。
  2. 模板解析 :编译器扫描模板,将指令转换为渲染函数,同时为每个依赖数据的 DOM 节点创建 Watcher 实例。
  3. 依赖收集 :首次渲染时,访问 data 属性触发 getter,将 Watcher 实例添加到该属性的 "依赖列表" 中。
  4. 响应式更新 :数据变化触发 setter,遍历依赖列表,调用所有 Watcherupdate 方法,更新对应的 DOM 节点。

Vue2 中如何检测数组的变化?

在 Vue2 中,数组的变化 不能被直接劫持索引或 length ,所以 Vue2 通过 重写数组的七个变异方法 来实现响应式。

  • 当调用这些方法修改数组时,Vue 会触发依赖更新,视图自动刷新。
  • 如果直接用索引修改,如 arr[0] = 1,则不会触发更新,需要用 Vue.set(arr, 0, 1)this.$set(arr, index, value) 来保证响应式。

this.$set 用法

给对象新增响应式属性,解决 Vue2 无法监听新增属性问题。

this.$nextTick 作用及原理

  • 作用在下次 DOM 更新循环结束之后执行回调,保证获取到的是最新的 DOM 状态。
  • 原理Vue 的 DOM 更新是异步的,$nextTick 把回调推迟到 "下次 DOM 更新循环结束后" 执行,确保能获取更新后的 DOM。

组件通信

父子组件通信

  • props 传值,事件 $emit 通知父。

子组件能否直接修改父组件的值

子组件不应该直接修改父组件的 props ,因为 props 是父组件单向下传的数据,直接修改会破坏单向数据流,Vue 会在控制台提示警告。

平行组件通信

  • 借用父组件实现平行组件间通信。
  • 事件总线(小型项目)。
  • Vuex / Pinia(大型项目)。
  • provide/inject(跨层级)。

插槽(slot、具名插槽、作用域插槽)

  • 具名插槽 :带有 name 属性,一个组件可以有多个具名插槽。
  • 匿名插槽 :没有 name 属性,一个组件只能有一个匿名插槽。
  • 作用域插槽:是子组件提供数据、父组件自定义渲染的插槽。

provide / inject

provide 父组件提供,inject 子组件注入,跨层传递数据,减少 props 链式传递。

provide / inject 是 Vue 提供的 跨级组件通信机制

  1. provide
  • 在父组件或祖先组件中声明要提供的数据。
  • 通过对象或函数返回给后代组件使用。
  1. inject
  • 在子组件或后代组件中声明依赖的键。
  • 可以访问父组件或祖先组件提供的数据。

特点

  • 数据 单向传递,子组件修改不会影响父组件。
  • 适合跨多层组件传递数据,避免层层 props 传递。
  • 不是响应式的默认机制,如果需要响应式,需要在 provide 时用 reactiveref 包裹。

Composition API

为什么引入?

解决 Options API 中逻辑复用难、组件大型化后管理困难的问题。

Teleport / Suspense 简单使用场景

  • <Teleport> (传送门)

    • 作用:允许我们将一个组件的内容渲染到 DOM 树的另一个位置。
    • 典型场景 :创建全局的模态框(Modal)、弹窗(Dialog)、通知(Notification)等。这些组件在逻辑上属于当前组件,但在视觉上需要脱离当前组件的 DOM 结构(例如挂载到 <body>下),以避免 z-indexoverflow 的问题。
  • <Suspense> (实验性)

    • 作用 :用于优雅地处理异步组件或异步 setup 函数。
    • 如何工作 :它提供了两个插槽 #default#fallback。当 default 插槽内的异步组件正在加载时,会显示 fallback 插槽的内容(如 Loading 动画)。加载完成后,再切换回 default 内容。这简化了自己写 v-if="loading" 的逻辑。

虚拟 DOM

原理、作用、解析过程

原理

  • Vue 模板被编译成渲染函数,返回虚拟 DOM(VNode)树。
  • 数据变化时,生成新的 VNode 树。
  • 对比新旧 VNode 树(Diff 算法),找出最小变更。
  • 将差异应用到真实 DOM(Patch),更新视图。

作用

  1. 性能优化:避免频繁操作真实 DOM,通过 diff 算法批量更新最小化真实 DOM 变更。
  2. 跨平台:在不同环境(Web、Weex、Native)都可以渲染,因为虚拟 DOM 只是 JS 对象。

解析过程

  1. 模板编译:Vue 模板 → 渲染函数 → 生成虚拟 DOM(VNode)。
  2. 渲染:渲染函数返回 VNode 树,描述当前视图状态。
  3. 更新(Diff):数据变化 → 新旧 VNode 树对比 → 找出最小变更。
  4. 打补丁(Patch):将差异应用到真实 DOM,更新视图。

diff 算法(Vue2 vs Vue3)

  • 静态标记 :Vue2 全量遍历,Vue3 仅对比带 PatchFlags 的动态节点。
  • 数组 Diff:Vue2 双端对比(O(n)),Vue3 最长递增子序列(O(k),少 DOM 移动)。
  • 优化逻辑:Vue2 纯运行时 Diff,Vue3 编译预优化 + 运行时按需对比。

两者均对比新旧 VNode 更优更新 DOM,Vue3 针对 Vue2 全量 Diff 痛点,用静态标记 + LIS 算法优化,聚焦动态节点和最少 DOM 操作,复杂组件 / 长列表性能提升明显。

key 的作用

  1. 为了性能优化 :Vue 底层是虚拟 Dom,更新时使用 diff 算法,为每个节点设置 key 值,方便 Vue 判断节点是否可以复用,从而提高渲染效率。
  2. 确保正确的组件状态 :在使用带有状态的组件时,如果不使用 key,容易出现状态错乱的问题,使用 key 可以确保每个节点都能正确保存组件状态。

v-if vs v-show

  • v-show :元素始终渲染到 HTML 中,通过修改元素的 CSS 属性 display 来决定显示还是隐藏。
  • v-if:满足条件才会渲染到 HTML 中,通过操作 DOM 元素来切换显示。

路由

路由跳转方式

  1. 使用 <router-link> 标签。
  2. 使用 this.$router.push 方法。
  3. 使用 this.$router.replace 方法。
  4. 使用 this.$router.go 方法。

动态路由

  1. 动态路由
  • 在路由配置中可以使用 占位符 (如 /index/:id)表示参数。
  • 路由跳转时传入参数,组件通过 $route 获取对应值。
  1. 传参方式
  • params 参数
    • 路由路径带占位符。
    • 参数通过 $route.params 获取。
    • 不显示在 URL 中,刷新后会丢失。
  • query 参数
    • 路由路径正常。
    • 参数通过 $route.query 获取。
    • 显示在 URL 中,刷新不会丢失。

路由守卫

  • 全局前置钩子beforeEach
  • 路由独享守卫beforeEnter
  • 组件内钩子beforeRouterEnterbeforeRouterUpdatebeforeRouterLeave

为什么 Vue 和 React 都选择自己实现路由?

核心需求:SPA 要求路由跳转时不刷新页面,仅更新页面内容(组件切换),而原生 <a> 标签跳转会刷新页面,无法满足需求。

  1. 保持框架风格一致
  • Vue、React 都是组件化框架 ,希望路由也能用组件的方式声明式使用
  • 自研路由可以无缝和响应式系统、生命周期机制整合。
  1. 统一生态标准
  • 官方路由能提供一致的 API、约定和最佳实践。
  • 避免社区出现多个实现方式导致学习成本高、兼容性混乱。
  1. 更好地集成框架特性
  • Vue Router 能和 Vue 的 响应式系统、keep-alive、transition 深度联动。
  • React Router 能利用 React hooks (如 useNavigate, useParams)与组件状态自然融合。
  1. 控制渲染流程与性能
  • 框架自己控制路由切换时的组件卸载、缓存、动画、懒加载等细节。
  • 能更好地做动态导入、SSR 支持(如 Next.js、Nuxt)。
  1. 生态闭环
  • 路由是前端 SPA 的核心,官方实现意味着框架能提供从渲染到导航的一体化方案,增强生态控制力与可维护性。

浏览器本身就有路由,为什么不直接用 a 标签跳转?

因为前端框架的"路由跳转"不是刷新整个页面,而是在单页应用(SPA)中切换视图、复用资源,提升性能与体验。

  • <a> 标签跳转 → 后端控制页面渲染
  • Router 跳转 → 前端控制视图切换

前端路由的本质是 "不刷新页面的页面切换" ,让 SPA 应用更快、更流畅。

history 导航时,页面真的切换了吗?怎么做到的?

history.pushState() 并没有真正让浏览器"切换页面",而是"在同一个页面里切换视图 + 改地址"。这就是浏览器支持单页面路由的本质。

原生 JS 如何进行路由监听?

有两种方式:hash 模式 和 history 模式。

  • Hash 模式 :监听 hashchange 事件。
  • History 模式 :监听 popstate 事件。

没有 hash 的路由如何进行监听?

使用 HTML5 History APIpushState / replaceState + popstate

  • 浏览器的前进、后退操作会触发 popstate
  • 但主动调用 pushState() 不会触发,需要自己派发或封装监听。

onpopstate 可以监听到 pushstate 事件吗?

不能onpopstate 只在用户点击浏览器的前进/后退,或调用 history.back()history.forward()history.go() 时触发。主动调用 pushState()replaceState() 不会触发。框架通常会在调用后手动执行路由更新逻辑。


性能优化

Vue 性能优化策略

  1. 模板和渲染优化
  • 使用 v-if / v-show 区分频繁切换和固定显示的元素。
  • 对列表使用 key 提高虚拟 DOM 节点复用效率。
  • 静态内容标记为 静态节点,减少重复渲染。
  • 组件懒加载,按需加载路由或组件。
  1. 响应式数据优化
  • 避免不必要的响应式数据,减少 watcher 数量。
  • 对复杂对象使用 computed 缓存计算结果。
  • 对频繁变化的数据,使用 v-once / v-memo 避免重复渲染。
  1. 事件与 DOM 操作优化
  • 使用事件修饰符和 节流 / 防抖 减少频繁回调。
  • 频繁切换元素时,使用 v-show 替代 v-if
  1. 内存和资源优化
  • 组件销毁时清理事件监听、定时器、第三方库引用。
  • 使用 keep-alive 缓存组件状态,但注意释放不再使用的组件。
  1. 服务端渲染(SSR)与懒加载
  • 对首屏渲染优化,减少首次加载时间。
  • 异步组件和路由懒加载减少 JS 包体积。

设计模式

Vue 中使用的设计模式

  1. 观察者模式
    • 数据变化通知依赖更新,是 Vue 响应式系统的核心。
  2. 发布-订阅模式
    • Watcher 订阅数据变化,Observer 发布变化。
  3. 策略模式
    • 不同渲染模式、生命周期钩子处理、指令更新方式等采用策略模式封装。
  4. 工厂模式
    • 创建 VNode、组件实例、渲染函数等对象。
  5. 单例模式
    • VueRouter、Vuex 实例在应用中通常是单例。
  6. 代理模式
    • 用于数据代理和响应式实现,代替 Vue2 的 Object.defineProperty

MVVM 和 MVC 模式的区别

MVC(Model-View-Controller)是一种将应用拆分为模型、视图和控制器的架构模式:

  • Model:管理数据和业务逻辑。
  • View:负责 UI 展示。
  • Controller:处理用户输入,更新 Model 并通知 View。
  • 数据流通常是 用户操作 → Controller → Model → View,视图和数据绑定是手动的。

MVVM (Model-View-ViewModel)在 MVC 基础上演化,引入了 ViewModel 作为桥梁,实现 数据双向绑定

  • Model:管理数据。
  • View:展示 UI。
  • ViewModel:将 Model 数据映射到 View,并将用户操作同步回 Model。
  • 数据变化自动更新视图,视图操作也会自动更新数据。
  • Vue、Angular 是典型的 MVVM 框架。

核心区别

  • MVC 依赖 Controller 手动更新视图,MVVM 通过响应式双向绑定自动同步数据和视图。
  • MVC 数据流单向,MVVM 数据流双向。
  • MVVM 更适合现代前端框架开发,减少 DOM 操作,提高开发效率。

Vue SSR 原理

  1. 服务端渲染
  • 将 Vue 组件在服务端渲染成 HTML 字符串。
  • 客户端拿到完整 HTML 可以直接展示页面,提升首屏渲染速度和 SEO 友好性。
  1. 渲染流程
  • 服务端
    1. Vue 组件生成渲染函数。
    2. 渲染函数生成 虚拟 DOM
    3. 将虚拟 DOM 转为 HTML 字符串返回给客户端。
  • 客户端
    1. 接收 HTML,展示页面。
    2. Vue 客户端挂载,进行 hydration,将静态 DOM 转换为可交互的 Vue 应用。
  1. 优化点
  • 可以结合路由懒加载、数据预取(asyncData / fetch)提高性能。
  • 避免服务端渲染副作用操作,如直接操作 DOM。

Vue 模板编译原理

  1. 模板解析
  • 将模板字符串解析成 抽象语法树(AST) ,描述节点结构和指令信息。
  1. 优化阶段
  • 标记静态节点、静态根,减少后续渲染过程中不必要的更新。
  1. 代码生成
  • 将 AST 转换成 渲染函数(返回 VNode),供 Vue 在渲染阶段调用。
  • 执行渲染函数生成 HTML。

Vuex

Vuex 实现原理

Vuex = 响应式 state + mutation 管理修改 + action 异步处理 + getters 计算状态

Vuex 是 Vue 的 集中式状态管理库 ,核心原理是通过 响应式对象 + 单向数据流 + 事件订阅机制 实现状态管理:

核心组成

  1. State :存储共享状态,是响应式对象,组件通过 mapStatethis.$store.state 访问。
  2. Getter :类似 computed,依赖 state 计算衍生状态,自动缓存。
  3. Mutation :唯一修改 state 的方式,同步操作,保证状态可追踪。
  4. Action :处理异步逻辑,内部调用 mutation 修改 state
  5. Module :支持模块化管理 statemutationactiongetter

数据流

复制代码
Component -> dispatch action -> commit mutation -> update state -> view 自动更新
  • State 是响应式的,组件依赖收集后,会在 state 改变时自动重新渲染。
  • Vuex 内部通过 Vue 的响应式系统实现状态响应。

mutations 能否异步

不能 。Vuex 所有状态更新唯一途径都是 mutation,异步操作通过 Action 来提交 mutation 实现,方便跟踪每一个状态的变化。若 mutations 是异步的,就无法很好地追踪状态,给调试带来困难。

刷新页面数据丢失问题

  • 原因:Vuex 数据保存在运行内存中,页面刷新会重新加载 Vue 实例,Vuex 数据就会被清空。
  • 解决方法
    1. 可结合 localStorage / sessionStorage 持久化。
    2. 使用插件,如 vuex-persistedstate 等插件来使 Vuex 数据持久化到本地中。

Vuex vs localStorage 区别

1. Vuex

  • 响应式state 是响应式的,数据变化会自动更新组件视图。
  • 临时存储 :页面刷新后 state 会丢失(除非配合持久化插件)。
  • 单向数据流 + 可追踪 :通过 mutations 修改 state,方便调试和管理复杂状态。

2. localStorage

  • 非响应式:直接存储在浏览器,本身不会触发组件更新。
  • 持久化存储:页面刷新不会丢失,适合保存登录信息、用户偏好等。
  • 手动管理:需要手动读取/写入,并手动同步到组件状态。

Vuex 和 Pinia 的区别

对比项 Vuex Pinia
语法风格 强调 mutation + action 的固定流程 直接在 actions里改状态,更简洁
TypeScript 支持 较弱,需要手动类型定义 内置类型推导,TS 友好
模块化 模块需命名空间(namespace)管理 Store 是独立的,不需要命名空间
数据定义 stategettersmutationsactions分开写 全部集中在 defineStore里,更直观
调试工具 支持 Vue DevTools 同样支持(且更清晰)
兼容性 Vue2/3 都能用(Vuex4 支持 Vue3) 主要为 Vue3 设计(可在 Vue2 用插件兼容)
学习成本 较高 较低,语法接近 Composition API

为什么项目优先采用 Vuex 而不是 Pinia?

新项目会更倾向 Pinia,老项目或保守团队会优先 Vuex。

  1. 历史项目兼容性
  • 大量项目是 Vue2 架构,Vuex 已深度集成。
  • 升级到 Pinia 需改写逻辑、迁移工具链,成本高。
  1. 团队和生态惯性
  • Vuex 存在多年,文档、教程、插件生态成熟。
  • 老项目、老团队成员更熟悉 Vuex 的使用模式。
  1. 规范与稳定性
  • Vuex 强制使用 mutation 改状态,结构清晰、便于调试与规范团队协作。
  • Pinia 更灵活,但也意味着约束更少,对团队自律要求更高。
  1. 第三方依赖兼容
  • 一些旧组件库、插件仍默认依赖 Vuex(例如某些权限、缓存中间件)。

什么是状态管理?作用是什么?

  • 状态管理:数据需要保存在应用中某个位置,这些数据管理称之为状态管理。
  • 作用 :当使用 Vue 进行单页应用开发时,经常会在各个组件之间数据通信,比较简单情况下,可以用 propsemit 事件进行传递,数据逻辑过于复杂时,需要状态管理。

其他

$attrs / $listeners / 插槽透传

$attrs 透传属性,$listeners 透传事件,插槽透传内容

  • Vue3$listeners 被合并到 $attrs 中,通过 v-on="$attrs" 透传事件。
  1. $attrs
  • 包含 父组件传递给子组件,但子组件没有显式声明的 props
  • 常用于 将未声明的属性透传给子组件的根元素或子组件
  1. $listeners(Vue2 特有)
  • 包含 父组件传递给子组件的事件监听器
  • 可以通过 $listeners 将事件透传给子组件内部元素。
  1. 插槽透传
  • 子组件内部使用 <slot><slot name="xxx"> 将父组件传入的内容渲染出来。
  • 可以实现 父组件内容在子组件内部复用

Vue 与 React 的区别

  • 模板方式:Vue 使用模板 + 指令,React 使用 JSX(函数式写法)。
  • 响应式机制 :Vue 内置响应式(Vue2 Object.defineProperty / Vue3 Proxy),React 通过 setState 或 Hook 管理状态。
  • 数据流 :Vue 默认单向 + 双向绑定(v-model),React 默认单向流。
  • 组件通信 :Vue 提供 props / events / provide-inject / Vuex,React 主要通过 propsContext
  • 学习成本:Vue 上手快,React 更偏向函数式和组合式思维。

Vue2 与 Vue3 的区别

  • 响应式实现 :Vue2 用 Object.defineProperty,Vue3 用 Proxy,更高效、支持动态添加属性。
  • Composition API:Vue3 引入 Composition API,提高逻辑复用和组织能力。
  • 性能优化:Vue3 更小体积、更快虚拟 DOM,支持 tree-shaking。
  • Fragment / Teleport / Suspense:Vue3 支持多根节点组件、DOM 传送、异步组件占位渲染。

SPA 理解及优缺点

  • 理解:单页应用在首次加载后,通过 JS 控制页面路由,局部更新 DOM,而不是每次请求新页面。
  • 优点:体验流畅、减少页面刷新、前后端分离。
  • 缺点:首屏加载慢(需 SSR 优化)、SEO 较弱、浏览器历史管理需处理。

浏览器为什么支持单页面路由?

浏览器支持单页面路由,是因为提供了 Hash 和 History API ,让前端可以修改 URL 而不刷新页面 ,从而实现 高性能、可导航、可回退的 SPA 体验。

v-html 原理

  • v-html 用于将字符串渲染为 HTML 内容
  • 本质是修改元素的 innerHTML
  • 安全风险:可能导致 XSS,需要确保内容可信。

组件中写 name 的好处

  • 调试工具中显示组件名称,更易定位问题。
  • 递归组件 必须用 name 来引用自己。
  • keep-alive 中使用 include / exclude 缓存策略时依赖 name

index 做 key 的问题

在 Vue 中,key 用于标识列表中每个节点的唯一性,帮助虚拟 DOM diff 算法复用节点。

使用索引作为 key 的问题

  1. 当列表顺序发生变化(插入、删除、移动)时,索引会改变,虚拟 DOM 会认为节点是不同的,从而销毁重建,导致 性能浪费状态丢失 (如 input 输入内容丢失)。
  2. 对于静态列表或不会变的列表,使用 index 可以,但对动态列表不推荐。

推荐做法

  • 尽量使用 唯一且稳定的 id 作为 key,以保证节点复用。

Vue 指令,如何添加自定义指令

Vue 指令

  • 指令是带有 v- 前缀的特殊属性,用来在 DOM 上执行特定操作,如 v-ifv-showv-model

自定义指令

  • Vue.directive 函数来添加自定义指令。
  • Vue 允许用户创建自定义指令,扩展 DOM 功能。
  • 核心是提供 生命周期钩子函数
    • bind / created:指令绑定到元素时执行。
    • inserted / mounted:元素插入 DOM 时执行。
    • update / updated:绑定值更新时执行。
    • unbind / unmounted:指令解绑时执行。

应用场景:如自动聚焦、拖拽、节流防抖等 DOM 操作。

相关推荐
恋猫de小郭36 分钟前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端