◆

Vue2 初始化与响应式原理实现流程

2023-04-19 07:370
  1. 首先拿到用户传入的 options 然后,赋值给 vue 内部的 $options
  2. 初始化 state,主要就是拿到 options 中的 data,然后对 data 进行递归 Object.defineProperty 监听
  3. 在劫持监听之前,需要做一些判断,比如 data 的值是否是对象,是否已经被劫持过
  4. 如果没有被劫持过,可以增加一个属性标志,等下次使用,判断有没有这个标志,就能知道是否已经被劫持过 ob
  5. this.walk 中对 data 使用 Object.keys 对对象的 data key 进行劫持
  6. 修改时,由于属性值被改变,应该重新进行观测
  7. 用 vm 代理 vm.data
  8. Obejet.defineproperty 无法监听到数组和对象的内部的变化,所以 vue2.0 重写了常用的数组的方法来使得数组更新的时候触发页面的更新 数组重新定义其原型 'push','pop','shift','unshift','reverse','sort','splice', push unshift splice 新增的值进行劫持观测
  9. ast -> render->虚拟 dom->真是 dom
  10. 每个响应式属性都有 dep,dep 存放了依赖这个属性的 watcher(watcher 是观测数据变化的函数),如果数据发生变化,dep 就会通知所有的观察者 watcher 去调用更新方法 观察者需要被目标对象收集,目的是通知依赖它的所有观察者,get 时添加观察者对象,set 时通知更新,循环,触发每一个
  11. 异步刷新,变量控制,定时器
  12. vue 通过 MutationObserver 监听 dom 的改变

https://juejin.cn/post/6966418770768166919

function Vue(options) {
  // options就是用户的选项
  this._init(options); // 默认就调用了init
}

import { newArrayProto } from './array';

function proxy(vm, target, key) {
  Object.defineProperty(vm, key, {
    // vm.name
    get() {
      return vm[target][key]; // vm._data.name
    },
    set(newValue) {
      vm[target][key] = newValue;
    },
  });
}

function initData(vm) {
  let data = vm.$options.data; // data可能是函数和对象
  data = typeof data === 'function' ? data.call(vm) : data; // data是用户返回的对象

  vm._data = data; // 我将返回的对象放到了_data上
  // 对数据进行劫持 vue2 里采用了一个api defineProperty
  observe(data);

  // 将vm._data 用vm来代理就可以了
  for (let key in data) {
    proxy(vm, '_data', key);
  }
}

class Observer {
  constructor(data) {
    // Object.defineProperty只能劫持已经存在的属性 (vue里面会为此单独写一些api  $set $delete)
    Object.defineProperty(data, '__ob__', {
      value: this,
      enumerable: false, // 将__ob__ 变成不可枚举 (循环的时候无法获取到)
    });
    // data.__ob__ = this; // 给数据加了一个标识 如果数据上有__ob__ 则说明这个属性被观测过了
    if (Array.isArray(data)) {
      // 这里我们可以重写数组中的方法 7个变异方法 是可以修改数组本身的
      data.__proto__ = newArrayProto; // 需要保留数组原有的特性,并且可以重写部分方法
      this.observeArray(data); // 如果数组中放的是对象 可以监控到对象的变化
    } else {
      this.walk(data);
    }
  }

  walk(data) {
    // 循环对象 对属性依次劫持
    // "重新定义"属性   性能差
    Object.keys(data).forEach(key => defineReactive(data, key, data[key]));
  }

  observeArray(data) {
    // 观测数组
    data.forEach(item => observe(item));
  }
}

export function defineReactive(target, key, value) {
  // 闭包  属性劫持
  observe(value); // 对所有的对象都进行属性劫持
  Object.defineProperty(target, key, {
    get() {
      // 取值的时候 会执行get
      console.log('key', key);
      return value;
    },
    set(newValue) {
      // 修改的时候 会执行set
      if (newValue === value) return;
      observe(newValue);
      value = newValue;
    },
  });
}

export function observe(data) {
  // 对这个对象进行劫持
  if (typeof data !== 'object' || data == null) {
    return; // 只对对象进行劫持
  }
  if (data.__ob__ instanceof Observer) {
    // 说明这个对象被代理过了
    return data.__ob__;
  }
  // 如果一个对象被劫持过了,那就不需要再被劫持了 (要判断一个对象是否被劫持过,可以增添一个实例,用实例来判断是否被劫持过)

  return new Observer(data);
}
💬留言板0

还没有留言,来发表第一条吧~

PAGE LOAD / LIVEVue2 初始化与响应式原理实现流程加载中