Skip to content

Vue 核心原理

MVVM · 响应式(Observer / Proxy)· diff · 生命周期 · 虚拟 DOM · 初始化流程


一、Vue2 响应式原理(Observer / Dep / Watcher)

  • MVVM 架构:Model(数据)+ View(视图)+ ViewModel(核心,负责数据与视图关联)
  • 核心组成:Observer(监听器)+ Compiler(解析器)+ Watcher(订阅者)+ Dep(发布者)

Observer — 使数据可观测

js
function defineReactive(obj, key, val) {
  if (typeof val === 'object') new Observer(val)  // 递归子属性
  const dep = new Dep()
  Object.defineProperty(obj, key, {
    get() { dep.depend(); return val },         // 收集依赖
    set(newVal) { if (val !== newVal) { val = newVal; dep.notify() } }  // 触发更新
  })
}

Dep — 依赖管理器

js
class Dep {
  constructor() { this.subs = [] }
  depend() { if (window.target) this.addSub(window.target) }
  notify() { this.subs.forEach(sub => sub.update()) }
}

Watcher — 订阅者

js
class Watcher {
  get() {
    window.target = this        // 将自身设为全局目标
    let value = this.getter.call(this.vm, this.vm)  // 触发 getter → dep 收集 watcher
    window.target = undefined   // 释放
    return value
  }
}

完整流程

  1. Data 通过 Observer 转换为 getter/setter
  2. 读取数据时触发 getter → dep.depend() 收集当前 Watcher
  3. 修改数据时触发 setter → dep.notify() 通知所有 Watcher
  4. Watcher 收到通知后调用 update() → 更新视图

Object.defineProperty 的不足:只能侦测属性的读取和设置,无法侦测属性新增和删除。Vue 解决方案:Vue.set / Vue.delete API。


二、Vue3 响应式升级(Proxy)

  • Vue2:Object.defineProperty 劫持属性,需深度遍历,无法监听动态新增/删除、数组索引和 length
  • Vue3:Proxy 拦截对象任意属性的读写、新增、删除操作;配合 Reflect 操作源对象,保证行为与原生一致

Vue3 响应式核心简易实现

js
var activeEffect = null
function effect(fn) { activeEffect = fn; activeEffect(); activeEffect = null }
var depsMap = new WeakMap()
function gather(target, key) {
  if (!activeEffect) return
  let depMap = depsMap.get(target) || depsMap.set(target, new Map()).get(target)
  let dep = depMap.get(key) || depMap.set(key, new Set()).get(key)
  dep.add(activeEffect)
}
function trigger(target, key) {
  const dep = depsMap.get(target)?.get(key)
  if (dep) dep.forEach(e => e())
}
function reactive(target) {
  return new Proxy(target, {
    get(target, key, receiver) { gather(receiver, key); return Reflect.get(target, key, receiver) },
    set(target, key, value, receiver) { Reflect.set(target, key, value, receiver); trigger(receiver, key); return true }
  })
}
// ref 本质是 reactive({ value: xxx })

三、Vue3 性能提升

编译阶段优化

  • 静态提升(hoistStatic):静态节点在 render 外创建一次,后续复用
  • Patch Flag:动态标签末尾加标记,只追踪带 patchFlag 的节点,跳过逐层遍历
  • 缓存事件处理函数(cacheHandler):避免每次重新生成新 function
  • Fragments:template 不需要唯一根节点,减少标签层级和内存占用

源码体积:Tree-shaking 友好,按需打包(ref/reactive/computed 等仅在使用时引入)


四、Vue 数组处理

  • Vue2 重写了数组的 7 个变更方法:pushpopshiftunshiftsplicesortreverse
  • 无法通过索引直接修改数组项,需使用 Vue.set / this.$set
  • Vue3 使用 Proxy 可直接监听数组索引和 length 变化

五、Vue key 与 diff 算法

  • diff 算法策略:深度优先,同层比较,不跨层级
  • patchVnode 过程:比较文本/子节点差异,调用 updateChildren 更新子节点
  • updateChildren:设置新旧 VNode 头尾指针,从两端向中间比较,根据 key 映射复用节点
  • key 的作用:唯一标识,帮助 diff 算法精准识别节点,实现本地复用策略,提升性能

六、Vue 生命周期

Vue2 生命周期钩子

  1. beforeCreate:数据观测和事件初始化之前,不能访问 data/computed/methods
  2. created:实例创建完成,data/computed/watch/methods 已配置,$el 未挂载
  3. beforeMount:挂载前,模板已编译但未渲染到 DOM
  4. mounted:el 被 $el 替换,模板渲染到页面,可发起 ajax 请求
  5. beforeUpdate:响应式数据更新时,DOM 还未重新渲染
  6. updated:DOM 已根据数据变化更新
  7. beforeDestroy:实例销毁前,实例仍完全可用
  8. destroyed:销毁后,所有事件监听器移除

Vue3 变化beforeDestroybeforeUnmountdestroyedunmounted

Composition API 形式onBeforeMountonMountedonBeforeUpdateonUpdatedonBeforeUnmountonUnmounted

父子组件执行顺序:父 beforeCreate → created → beforeMount → 子 beforeCreate → created → beforeMount → mounted → 父 mounted


七、Vue 路由

  • 路由本质:URL 与组件的映射关系,通过 hash 或 history 模式实现页面切换
  • hash 模式:URL 中带 #,利用 hashchange 事件监听,兼容性好
  • history 模式:利用 HTML5 pushState/replaceState,URL 更美观,需服务端配置

八、Vue 组件通信方式

  • props / $emit:父子组件通信
  • ref:父组件获取子组件实例
  • EventBus($bus):兄弟组件通信
  • $parent / $root:访问父/根组件实例
  • $attrs / $listeners:祖先与后代组件传递(Vue3 中 $listeners 已移除,合并到 $attrs)
  • Provide / Inject:祖先组件向后代组件注入数据
  • Vuex / Pinia:全局状态管理(详见 状态管理与中间件

九、v-show 与 v-if 区别

特性v-showv-if
控制方式CSS display:none,DOM 还在真正添加/移除 DOM 元素
性能更高初始渲染消耗更高切换消耗
使用场景频繁切换条件很少变化

十、$nextTick 原理

  • 作用:在下次 DOM 更新循环结束后执行回调,获取更新后的 DOM
  • 核心流程:响应式数据更新 → watcher 加入队列 → nextTick 将 flushSchedulerQueue 放入 callbacks → 通过 timerFunc 放入微任务队列(首选 Promise → MutationObserver → setImmediate → setTimeout)

十一、keep-alive

  • 作用:缓存组件实例,避免重复渲染
  • 属性include(匹配才缓存)、exclude(匹配不缓存)、max(最多缓存数)
  • 生命周期:多出 activated(进入)和 deactivated(离开)
  • 首次进入:beforeCreate → created → mounted → activated
  • 再次进入:只触发 activated(跳过 created/mounted)

十二、data 为什么是函数

  • 组件可能有多个实例,若 data 是对象则所有实例共享同一引用(数据污染)
  • 函数返回新对象,每个实例拥有独立的数据副本

十三、虚拟 DOM(VNode)

  • 概念:用 JS 对象描述真实 DOM 结构,追踪如何改变真实 DOM
  • VNode 核心属性:tag、data、children、text、elm(对应真实 DOM)、key

十四、Vue2 初始化与挂载流程

new Vue_init() → 合并配置 → initLifecycle → initEvents → initRender → beforeCreateinitState(data/props/computed/watch)→ created → 若有 el 则 $mount

mountComponent 核心

js
new Watcher(vm, () => { vm._update(vm._render()) }, ...)
// 渲染 Watcher:初始化执行一次 + 数据变化时执行

十五、Vue3 初始化流程

createApp()mount()render()patch()processComponent()mountComponent()


十六、Vue2 vs Vue3 核心区别

  1. 响应式:Object.definePropertyProxy
  2. TypeScript 支持更完善
  3. 新增 Composition API,更好的逻辑复用
  4. v-if 和 v-for 优先级变化(Vue3 中 v-if 优先)
  5. 静态元素提升、虚拟节点静态标记
  6. 生命周期钩子重命名(beforeDestroy→beforeUnmount 等)
  7. 打包体积优化、SSR 渲染性能提升
  8. 支持多个根节点

十七、Vue3 新组件

Fragment:组件可以没有根标签,内部包含在 Fragment 虚拟元素中,减少标签层级

Teleport:将组件的 HTML 结构移动到指定位置(如弹窗挂载到 body)

html
<teleport to="body">
  <div v-if="isShow" class="dialog">弹窗内容</div>
</teleport>

Suspense:等待异步组件时渲染额外内容(loading),提供 #default#fallback 两个插槽


十八、Vue3 大型表格性能优化

场景:20行 × 180列 el-table,switch 切换耗时 7-8s(Vue3 响应式开销)

优化手段

  1. shallowRef 替代 ref(减少 17-20%)

    • ref() 深层递归代理所有属性,shallowRef() 仅代理 .value 本身
    js
    const data = shallowRef([])  // 仅 data.value = xxx 触发更新
  2. 渲染函数中避免响应式依赖(减少 7-20%)

    js
    // 提取为普通数组,避免重复代理
    const plainCols = columns.value.map(item => ({ realWidth: item.realWidth }))
  3. v-if 替代 disabled(减少 80%)

    • el-tooltip 等复杂组件会成倍增加 patch 比对耗时
    html
    <!-- 仅需要时才创建 tooltip 组件 -->
    <span v-if="!showTooltip">{{ text }}</span>
    <el-tooltip v-else>...</el-tooltip>

相关