vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > Vue2与Vue3响应式原理

Vue 2与Vue 3响应式原理及实际应用详解

作者:小熊猫写前端

Vue.js的响应式系统是其核心特性之一,它使得数据和视图能够自动保持同步,这篇文章主要介绍了Vue2与Vue3响应式原理及实际应用的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

Vue 2 与 Vue 3 响应式原理详解

深入理解 Vue 响应式系统的核心机制,对比 Vue 2 和 Vue 3 的实现差异

Vue 2 响应式原理

核心机制:Object.defineProperty

Vue 2 使用 Object.defineProperty 拦截对象属性的访问和修改,这是其响应式系统的基石。

实现流程

1. 初始化阶段

// 伪代码示例
function defineReactive(obj, key, val) {
  const dep = new Dep(); // 每个属性对应一个依赖收集器
  
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get() {
      // 依赖收集
      if (Dep.target) {
        dep.addSub(Dep.target); // 将当前 Watcher 添加到 Dep 中
      }
      return val;
    },
    set(newVal) {
      if (newVal !== val) {
        val = newVal;
        // 派发更新
        dep.notify(); // 通知所有 Watcher 更新
      }
    }
  });
}

关键组件:

2. 依赖收集(getter)

当访问响应式属性时,会触发 getter:

get() {
  // 将当前 Watcher 添加到 Dep 中
  Dep.target && dep.addSub(Dep.target);
  return value;
}

流程说明:

  1. 组件渲染时访问 data 中的属性
  2. 触发 getter
  3. 将当前 Watcher(组件实例)添加到该属性的 Dep 中
  4. 建立依赖关系

3. 派发更新(setter)

当修改响应式属性时,会触发 setter:

set(newVal) {
  if (newVal !== value) {
    value = newVal;
    // 通知所有 Watcher 更新
    dep.notify();
  }
}

流程说明:

  1. 修改属性值
  2. 触发 setter
  3. 调用 dep.notify() 通知所有依赖该属性的 Watcher
  4. Watcher 执行更新函数,触发组件重新渲染

4. 更新视图

Watcher 收到通知后执行更新函数,触发组件重新渲染。

局限性

1. 无法监听数组索引和长度变化

// Vue 2 中无法监听到这些变化
this.list[0] = 'new value'; // ❌ 不会触发更新
this.list.length = 0; // ❌ 不会触发更新

解决方案: Vue 2 重写了数组的 7 个方法:

// 只有通过这些方法才能触发更新
this.list.push('new item'); // ✅ 会触发更新
this.list.splice(0, 1, 'new value'); // ✅ 会触发更新

2. 无法监听对象属性的新增/删除

// Vue 2 中无法监听到新增属性
this.obj.newProperty = 'value'; // ❌ 不会触发更新
delete this.obj.property; // ❌ 不会触发更新

解决方案: 使用 Vue.set 或 this.$set

// 正确的方式
Vue.set(this.obj, 'newProperty', 'value');
// 或
this.$set(this.obj, 'newProperty', 'value');

3. 性能问题

Vue 3 响应式原理

核心机制:Proxy

Vue 3 使用 Proxy 代理整个对象,可以拦截更多操作,提供了更强大的响应式能力。

实现流程

1. 创建响应式对象

// reactive 使用 Proxy
function reactive(target) {
  return new Proxy(target, {
    get(target, key, receiver) {
      // 依赖收集
      track(target, key);
      return Reflect.get(target, key, receiver);
    },
    set(target, key, value, receiver) {
      const result = Reflect.set(target, key, value, receiver);
      // 派发更新
      trigger(target, key);
      return result;
    },
    deleteProperty(target, key) {
      const result = Reflect.deleteProperty(target, key);
      trigger(target, key);
      return result;
    },
    has(target, key) {
      track(target, key);
      return Reflect.has(target, key);
    }
    // 还可以拦截更多操作...
  });
}

关键优势:

2. 依赖收集(track)

使用 WeakMap 存储依赖关系:

// 依赖存储结构
// WeakMap<target, Map<key, Set<effect>>>
const targetMap = new WeakMap();

function track(target, key) {
  if (!activeEffect) return;
  
  let depsMap = targetMap.get(target);
  if (!depsMap) {
    targetMap.set(target, (depsMap = new Map()));
  }
  
  let dep = depsMap.get(key);
  if (!dep) {
    depsMap.set(key, (dep = new Set()));
  }
  
  dep.add(activeEffect);
}

数据结构说明:

3. 派发更新(trigger)

找到该属性对应的所有 effect 并执行:

function trigger(target, key) {
  const depsMap = targetMap.get(target);
  if (!depsMap) return;
  
  const dep = depsMap.get(key);
  if (dep) {
    dep.forEach(effect => {
      effect();
    });
  }
}

4. Effect 系统

effect 是响应式更新的执行单元:

let activeEffect = null;

function effect(fn) {
  activeEffect = fn;
  fn(); // 执行时自动收集依赖
  activeEffect = null;
}

Effect 的应用:

优势

1. 可监听数组索引和长度变化

const list = reactive([1, 2, 3]);
list[0] = 10; // ✅ 自动触发更新
list.length = 0; // ✅ 自动触发更新

2. 可监听对象属性的新增/删除

const obj = reactive({ name: 'Vue3' });
obj.age = 18; // ✅ 自动响应式
delete obj.name; // ✅ 自动触发更新

3. 性能更好

核心对比总结

特性Vue 2Vue 3
核心 APIObject.definePropertyProxy
监听范围对象属性(需递归)整个对象
数组监听需重写方法原生支持
新增属性需 Vue.set自动支持
删除属性需 Vue.delete自动支持
性能初始化时递归遍历按需收集,性能更好
依赖存储Dep + WatcherWeakMap + Set + effect
内存占用需要为每个属性创建 Dep按需创建,更节省内存
拦截能力只能拦截 get/set可拦截多种操作

实际应用示例

Vue 2 示例

export default {
  data() {
    return {
      obj: { name: 'Vue2' },
      list: [1, 2, 3]
    };
  },
  methods: {
    addProperty() {
      // ❌ 错误:不会触发更新
      // this.obj.age = 18;
      
      // ✅ 正确:需要使用 $set
      this.$set(this.obj, 'age', 18);
    },
    updateArray() {
      // ❌ 错误:不会触发更新
      // this.list[0] = 10;
      
      // ✅ 正确:需要使用数组方法
      this.list.splice(0, 1, 10);
    }
  }
};

Vue 3 示例

import { reactive, ref } from 'vue';

export default {
  setup() {
    const obj = reactive({ name: 'Vue3' });
    const list = reactive([1, 2, 3]);
    
    const addProperty = () => {
      // ✅ 直接添加即可响应式
      obj.age = 18;
    };
    
    const updateArray = () => {
      // ✅ 直接修改即可响应式
      list[0] = 10;
      list.length = 5; // ✅ 也支持
    };
    
    return {
      obj,
      list,
      addProperty,
      updateArray
    };
  }
};

Composition API 示例

import { reactive, ref, computed, watch } from 'vue';

export default {
  setup() {
    // reactive 用于对象
    const state = reactive({
      count: 0,
      name: 'Vue3'
    });
    
    // ref 用于基本类型
    const message = ref('Hello');
    
    // computed 自动创建 effect
    const doubleCount = computed(() => state.count * 2);
    
    // watch 自动创建 effect
    watch(() => state.count, (newVal, oldVal) => {
      console.log(`count changed: ${oldVal} -> ${newVal}`);
    });
    
    return {
      state,
      message,
      doubleCount
    };
  }
};

核心概念补充

Vue 3 的三种响应式 API

1. reactive

用于对象,返回 Proxy 代理对象:

const obj = reactive({ name: 'Vue3', age: 18 });
// 可以直接访问和修改
obj.name = 'Vue 3.0';
obj.city = 'Beijing'; // ✅ 新增属性自动响应式

2. ref

用于基本类型,返回包含 .value 的对象(内部也是 Proxy):

const count = ref(0);
// 在 JavaScript 中需要使用 .value
count.value = 10;
// 在模板中自动解包
// <div>{{ count }}</div> 不需要 .value

ref 也可以用于对象:

const obj = ref({ name: 'Vue3' });
obj.value.name = 'Vue 3.0'; // 需要 .value

3. readonly

创建只读的响应式对象:

const state = reactive({ count: 0 });
const readonlyState = readonly(state);

readonlyState.count = 10; // ❌ 警告:无法修改只读属性

响应式流程总结

数据变化 
  ↓
Proxy 拦截 
  ↓
trigger 触发 
  ↓
找到所有 effect 
  ↓
执行更新 
  ↓
重新渲染

完整示例:理解响应式流程

import { reactive, effect } from 'vue';

// 1. 创建响应式对象
const state = reactive({ count: 0, name: 'Vue3' });

// 2. 创建 effect(类似组件的渲染函数)
effect(() => {
  // 访问响应式属性时会自动收集依赖
  console.log(`Count: ${state.count}, Name: ${state.name}`);
});

// 3. 修改数据
state.count = 1; // ✅ 自动触发 effect 执行
state.name = 'Vue 3.0'; // ✅ 自动触发 effect 执行
state.age = 18; // ✅ 新增属性也会触发(如果 effect 中访问了它)

// 输出:
// Count: 0, Name: Vue3  (初始执行)
// Count: 1, Name: Vue3  (count 变化触发)
// Count: 1, Name: Vue 3.0  (name 变化触发)

总结

Vue 2 和 Vue 3 的响应式系统在核心实现上有本质区别:

理解这些差异有助于:

参考资源:

到此这篇关于Vue2与Vue3响应式原理及实际应用详解的文章就介绍到这了,更多相关Vue2与Vue3响应式原理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文