vue源码中如何实现数据监听?

发布于:2024-04-29 ⋅ 阅读:(31) ⋅ 点赞:(0)

在Vue.js框架中,数据监听是一个核心功能,它允许开发者创建响应式的应用程序。这一功能的实现主要依赖于Vue的响应式系统,其中最关键的部分是如何监控数据变化并响应这些变化。以下是一个对Vue中数据监听实现方式的简要概述,适用于理解Vue 2.x版本的源码结构。

核心概念

  1. 响应式对象: Vue使用Object.defineProperty来把这些属性转换为getter/setter,从而能够监听数据的变化。这个过程在Vue实例初始化时,通过data选项中的数据进行处理。

  2. 依赖收集: 在getter中进行依赖收集。当组件或者指令访问某个数据时,会触发getter,此时,当前活动的观察者(Watcher)会被添加到这个数据属性的依赖列表中。这样,当数据变化时,可以通知所有依赖于此数据的观察者。

  3. 派发更新: 在setter中处理数据变化。当数据被修改时,setter会被触发,它将通知所有依赖于这个数据的观察者,告知它们所依赖的数据已经改变。

实现细节

初始化响应式系统
function initData(vm) {
  let data = vm.$options.data;
  data = vm._data = typeof data === 'function' ? data.call(vm, vm) : data || {};
  const keys = Object.keys(data);

  keys.forEach(key => {
    proxy(vm, `_data`, key);
  });

  observe(data);
}

function proxy(vm, sourceKey, key) {
  Object.defineProperty(vm, key, {
    get: function proxyGetter() {
      return vm[sourceKey][key];
    },
    set: function proxySetter(val) {
      vm[sourceKey][key] = val;
    }
  });
}
创建观察者对象和依赖类
class Dep {
  constructor() {
    this.subs = [];
  }

  addSub(sub) {
    this.subs.push(sub);
  }

  notify() {
    this.subs.forEach(sub => sub.update());
  }
}

class Watcher {
  constructor(vm, expOrFn, cb) {
    this.vm = vm;
    this.cb = cb;
    this.getter = parsePath(expOrFn);
    this.value = this.get();
  }

  get() {
    Dep.target = this;
    let value = this.getter.call(this.vm, this.vm);
    Dep.target = null;
    return value;
  }

  update() {
    const oldValue = this.value;
    this.value = this.get();
    this.cb.call(this.vm, this.value, oldValue);
  }
}
数据劫持与依赖收集
function observe(value) {
  if (typeof value !== 'object') return;
  let ob;
  if (typeof value.__ob__ !== 'undefined') {
    ob = value.__ob__;
  } else {
    ob = new Observer(value);
  }
  return ob;
}

class Observer {
  constructor(value) {
    this.value = value;
    this.dep = new Dep();
    def(value, '__ob__', this);
    if (Array.isArray(value)) {
      // 处理数组
      // 略
    } else {
      this.walk(value);
    }
  }

  walk(obj) {
    const keys = Object.keys(obj);
    keys.forEach(key => {
      defineReactive(obj, key, obj[key]);
    });
  }
}

function defineReactive(obj, key, val) {
  const dep = new Dep();
  let childOb = observe(val);
  Object.defineProperty(obj, key, {
    get() {
      if (Dep.target) {
        dep.addSub(Dep.target);
        if (childOb) {
          childOb.dep.addSub(Dep.target);
        }
      }
      return val;
    },
    set(newVal) {
      if (newVal === val) return;
      val = newVal;
      childOb = observe(newVal);
      dep.notify();
    }
  });
}

结论

Vue的响应式系统是通过利用JavaScript的Object.defineProperty方法来劫持各个属性的getter和setter,实现数据的观察与派发更新。这种机制使得Vue能够非常高效地更新DOM,确保用户界面与数据状态保持一致。这个系统的设计也展示了现代前端框架中数据驱动视图的核心思想。


网站公告

今日签到

点亮在社区的每一天
去签到