阅读 239

mvvm模式探索

什么是mvvm

mvvm是一种架构模式,它由三部分构成,其中m代表Model(数据层),v代表View(视图层),vm代表ViewModel(View和Model中间的桥梁)。当数据层的数据发生改变时,视图层也会随之发生变化。

对于我们前端来说,我们不用关心dom内部是如何增删改查,这些都由我们的框架来完成,ViewModel首先对数据有个监听的行为,当数据发生改变时,由ViewModel通知视图层要更新了。

mvvm实现思路

对于上面的流程是这样的:

  • 首先对数据层的数据进行劫持,监听每一个属性,每个属性有一个管理观察者的容器Dep。
  • 当视图层某个节点引用某个属性时,这个节点就会创建一个与之对应的观察者(Watcher),然后把观察者添加到属性的Dep中,这样两者就建立了一种联系。
  • 当数据层的属性发生变化时,与之对应的Dep就会通知节点(观察者)要更新了。

这样就实现了一个完成的mvvm的流程。

数据劫持

利用js的Object.defineProperty对数据的每一个属性进行监听。

// 判断是否是undefined
function isUndefined(data) {
  return data === undefined;
}
// 判断是否是Null
function isNull(data) {
  return data === null;
}
// 判读是否是number string boolean基本数据类型
function isBaseType(data) {
  const type = typeof data;
  const baseType = ["string", "number", "boolean"];
  return (
    baseType.findIndex((v) => v === type) > -1 ||
    isUndefined(data) ||
    isNull(data)
  );
}
// 判断是否是数组类型
function isArray(data) {
  return data instanceof Array;
}

function observer(data) {
  if (isBaseType(data) || isArray(data)) return data;
  Object.keys(data).forEach((key) => {
    defineReactive(data, key, data[key]);
  });
}
function defineReactive(data, key, value) {
  observer(value);
  const dep = new Dep(); // 管理观察者的一个容器
  Object.defineProperty(data, key, {
    get() {
      console.log("有人使用我");
      Dep.target && dep.addDep(Dep.target); // 收集观察者
      return value;
    },
    set(newValue) {
      if (newValue === value) return null;
      value = newValue;
      dep.notify(); // 通知观察者更新
      console.log("数据发生变化");
    },
  });
}
复制代码

谁来管理观察者

// Dep:用来管理Watcher
class Dep {
  constructor() {
    // 这里存放若干依赖(watcher)
    this.deps = [];
  }

  addDep(dep) {
    this.deps.push(dep);
  }

  notify() {
    this.deps.forEach((dep) => dep.update());
  }
}
复制代码

假设现在有个节点引用了某个属性

一个html由众多的节点组成。我现在假设有一个节点,引用了data的name属性

class Node {
  constructor(key) {
    this.value = data[key];
    new Watcher(data, key, (value) => {
      this.update(value);
    });
  }
  update(value) {
    console.log("data数据更新了,我也要更新");
    this.value = value;
  }
}
// Watcher
class Watcher {
  constructor(data, key, cb) {
      this.data = data;
      this.key = key;
      this.cb = cb;

    // 将当前watcher实例指定到Dep静态属性target
    Dep.target = this;
    this.vm[this.key]; // 触发getter,添加依赖
    Dep.target = null;
  }

  update() {
    // console.log("属性更新了");
    this.cb.call(this.data, this.data[this.key]);
  }
}
复制代码

在上面的代码中,Node的构造方法中引用了data的name属性,这时会创建一个观察者(watcher),并且添加到管理观察者的容器(dep)中。

数据层的属性发生变化时

当数据发生变化时,dep.notify()会通知所用观察者更新。

完整代码如下:

const data = {
  name: "奥特曼",
  info: {
    sex: "man",
  },
};
// 判断是否是undefined
function isUndefined(data) {
  return data === undefined;
}
// 判断是否是Null
function isNull(data) {
  return data === null;
}
// 判读是否是number string boolean基本数据类型
function isBaseType(data) {
  const type = typeof data;
  const baseType = ["string", "number", "boolean"];
  return (
    baseType.findIndex((v) => v === type) > -1 ||
    isUndefined(data) ||
    isNull(data)
  );
}
// 判断是否是数组类型
function isArray(data) {
  return data instanceof Array;
}

function observer(data) {
  if (isBaseType(data) || isArray(data)) return data;
  Object.keys(data).forEach((key) => {
    defineReactive(data, key, data[key]);
  });
}
function defineReactive(data, key, value) {
  observer(value);
  const dep = new Dep();
  Object.defineProperty(data, key, {
    get() {
      console.log("有人使用我");
      Dep.target && dep.addDep(Dep.target);
      return value;
    },
    set(newValue) {
      if (newValue === value) return null;
      value = newValue;
      dep.notify();
      console.log("数据发生变化");
    },
  });
}
class Node {
  constructor(key) {
    this.value = data[key];
    new Watcher(data, key, (value) => {
      this.update(value);
    });
  }
  update(value) {
    console.log("data数据更新了,我也要更新");
    this.value = value;
  }
}
// Watcher
class Watcher {
  constructor(data, key, cb) {
    this.data = data;
    this.key = key;
    this.cb = cb;
    // 将当前watcher实例指定到Dep静态属性target
    Dep.target = this;
    this.data[this.key]; // 触发getter,添加依赖
    Dep.target = null;
  }

  update() {
    // console.log("属性更新了");
    this.cb.call(this.data, this.data[this.key]);
  }
}
// Dep:用来管理Watcher
class Dep {
  constructor() {
    // 这里存放若干依赖(watcher)
    this.deps = [];
  }

  addDep(dep) {
    this.deps.push(dep);
  }

  notify() {
    this.deps.forEach((dep) => dep.update());
  }
}
observer(data);
const node = new Node("name");
console.log(node, "跟新之前");
data.name = "迪迦奥特蛮";
console.log(node, "更新之后");

复制代码
文章分类
前端
文章标签