响应式原理
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:拦截函数的调用。
ref 和 reactive 的区别与使用场景
reactive(obj):将对象变成响应式对象,深度代理。ref(value):包装基本类型或对象为响应式引用,访问需.value。- 使用场景 :
- 基本类型用
ref。 - 对象/数组用
reactive。 - 想解构对象响应式时也用
ref+toRefs。
- 基本类型用
ref 可以大量替换成 reactive 吗?
不能完全替换。
ref 和 reactive 虽然都能实现响应式,但用途、结构、行为不同。有些场景可以互换,有些则不行。
如果如果是基本类型就不能用reactive,因为它只代理对象。
computed 和 watch 的区别
- 依赖对象:computed 依赖数据计算结果,watch 监听具体数据。
- 缓存机制:computed 有缓存,只有依赖变化才重新计算;watch 每次变化都触发回调。
- 异步支持:computed 不适合异步操作,watch 可处理异步逻辑。
- 返回值 :computed 必须
return,watch 不需要。 - 首次执行 :computed 首次访问才计算,watch 默认不立即触发,除非
immediate: true。 - 适用场景:computed 适合复杂计算和模板渲染;watch 适合在数据变化时执行特定操作。
computed 和 watch 的依赖收集机制区别
- 触发方式不同
- computed:基于"惰性求值 + 缓存",只有当计算属性被访问时才触发依赖收集。
- watch:立即执行回调或延迟执行,监听指定数据源,依赖收集发生在 watch 初始化时(或 getter 访问时)。
- 依赖收集对象不同
- computed:收集计算属性内部访问的响应式数据作为依赖。
- watch:收集 getter 或直接指定的响应式数据作为依赖。
- 执行时机不同
- computed:依赖变化时标记为"脏",下一次访问时重新计算。
- watch:依赖变化立即触发回调。
- 用途差异
- 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立即触发一次回调。 - 内部流程 :
- 创建 Watcher 实例,收集依赖。
- 数据变化时触发依赖通知(Observer -> Dep -> Watcher)。
- 执行回调,更新业务逻辑或触发副作用。
Vue3 watch 和 watchEffect 的区别
- 依赖收集方式
watch:显式指定要监听的数据或 getter 函数。watchEffect:自动收集运行时访问的所有响应式数据。
- 执行时机
watch:数据变化后触发回调,可选择立即触发 (immediate)。watchEffect:创建时立即执行一次,并在依赖变化时重新执行。
- 适用场景
watch:适合监听特定数据变化,执行副作用逻辑。watchEffect:适合依赖多个响应式变量的副作用,减少手动指定依赖。
为什么大量数据变化只更新一次
多次改值 → watcher 入队列去重 → nextTick 批量更新一次页面
- Vue 在数据频繁变化时只会更新一次,是因为它 采用异步更新 + watcher 去重。
- 每次数据变化,相关 watcher 会被推入一个队列,但同一个 watcher 只会存在一次(通过 id 去重)。
- 队列会在 下一个事件循环 执行,批量更新视图。
- 这样,无论数据变化多少次,页面只会触发一次更新,提升性能。
Vue3 的 effect / tracker / trigger 概念
effect = 副作用函数,tracker = 收集依赖,trigger = 数据变化触发更新
- 可结合 Vue3 Proxy 响应式实现:
get时tracker收集依赖,set时trigger通知更新。 - 可以和 Vue2 的
Object.defineProperty对比:Vue2getter收集依赖,setter触发更新。
- effect
- 响应式副作用函数,当依赖的响应式数据变化时自动重新执行。
- 在 Vue 中,模板渲染函数和
computed本质上都是effect。
- tracker
- 又叫 依赖收集。
- 当
effect执行时,访问了哪些响应式数据,这些数据会记录effect为依赖。 - 作用是 建立数据和 effect 的映射关系。
- trigger
- 数据变化时触发
effect。 - 当响应式数据被修改,
trigger找到依赖该数据的effect并重新执行。
生命周期
Vue2 生命周期
- 初始化阶段的钩子函数(初始化 Vue 实例)
beforeCreate():实例创建前,模板和数据均未获取到。created():实例创建后,最早可以获得 data 数据,模板还没拿到。beforeMount():数据挂载前,模板已经获取到,但数据未挂载。mounted():数据挂载后,数据已挂载到模板中。
- 更新阶段的钩子函数(数据渲染,更新 Dom)
beforeUpdate():模板更新前,data 改变后,更新数据模板前调用。updated():模板更新后,将 data 渲染到数据模板中。
- 销毁阶段的钩子函数(销毁实例)
beforeDestroy():实例销毁前。destroyed():实例销毁后。
Vue2 父子组件生命周期执行顺序
-
挂载阶段
父 beforeCreate->父 created->父 beforeMount->子 beforeCreate->子 created->子 beforeMount->子 mounted->父 mounted -
更新阶段
父 beforeUpdate->子 beforeUpdate->子 updated->父 updated -
销毁阶段
父 beforeDestroy->子 beforeDestroy->子 destroyed->父 destroyed
created 和 mounted 区别
- created :组件实例创建完成,但 DOM 还未挂载 。
- 常用于初始化数据、发起 API 请求或设置定时器。
- mounted :组件的 DOM 已挂载到页面 ,可以访问真实 DOM 元素。
- 常用于操作 DOM、第三方库初始化或需要依赖真实节点的逻辑。
组件中的 data 为什么是函数
避免多个组件实例共享同一 data 对象,造成数据冲突。
v-if 和 v-for 的优先级
- 在 Vue2 中
v-for优先级更高。 - 在 Vue3 中,
v-if的优先级更高。
keep-alive 缓存的具体内容
<keep-alive>缓存的是组件实例,包括响应式状态和方法,而不仅仅是 DOM。- 缓存的组件不会重新执行
created和mounted,只触发activated/deactivated。 - 可以通过
include/exclude/max控制缓存策略。
keep-alive 缓存原理
Vue 内部维护一个缓存对象(key → 组件实例),渲染时优先复用缓存实例,超过 max 或不在 include 范围时清理旧实例。
组件销毁和内存泄漏问题
组件销毁 = 清理依赖、事件、子组件和引用;漏掉任何清理都可能造成内存泄漏。
- 组件销毁流程
- Vue 组件有生命周期钩子,如
beforeDestroy/beforeUnmount和destroyed/unmounted。 - 销毁时会做以下操作:
- 移除组件自身的响应式依赖。
- 清理事件监听(包括
$on、DOM 事件)。 - 卸载子组件。
- 从父组件中移除引用。
- 内存泄漏的常见原因
- 全局事件或定时器未清理(
window.addEventListener、setInterval)。 - 外部库引用未解除。
- 对 DOM 或组件实例的引用未释放。
- 防止方法
- 在
beforeDestroy/beforeUnmount中手动清理事件和定时器。 - 避免在全局对象中持有组件引用。
- 使用 Vue 提供的
$off、clearInterval等 API。
数据绑定
v-model 原理和用途
v-model是 Vue 提供的 双向数据绑定语法糖 ,用于在 表单元素或组件中同步数据和视图。- Vue2 :绑定
value+ 监听input事件来实现双向绑定。 - Vue3 :支持自定义 prop 和事件,组件内部通过
emit('update:modelValue', newValue)更新父组件状态,可以多个v-model,对应不同 prop。 - 用途 :
- 表单输入同步:
input、textarea、select。 - 自定义组件状态双向绑定。
- 减少手动写事件和 props 的样板代码。
- 表单输入同步:
Vue2 双向绑定原理
- 数据劫持 :通过
Object.defineProperty遍历data中所有属性,为每个属性添加getter(收集依赖)和setter(触发更新)。 - 模板解析 :编译器扫描模板,将指令转换为渲染函数,同时为每个依赖数据的 DOM 节点创建
Watcher实例。 - 依赖收集 :首次渲染时,访问
data属性触发getter,将Watcher实例添加到该属性的 "依赖列表" 中。 - 响应式更新 :数据变化触发
setter,遍历依赖列表,调用所有Watcher的update方法,更新对应的 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 提供的 跨级组件通信机制:
- provide
- 在父组件或祖先组件中声明要提供的数据。
- 通过对象或函数返回给后代组件使用。
- inject
- 在子组件或后代组件中声明依赖的键。
- 可以访问父组件或祖先组件提供的数据。
特点:
- 数据 单向传递,子组件修改不会影响父组件。
- 适合跨多层组件传递数据,避免层层
props传递。 - 不是响应式的默认机制,如果需要响应式,需要在
provide时用reactive或ref包裹。
Composition API
为什么引入?
解决 Options API 中逻辑复用难、组件大型化后管理困难的问题。
Teleport / Suspense 简单使用场景
-
<Teleport>(传送门):- 作用:允许我们将一个组件的内容渲染到 DOM 树的另一个位置。
- 典型场景 :创建全局的模态框(Modal)、弹窗(Dialog)、通知(Notification)等。这些组件在逻辑上属于当前组件,但在视觉上需要脱离当前组件的 DOM 结构(例如挂载到
<body>下),以避免z-index和overflow的问题。
-
<Suspense>(实验性):- 作用 :用于优雅地处理异步组件或异步
setup函数。 - 如何工作 :它提供了两个插槽
#default和#fallback。当default插槽内的异步组件正在加载时,会显示fallback插槽的内容(如 Loading 动画)。加载完成后,再切换回default内容。这简化了自己写v-if="loading"的逻辑。
- 作用 :用于优雅地处理异步组件或异步
虚拟 DOM
原理、作用、解析过程
原理:
- Vue 模板被编译成渲染函数,返回虚拟 DOM(VNode)树。
- 数据变化时,生成新的 VNode 树。
- 对比新旧 VNode 树(Diff 算法),找出最小变更。
- 将差异应用到真实 DOM(Patch),更新视图。
作用:
- 性能优化:避免频繁操作真实 DOM,通过 diff 算法批量更新最小化真实 DOM 变更。
- 跨平台:在不同环境(Web、Weex、Native)都可以渲染,因为虚拟 DOM 只是 JS 对象。
解析过程:
- 模板编译:Vue 模板 → 渲染函数 → 生成虚拟 DOM(VNode)。
- 渲染:渲染函数返回 VNode 树,描述当前视图状态。
- 更新(Diff):数据变化 → 新旧 VNode 树对比 → 找出最小变更。
- 打补丁(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 的作用
- 为了性能优化 :Vue 底层是虚拟 Dom,更新时使用 diff 算法,为每个节点设置
key值,方便 Vue 判断节点是否可以复用,从而提高渲染效率。 - 确保正确的组件状态 :在使用带有状态的组件时,如果不使用
key,容易出现状态错乱的问题,使用key可以确保每个节点都能正确保存组件状态。
v-if vs v-show
- v-show :元素始终渲染到 HTML 中,通过修改元素的 CSS 属性
display来决定显示还是隐藏。 - v-if:满足条件才会渲染到 HTML 中,通过操作 DOM 元素来切换显示。
路由
路由跳转方式
- 使用
<router-link>标签。 - 使用
this.$router.push方法。 - 使用
this.$router.replace方法。 - 使用
this.$router.go方法。
动态路由
- 动态路由
- 在路由配置中可以使用 占位符 (如
/index/:id)表示参数。 - 路由跳转时传入参数,组件通过
$route获取对应值。
- 传参方式
- params 参数
- 路由路径带占位符。
- 参数通过
$route.params获取。 - 不显示在 URL 中,刷新后会丢失。
- query 参数
- 路由路径正常。
- 参数通过
$route.query获取。 - 显示在 URL 中,刷新不会丢失。
路由守卫
- 全局前置钩子 :
beforeEach - 路由独享守卫 :
beforeEnter - 组件内钩子 :
beforeRouterEnter、beforeRouterUpdate、beforeRouterLeave
为什么 Vue 和 React 都选择自己实现路由?
核心需求:SPA 要求路由跳转时不刷新页面,仅更新页面内容(组件切换),而原生 <a> 标签跳转会刷新页面,无法满足需求。
- 保持框架风格一致
- Vue、React 都是组件化框架 ,希望路由也能用组件的方式声明式使用。
- 自研路由可以无缝和响应式系统、生命周期机制整合。
- 统一生态标准
- 官方路由能提供一致的 API、约定和最佳实践。
- 避免社区出现多个实现方式导致学习成本高、兼容性混乱。
- 更好地集成框架特性
- Vue Router 能和 Vue 的 响应式系统、keep-alive、transition 深度联动。
- React Router 能利用 React hooks (如
useNavigate,useParams)与组件状态自然融合。
- 控制渲染流程与性能
- 框架自己控制路由切换时的组件卸载、缓存、动画、懒加载等细节。
- 能更好地做动态导入、SSR 支持(如 Next.js、Nuxt)。
- 生态闭环
- 路由是前端 SPA 的核心,官方实现意味着框架能提供从渲染到导航的一体化方案,增强生态控制力与可维护性。
浏览器本身就有路由,为什么不直接用 a 标签跳转?
因为前端框架的"路由跳转"不是刷新整个页面,而是在单页应用(SPA)中切换视图、复用资源,提升性能与体验。
<a>标签跳转 → 后端控制页面渲染- Router 跳转 → 前端控制视图切换
前端路由的本质是 "不刷新页面的页面切换" ,让 SPA 应用更快、更流畅。
history 导航时,页面真的切换了吗?怎么做到的?
history.pushState() 并没有真正让浏览器"切换页面",而是"在同一个页面里切换视图 + 改地址"。这就是浏览器支持单页面路由的本质。
原生 JS 如何进行路由监听?
有两种方式:hash 模式 和 history 模式。
- Hash 模式 :监听
hashchange事件。 - History 模式 :监听
popstate事件。
没有 hash 的路由如何进行监听?
使用 HTML5 History API :pushState / replaceState + popstate。
- 浏览器的前进、后退操作会触发
popstate。 - 但主动调用
pushState()不会触发,需要自己派发或封装监听。
onpopstate 可以监听到 pushstate 事件吗?
不能 。onpopstate 只在用户点击浏览器的前进/后退,或调用 history.back()、history.forward()、history.go() 时触发。主动调用 pushState() 或 replaceState() 不会触发。框架通常会在调用后手动执行路由更新逻辑。
性能优化
Vue 性能优化策略
- 模板和渲染优化
- 使用 v-if / v-show 区分频繁切换和固定显示的元素。
- 对列表使用 key 提高虚拟 DOM 节点复用效率。
- 静态内容标记为 静态节点,减少重复渲染。
- 组件懒加载,按需加载路由或组件。
- 响应式数据优化
- 避免不必要的响应式数据,减少 watcher 数量。
- 对复杂对象使用 computed 缓存计算结果。
- 对频繁变化的数据,使用 v-once / v-memo 避免重复渲染。
- 事件与 DOM 操作优化
- 使用事件修饰符和 节流 / 防抖 减少频繁回调。
- 频繁切换元素时,使用
v-show替代v-if。
- 内存和资源优化
- 组件销毁时清理事件监听、定时器、第三方库引用。
- 使用
keep-alive缓存组件状态,但注意释放不再使用的组件。
- 服务端渲染(SSR)与懒加载
- 对首屏渲染优化,减少首次加载时间。
- 异步组件和路由懒加载减少 JS 包体积。
设计模式
Vue 中使用的设计模式
- 观察者模式
- 数据变化通知依赖更新,是 Vue 响应式系统的核心。
- 发布-订阅模式
- Watcher 订阅数据变化,Observer 发布变化。
- 策略模式
- 不同渲染模式、生命周期钩子处理、指令更新方式等采用策略模式封装。
- 工厂模式
- 创建 VNode、组件实例、渲染函数等对象。
- 单例模式
- VueRouter、Vuex 实例在应用中通常是单例。
- 代理模式
- 用于数据代理和响应式实现,代替 Vue2 的
Object.defineProperty。
- 用于数据代理和响应式实现,代替 Vue2 的
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 原理
- 服务端渲染
- 将 Vue 组件在服务端渲染成 HTML 字符串。
- 客户端拿到完整 HTML 可以直接展示页面,提升首屏渲染速度和 SEO 友好性。
- 渲染流程
- 服务端 :
- Vue 组件生成渲染函数。
- 渲染函数生成 虚拟 DOM。
- 将虚拟 DOM 转为 HTML 字符串返回给客户端。
- 客户端 :
- 接收 HTML,展示页面。
- Vue 客户端挂载,进行 hydration,将静态 DOM 转换为可交互的 Vue 应用。
- 优化点
- 可以结合路由懒加载、数据预取(
asyncData/fetch)提高性能。 - 避免服务端渲染副作用操作,如直接操作 DOM。
Vue 模板编译原理
- 模板解析
- 将模板字符串解析成 抽象语法树(AST) ,描述节点结构和指令信息。
- 优化阶段
- 标记静态节点、静态根,减少后续渲染过程中不必要的更新。
- 代码生成
- 将 AST 转换成 渲染函数(返回 VNode),供 Vue 在渲染阶段调用。
- 执行渲染函数生成 HTML。
Vuex
Vuex 实现原理
Vuex = 响应式 state + mutation 管理修改 + action 异步处理 + getters 计算状态
Vuex 是 Vue 的 集中式状态管理库 ,核心原理是通过 响应式对象 + 单向数据流 + 事件订阅机制 实现状态管理:
核心组成:
- State :存储共享状态,是响应式对象,组件通过
mapState或this.$store.state访问。 - Getter :类似
computed,依赖state计算衍生状态,自动缓存。 - Mutation :唯一修改
state的方式,同步操作,保证状态可追踪。 - Action :处理异步逻辑,内部调用
mutation修改state。 - Module :支持模块化管理
state、mutation、action、getter。
数据流:
Component -> dispatch action -> commit mutation -> update state -> view 自动更新
State是响应式的,组件依赖收集后,会在state改变时自动重新渲染。- Vuex 内部通过 Vue 的响应式系统实现状态响应。
mutations 能否异步
不能 。Vuex 所有状态更新唯一途径都是 mutation,异步操作通过 Action 来提交 mutation 实现,方便跟踪每一个状态的变化。若 mutations 是异步的,就无法很好地追踪状态,给调试带来困难。
刷新页面数据丢失问题
- 原因:Vuex 数据保存在运行内存中,页面刷新会重新加载 Vue 实例,Vuex 数据就会被清空。
- 解决方法 :
- 可结合
localStorage/sessionStorage持久化。 - 使用插件,如
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 是独立的,不需要命名空间 |
| 数据定义 | state、getters、mutations、actions分开写 |
全部集中在 defineStore里,更直观 |
| 调试工具 | 支持 Vue DevTools | 同样支持(且更清晰) |
| 兼容性 | Vue2/3 都能用(Vuex4 支持 Vue3) | 主要为 Vue3 设计(可在 Vue2 用插件兼容) |
| 学习成本 | 较高 | 较低,语法接近 Composition API |
为什么项目优先采用 Vuex 而不是 Pinia?
新项目会更倾向 Pinia,老项目或保守团队会优先 Vuex。
- 历史项目兼容性
- 大量项目是 Vue2 架构,Vuex 已深度集成。
- 升级到 Pinia 需改写逻辑、迁移工具链,成本高。
- 团队和生态惯性
- Vuex 存在多年,文档、教程、插件生态成熟。
- 老项目、老团队成员更熟悉 Vuex 的使用模式。
- 规范与稳定性
- Vuex 强制使用
mutation改状态,结构清晰、便于调试与规范团队协作。 - Pinia 更灵活,但也意味着约束更少,对团队自律要求更高。
- 第三方依赖兼容
- 一些旧组件库、插件仍默认依赖 Vuex(例如某些权限、缓存中间件)。
什么是状态管理?作用是什么?
- 状态管理:数据需要保存在应用中某个位置,这些数据管理称之为状态管理。
- 作用 :当使用 Vue 进行单页应用开发时,经常会在各个组件之间数据通信,比较简单情况下,可以用
props和emit事件进行传递,数据逻辑过于复杂时,需要状态管理。
其他
$attrs / $listeners / 插槽透传
$attrs 透传属性,$listeners 透传事件,插槽透传内容
- Vue3 中
$listeners被合并到$attrs中,通过v-on="$attrs"透传事件。
- $attrs
- 包含 父组件传递给子组件,但子组件没有显式声明的
props。 - 常用于 将未声明的属性透传给子组件的根元素或子组件。
- $listeners(Vue2 特有)
- 包含 父组件传递给子组件的事件监听器。
- 可以通过
$listeners将事件透传给子组件内部元素。
- 插槽透传
- 子组件内部使用
<slot>或<slot name="xxx">将父组件传入的内容渲染出来。 - 可以实现 父组件内容在子组件内部复用。
Vue 与 React 的区别
- 模板方式:Vue 使用模板 + 指令,React 使用 JSX(函数式写法)。
- 响应式机制 :Vue 内置响应式(Vue2
Object.defineProperty/ Vue3Proxy),React 通过setState或 Hook 管理状态。 - 数据流 :Vue 默认单向 + 双向绑定(
v-model),React 默认单向流。 - 组件通信 :Vue 提供
props/events/provide-inject/Vuex,React 主要通过props和Context。 - 学习成本: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 的问题:
- 当列表顺序发生变化(插入、删除、移动)时,索引会改变,虚拟 DOM 会认为节点是不同的,从而销毁重建,导致 性能浪费 和 状态丢失 (如
input输入内容丢失)。 - 对于静态列表或不会变的列表,使用
index可以,但对动态列表不推荐。
推荐做法:
- 尽量使用 唯一且稳定的 id 作为
key,以保证节点复用。
Vue 指令,如何添加自定义指令
Vue 指令:
- 指令是带有
v-前缀的特殊属性,用来在 DOM 上执行特定操作,如v-if、v-show、v-model。
自定义指令:
- 用
Vue.directive函数来添加自定义指令。 - Vue 允许用户创建自定义指令,扩展 DOM 功能。
- 核心是提供 生命周期钩子函数 :
- bind / created:指令绑定到元素时执行。
- inserted / mounted:元素插入 DOM 时执行。
- update / updated:绑定值更新时执行。
- unbind / unmounted:指令解绑时执行。
应用场景:如自动聚焦、拖拽、节流防抖等 DOM 操作。