Skip to content

miniVue

约 682 字大约 2 分钟

2024-07-28

Vue 构造函数编写

class Vue {
  constructor(options) {
    this.$options = options;
    this.$el = document.querySelector(options.el);
    this.$data = options.data;
    this.$node = options.node;
    this.init();
    return this;
  }
  init() {
    proxyData(this.$data);
    defineReactive(this.$data, this);
    const watcher = new Watcher();
    render2Dom(this.$node, this.$el, this);
  }
}

function proxyData(vm, key) {
  Object.defineProperty(vm, key, {
    get() {
      return vm.$data[key];
    },
    set(newVal) {
      vm.$data[key] = newVal;
    },
  });
}

// 将数据变成响应式
function defineReactive(data, vm) {
  const keys = Object.keys(data);
  keys.forEach((key) => {
    proxyData(vm, key);
    const property = Object.getOwnPropertyDescriptor(data, key);
    const setter = property && property.set;
    const getter = property && property.get;
    let val;
    if (!getter || setter) {
      val = data[key];
    }
    const dep = new Dep();
    Object.defineProperty(data, key, {
      get() {
        const value = getter ? getter.call(obj) : val;
        if (Dep.target) {
          dep.depend();
        }
        return value;
      },
      set(newVal) {
        const value = getter ? getter.call(obj) : val;
        if (newVal === value || (newVal !== newVal && value !== value)) {
          return;
        }
        if (getter && !setter) return;
        if (setter) {
          setter.call(data, newVal);
        } else {
          val = newVal;
        }
        dep.notify(vm);
      },
    });
  });
}

function render2Dom(node, parent, vm) {
  const fragment = document.createDocumentFragment();
  const curEl = document.createElement(node.tag);
  if (node.style) {
    node.style.forEach((item) => {
      curEl.style[item.key] = item.value;
    });
  }
  if (node.textNode) {
    let textNode;
    if (typeof node.textNode === "string") {
      textNode = document.createTextNode(node.textNode);
    } else {
      textNode = document.createTextNode(vm.$data[node.textNode.value]);
    }
    curEl.appendChild(textNode);
  }
  fragment.appendChild(curEl);
  if (node.childrenEl) {
    node.childrenEl.forEach((item) => {
      render2Dom(item, curEl, vm);
    });
  }
  parent.appendChild(fragment);
}

class Watcher {
  constructor() {
    Dep.target = this;
    this.deps = []; // watcher
  }
  addDep(dep) {
    this.deps.push(dep);
    dep.addSub(this);
  }
  update(vm) {
    vm.$el.innerHTML = "";
    render2Dom(vm.$node, vm.$el, vm);
  }
}

//  watcher 里收集 dep(sub目标),  dep里收集订阅者(watcher)
class Dep {
  static target; // watcher
  constructor() {
    this.subs = [];
  }
  depend() {
    if (Dep.target) {
      Dep.target.addDep(this);
    }
  }
  notify(vm) {
    this.subs.forEach((item) => {
      item.update(vm);
    });
  }
  addSub(watcher) {
    this.subs.push(watcher);
  }
}

demo 使用

mini-vue

javascript模块中,调用mini-vue的代码是从132行起,前面的是定义mini-vue

© 2024 图图 📧 email