Vue知识总结

55 阅读1分钟

一、模板语法

  1. 模板语法: {{}}, computed ,watch
  2. 指令
  3. 条件渲染,列表渲染v-if v-for

二、组件

  1. 组件通信: 父子props,子父$emit,兄弟/跨层级 provide/inject, event bus,vuex/pinia
  2. 插槽:
  3. 生命周期
  4. data为什么是函数?为了保证每个组件实例都拥有一个独立的数据副本,避免多个实例共享同一个数据对象所导致的状态污染。

二、核心机制-响应式,双向绑定

核心思想是数据劫持+发布-订阅模式

2.1 原理

image.png

2.1.1 Vue 2的响应式原理:基于 Object.defineProperty

Vue 2通过 Object.defineProperty API来劫持数据的读取和设置(getter/setter)。

实现过程主要分为以下几个步骤:

  1. 数据劫持 (Observer)

    • 当一个普通的JavaScript对象被传入Vue实例作为 data 选项时,Vue会遍历此对象的所有属性。
    • 对于每个属性,Vue会使用 Object.defineProperty 重新定义它的 get 和 set 方法。
    • 这样,当读取 (get) 这个属性时,Vue就知道“谁用了这个数据”。
    • 修改 (set) 这个属性时,Vue就能发出通知,告诉那些用到它的地方进行更新。
  2. 依赖收集 (Dep)

    • 在 getter 中,Vue会进行依赖收集
    • 每个被劫持的属性都会拥有自己的一个依赖管理器 (Dep对象)
    • 当视图在渲染计算属性在计算时,会创建一个Watcher(订阅者)。这个Watcher在求值过程中会触发数据的 getter
    • 此时,Dep 就会把这个当前的 Watcher 添加到自己的订阅者列表里。这就建立了“数据→依赖(Watcher)”的关系。
  3. 派发更新 (Notify)

    • 当数据的值被修改,即触发了 setter 时,Vue会通知对应的 Dep
    • Dep 会遍历它管理的所有 Watcher,并调用它们的 update 方法。
    • Watcher 的 update 方法最终会触发组件的重新渲染(re-render)或计算属性的重新计算,从而将最新的数据反映到视图上。

Vue 2实现的局限性:

  • 无法检测对象属性的添加或删除:由于 Object.defineProperty 是在初始化时遍历属性进行劫持的,所以后续新增的属性不是响应式的(需使用 Vue.set)。
  • 对数组的限制:无法劫持数组的索引操作(如 arr[index] = newValue)和修改长度的操作(如 arr.length = 0)。Vue通过重写数组的7个变更方法(pushpopshiftunshiftsplicesortreverse)来实现数组的响应式。

2.1.2 Vue 3的响应式原理:基于 Proxy

Vue 3使用了ES6的 Proxy 来重写了响应式系统,解决了Vue 2的大部分局限性。

实现过程:

  1. 数据劫持 (Reactive)

    • Vue 3通过 reactive() 函数创建一个对象的响应式代理。
    • Proxy 可以拦截对一个对象任何性质的操作,包括属性的读取 (get)、设置 (set)、删除 (deleteProperty)、in 操作符等共13种,而不仅仅是 get 和 set
    • 这意味着,无论是已有属性还是新增、删除属性,都能被完美劫持。
  2. 依赖收集与派发更新 (Track/Trigger)

    • track (追踪) :在 Proxy 的 get 拦截中,Vue会执行 track 函数,用于建立“目标对象→属性→依赖”的关系。这里的“依赖”在Vue 3中被称为 effect,类似于Vue 2的 Watcher
    • trigger (触发) :在 Proxy 的 setdeleteProperty 等拦截中,Vue会执行 trigger 函数,它会根据操作的类型和key,找到所有相关的 effect 并执行它们。
  3. Ref

    • 对于原始值(如 numberstring),Proxy 无法代理。Vue 3提供了 ref 函数来解决。
    • ref 会创建一个响应式的引用对象(RefImpl 实例),该对象的 .value 属性才是真正的值。访问 .value 时进行依赖收集,修改 .value 时触发更新

一个组件一个渲染 Watcher,不是多个,Dep是多个。 Watcher(订阅者)订阅了 Dep(发布者)。 你修改数据 -> 数据的 Dep (发布者) 发出通知 -> 所有订阅了该Dep的 Watcher (订阅者) 收到通知 -> Watcher 执行更新操作 -> 视图变化

// 伪代码,在 userInfo.age 的 getter 内部
if (Dep.target) { // Dep.target 就是当前正在执行的 Watcher (渲染 Watcher)
    dep.depend(); // 核心:让 Dep 收集这个 Watcher
}
return value;
// 伪代码,在 userInfo.age 的 setter 内部
set(newVal) {
  value = newVal;
  dep.notify(); // 核心:Dep 通知所有订阅者!
}

2.2 虚拟DOM

在 Vue 2 中,响应式系统和虚拟 DOM 是两个独立但协同工作的核心模块,两者共同支撑了 Vue 的数据驱动视图能力。

  • 响应式系统:负责追踪数据变化,当数据更新时通知相关依赖(组件)。
  • 虚拟 DOM:负责将数据变化高效地映射到真实 DOM,是响应式系统触发更新后的 “渲染执行者”。

2.3 模板编译

  • 阶段 1:解析(Parse)—— 模板 → AST
  • 阶段 2 转换(Transform)—— AST → 优化后的 AST
  1. 指令转换

    • 将 v-if 转换为条件判断逻辑(if/else)。
    • 将 v-for 转换为循环逻辑(Array.map)。
    • 将 v-bindv-on 转换为对应的属性绑定和事件监听(如 onClick)。
    • 将 {{ name }} 插值转换为 _ctx.name(访问组件上下文的变量)。
  • 阶段 3 生成(Generate)—— AST → 渲染函数

image.png

三、核心机制-组合式api

Vue 组合式 API 的原理是一个有机结合体:

  1. 以响应式系统为引擎ref 和 reactive 提供了数据驱动的动力。
  2. 以 setup() 函数为入口和上下文:它创建了组件初始化的执行环境,并收集所有暴露出的状态和方法。
  3. 以生命周期钩子函数为连接器:它们将逻辑与组件的生命周期阶段挂钩。
  4. 以普通 JavaScript 函数为复用载体:通过函数和闭包,将响应式状态和逻辑封装成可复用的单元。

它并没有引入全新的魔法,而是更底层、更灵活地暴露了 Vue 响应式系统的能力,让开发者能够像编写普通 JavaScript 代码一样来组织组件逻辑,从而获得了更好的类型推断、逻辑复用和代码组织能力。

  • 当渲染函数读取到 setup 返回的某个响应式属性(如 count.value)时,就会触发该响应式属性的 getter,从而将这个组件的渲染 effect(可以看作是一个特殊的 Watcher)收集为依赖。这就建立了数据 → 视图的依赖关系。

四、高级特性 vue 2 mixin

Vue 中的 mixin 是一种代码复用机制,用于将多个组件中可复用的逻辑(如数据、方法、生命周期钩子等)提取到一个独立的对象中,然后在多个组件中 “混入” 这些逻辑,实现代码共享。

  1. 合并规则

    • 数据(data):组件自身数据优先级高于 mixin,若存在同名属性,组件数据会覆盖 mixin
    • 方法、计算属性:同名时,组件自身的会覆盖 mixin 的。
    • 生命周期钩子:mixin 中的钩子会比组件自身的同名钩子先执行(如 mixin 的 created 先于组件的 created 执行)。
    • 组件选项(如 componentsdirectives):会合并为一个对象,同名键会被组件自身覆盖。
  • 缺点

    1. 命名冲突风险:mixin 与组件、mixin 之间可能出现同名属性 / 方法,不易排查。

    2. 逻辑来源模糊:组件中使用的属性或方法可能来自多个 mixin,调试时难以追溯。

    3. 灵活性有限:无法向 mixin 传递参数,难以实现个性化逻辑。

    Vue 3 中推荐使用 组合式 API(setup 函数 + 自定义 Hooks)  替代 mixin,通过函数调用的方式复用逻辑,解决了 mixin 的命名冲突和来源模糊问题,更适合复杂场景。

五、keep alive

  1. 缓存机制
    <keep-alive> 会将包裹的组件实例缓存到一个内部对象(通常称为 cache)中,键值一般是组件的 name 或标签名。当组件需要被切换(如使用 <router-view> 或 v-if)时,不会执行 beforeDestroy 和 destroyed 生命周期,而是被缓存起来。

  2. 激活与失活

    • 被缓存的组件再次渲染时,不会执行 createdmounted 等初始化生命周期,而是触发 activated 钩子。
    • 组件被缓存隐藏时,不会销毁,而是触发 deactivated 钩子。
  3. DOM处理
    <keep-alive> 自身不会渲染 DOM 节点,也不会出现在组件的父链中。它会将缓存的组件实例保存在内存中,对应的 DOM 节点会被暂时移除或隐藏,重新激活时再插入到文档中。

    • activated 是 Vue 的特殊生命周期钩子,需要与 <keep-alive> 配合使用

为什么不会重新渲染?

Vue 的组件生命周期在首次加载 Tab2 时会正常执行:created -> mounted -> ...。
当你切换到 Tab1 时,Tab2 会执行 deactivated
当你再次切换回 Tab2 时,Vue 会直接从缓存中恢复它的整个实例(包括所有的数据、DOM状态),因此只会触发 activated 钩子,而不会再次触发 created 和 mounted

六、路由管理

vue-router

七、状态管理Pinia/Vuex

image.png

vuex: 组件 -> Dispatch Action -> Commit Mutation -> Modify State

pinia: 组件 -> Call Action -> Directly Modify State 或 组件 -> Directly Modify State

nextTick,nextTick ,forceUpdate

  1. vue nextTick 执行时机:
  • 修改数据(如 this.msg = 'new value'
  • Vue 记录需要更新的组件
  • 当前所有同步代码执行完毕
  • Vue 批量更新 DOM
  • 执行 nextTick 回调
// 展示输入框
this.showInput = true;

// DOM更新后设置焦点
this.$nextTick(() => {
  this.$refs.input.focus();
  const width = this.$refs.box.offsetWidth;
});

  1. 数据更新但页面没有更新,强制更新 this.$forceUpdate()

其他、React Vue对比

react vue 有哪些功能是对方没有的?

  1. React 并发渲染 可中断 useTransition
  2. vue 双向绑定、类html模板语法指令系统