vue 响应式原理

84 阅读2分钟

vue2.0 响应式源码分析

Vue 2.0

  1. new Vue({})
  2. initMixin(Vue) 将_init方法是挂载在Vue原型的方法
  3. _init 赋值传入的 options(1 步骤传入的)参数, 执行initState 方法
  4. initState,依次初始化 prop>methods>data>computed>watch
  5. initData
    1. 把data数据代理到vm 也就是Vue实例上面 我们可以使用this.a来访问this._data.a

    2. 对数据进行观测 --响应式数据核心

    3. 递归嵌套 监测每一项data数据,除非数组和Object 对象,创建Observer对象 new Observer(value)

    4. Observer对象 构造函数中,增加了一个不可枚举的__ob__属性,防止已经被响应式观察的数据反复被观测 其次 响应式数据可以使用__ob__来获取 Observer 实例的相关方法 这对数组很关键。

      针对Object 对象 直接使用Object.defineProperty() 进行数据劫持,set 方法 会通知watcher 依赖视图更新。视图数据绑定,调用get方法, get 方法 会生成dep对象 -> watcher 对象 收集依赖

      针对数组,重写数组的7种方法,调用这7种方法,相当于set 方法,会通知watcher 依赖视图更新。 同时监测observe新增的 数组数据。监测observe数组每一个数据, 调用Object 对象数据劫持流程

Vue 3.0

Vue 3.0 改用 Proxy 替代 Object.defineProperty。因为 Proxy 可以直接监听对象和数组的变化

Proxy 监听数据,设置 set 方法 和 get 方法

import { mutableHandlers } from "./baseHandlers"; // 代理相关逻辑
import { isObject } from "./util"; // 工具方法

export function reactive(target) {
  // 根据不同参数创建不同响应式对象
  return createReactiveObject(target, mutableHandlers);
}
function createReactiveObject(target, baseHandler) {
  if (!isObject(target)) {
    return target;
  }
  const observed = new Proxy(target, baseHandler);
  return observed;
}

const get = createGetter();
const set = createSetter();

function createGetter() {
  return function get(target, key, receiver) {
    // 对获取的值进行放射
    const res = Reflect.get(target, key, receiver);
    console.log("属性获取", key);
    if (isObject(res)) {
      // 如果获取的值是对象类型,则返回当前对象的代理对象
      return reactive(res);
    }
    return res;
  };
}
function createSetter() {
  return function set(target, key, value, receiver) {
    const oldValue = target[key];
    const hadKey = hasOwn(target, key);
    const result = Reflect.set(target, key, value, receiver);
    if (!hadKey) {
      console.log("属性新增", key, value);
    } else if (hasChanged(value, oldValue)) {
      console.log("属性值被修改", key, value);
    }
    return result;
  };
}
export const mutableHandlers = {
  get, // 当获取属性时调用此方法
  set, // 当修改属性时调用此方法
};