Vue2的响应式原理及代码

何时进行响应式处理

1. 在new Vue的时候,会调用observe函数,observe函数会调用Observer类,Observer类会调用walk函数,walk函数会调用defineReactive函数,defineReactive函数会调用Object.defineProperty函数,从而实现响应式处理。
2. 在属性被读取的时候触发get()方法,Dep.target进行赋值调用dep.depend(Dep.target)进行依赖收集。
3. 在属性被赋值的时候触发set()方法,对newValue进行observe处理,调用dep.notify()通知依赖进行更新。
4. Dep是依赖收集器/发布者,Watcher是观察者/订阅者,是属性与界面的中介,当属性变化时通知中介去更新视图,当


相关代码如下

// 生成vue的响应式原理
function defineReactive(obj, key, val) {
  // 递归
  observe(val);
  // 创建Dep实例
  const dep = new Dep();
  Object.defineProperty(obj, key, {
    get() {
      // 收集依赖
      Dep.target && dep.addDep(Dep.target);
      return val;
    },
    set(newVal) {
      if (newVal !== val) {
        // 递归
        observe(newVal);
        val = newVal;
        // 发送通知
        dep.notify();
      }
    },
  });
}

// 生成vue响应式的observe函数
function observe(obj) {
  if (typeof obj !== "object" || obj === null) {
    return;
  }
  new Observer(obj);
}

// 生成vue响应式的Observer类
// Obeserver类对应的中文名称是什么 => Observer对应的中文名称是 观察者
// Obeserver和watcher的区别是什么 => Obeserver是一个类,用来实例化一个Observer对象,Observer对象中有一个value属性,value属性对应的值就是data中的数据;
// watcher是一个类,用来实例化一个watcher对象,watcher对象中有一个getter属性,getter属性对应的值是一个函数,函数中调用了data中的属性,从而触发属性的getter函数
class Observer {
  constructor(value) {
    this.value = value;
    // 判断value的类型
    if (Array.isArray(value)) {
      // todo
    } else {
      this.walk(value);
    }
  }
  // 遍历对象的每一个key,对每一个key调用defineReactive
  walk(obj) {
    Object.keys(obj).forEach((key) => {
      defineReactive(obj, key, obj[key]);
    });
  }
}

// 生成vue响应式的Dep类,用来收集依赖和发送通知
// Dep中文名称是什么 => Dep对应的中文名称是 【依赖收集器】
// 定义一个全局的target属性,用来存储当前的watcher
// Dep.target是一个全局的唯一的watcher,因此在使用的时候需要先保存之前的值,使用完之后再恢复
class Dep {
  static target = '';
  constructor() {
    this.deps = [];
  }
  addDep(dep) {
    this.deps.push(dep);
  }
  notify() {
    // 何时调用notify => 在属性的setter函数中调用
    this.deps.forEach((dep) => dep.update());
  }
}

// Dep.target在何时赋值 => 在Watcher的get方法中赋值

// 生成vue响应式的Watcher类,用来收集依赖和发送通知
// watcher是什么 => watcher是一个类,用来实例化一个watcher对象
// watcher有什么作用 => watcher的作用是在数据发生变化的时候,执行对应的更新函数
// watcher有哪些属性 => watcher有getter、options两个属性
// watcher有哪些方法 => watcher有get、update两个方法
// watcher对于的中文名称是什么 => 【Watcher】对应的中文名称是 观察者 或者 【订阅者】
// 发布者是谁 => 【发布者是Dep】

class Watcher {
  constructor(getter, options) {
    // getter是一个函数,函数中调用了data中的属性,从而触发属性的getter函数
    this.getter = getter;
    // options是一个对象,包含了watcher的一些配置,例如:immediate、deep、lazy等
    this.options = options;
    this.get();
  }
  get() {
    // 将Dep.target指向当前的Watcher实例
    // @ts-ignore
    // this是当前的watcher实例
    Dep.target = this;
    // 执行getter函数,触发属性的getter函数
    this.getter();
    // 将Dep.target置为null
    // @ts-ignore
    Dep.target = null;
  }
  update() {
    // if (this.options.sync) {
    //   this.run();
    // } else {
    //   queueWatcher(this);
    // }

  }
  run() {
    // const value = this.get();
    // if (value !== this.value || isObject(value) || this.deep) {
    //   const oldValue = this.value;
    //   this.value = value;
    //   if (this.user) {
    //     try {
    //       this.cb.call(this.vm, value, oldValue);
    //     } catch (e) {
    //       console.error(e, this.vm, `callback for watcher "${this.expression}"`);
    //     }
    //   } else {
    //     this.cb.call(this.vm, value, oldValue);
    //   }
    // }
  }

  queueWatcher(watcher) {
    // const id = watcher.id;
    // if (has[id] == null) {
    //   has[id] = true;
    //   if (!flushing) {
    //     queue.push(watcher);
    //   } else {
    //     // if already flushing, splice the watcher based on its id
    //     // if already past its id, it will be run next immediately.
    //     let i = queue.length - 1;
    //     while (i > index && queue[i].id > watcher.id) {
    //       i--;
    //     }
    //     queue.splice(i + 1, 0, watcher);
    //   }
    //   // queue the flush
    //   if (!waiting) {
    //     waiting = true;
    //     nextTick(flushSchedulerQueue);
    //   }
    // }
  }
}

// watcher在何时创建 => 在组件的mounted钩子函数中创建
// 在mounted中如何创建watcher => 通过调用render函数来创建watcher
// render函数在何时调用 => 在组件的render函数中调用 => new Watcher(getter, options)
// getter是一个函数,函数中调用了data中的属性,从而触发属性的getter函数
// getter具体长啥样:() => { this._update(this._render()) }


// render函数示例
// render函数中何时用到了watcher
// 在render函数中调用了h函数,h函数中调用了data中的属性,从而触发属性的getter函数
function render() {
  // 生成虚拟dom
  const vnode = h("div", { id: "app" }, [
    h("p", null, "hello world"),
    h("p", null, "hello world"),
    h("p", null, "hello world"),
  ]);
  // 将虚拟dom转换为真实dom
  const container = document.getElementById("app");
  patch(container, vnode);
}

// 生成vue的h函数,作用是生成虚拟dom
function h(tag, props, children) {
  return {
    tag,
    props,
    children,
  };
}

// 生成vue的patch函数,作用是将虚拟dom转换为真实dom
function patch(container, vnode) {
  const el = createElm(vnode);
  container.appendChild(el);
}

// 生成vue的createElm函数,作用是将虚拟dom转换为真实dom
function createElm(vnode) {
  const { tag, props, children } = vnode;
  // 创建元素
  const el = document.createElement(tag);
  // 处理属性
  if (props) {
    for (let key in props) {
      el.setAttribute(key, props[key]);
    }
  }
  // 处理子节点
  if (children) {
    if (typeof children === "string") {
      el.textContent = children;
    } else {
      children.forEach((child) => {
        patch(el, child);
      });
    }
  }
  return el;
}
posted @ 2023-03-27 17:20  脆皮鸡  阅读(236)  评论(0)    收藏  举报