自己动手实现一个mvvm库

来源:互联网 发布:淘宝女装货源 编辑:程序博客网 时间:2024/05/18 01:17

一、总体大纲

要实现一个我们自己的mvvm库,我们首先需要做的事情不是写代码,而是整理一下思路,捋清楚之后再动手绝对会让你事半功倍。先上流程图,我们对着流程图来捋思路

如上图所示,我们可以看到,整体实现分为四步

  1. 实现一个Observer,对数据进行劫持,通知数据的变化
  2. 实现一个Compile,对指令进行解析,初始化视图,并且订阅数据的变更,绑定好更新函数
  3. 实现一个Watcher,将其作为以上两者的一个中介点,在接收数据变更的同时,让Dep添加当前Watcher,并及时通知视图进行update
  4. 实现MVVM,整合以上三者,作为一个入口函数

二、动手时间

思路捋清楚了,接下来要做的事就是开始动手。

能动手的我决不动口

1、实现Observer

这里我们需要做的事情就是实现数据劫持,并将数据变更给传递下去。那么这里将会用到的方法就是Object.defineProperty()来做这么一件事。先不管三七二十一,咱先用用Object.defineProperty()试试手感。

function observe (data) {  if (!data || typeof data !== 'object') {    return;  }  Object.keys(data).forEach(key => {    observeProperty(data, key, data[key])  })}function observeProperty (obj, key, val) {  observe(val);  Object.defineProperty(obj, key, {    enumerable: true,   // 可枚举    configurable: true, // 可重新定义    get: function () {      return val;    },    set: function (newVal) {      if (val === newVal || (newVal !== newVal && val !== val)) {        return;      }      console.log('数据更新啦 ', val, '=>', newVal);      val = newVal;    }  });}

调用

var data = {  a: 'hello'}observe(data);

效果如下

看完是不是发现JavaScript提供给我们的Object.defineProperty()方法功能巨强大巨好用呢。

其实到这,我们已经算是完成了数据劫持,完整的Observer则需要将数据的变更传递给Dep实例,然后接下来的事情就丢给Dep去通知下面完成接下来的事情了,完整代码如下所示

/** * @class 发布类 Observer that are attached to each observed * @param {[type]} value [vm参数] */ function observe(value, asRootData) {   if (!value || typeof value !== 'object') {     return;   }   return new Observer(value); }function Observer(value) {  this.value = value;  this.walk(value);}Observer.prototype = {  walk: function (obj) {    let self = this;    Object.keys(obj).forEach(key => {      self.observeProperty(obj, key, obj[key]);    });  },  observeProperty: function (obj, key, val) {    let dep = new Dep();    let childOb = observe(val);    Object.defineProperty(obj, key, {      enumerable: true,      configurable: true,      get: function() {        if (Dep.target) {          dep.depend();        }        if (childOb) {          childOb.dep.depend();        }        return val;      },      set: function(newVal) {        if (val === newVal || (newVal !== newVal && val !== val)) {          return;        }        val = newVal;        // 监听子属性        childOb = observe(newVal);        // 通知数据变更        dep.notify();      }    })  }}/** * @class 依赖类 Dep */let uid = 0;function Dep() {  // dep id  this.id = uid++;  // array 存储Watcher  this.subs = [];}Dep.target = null;Dep.prototype = {  /**   * [添加订阅者]   * @param  {[Watcher]} sub [订阅者]   */  addSub: function (sub) {    this.subs.push(sub);  },  /**   * [移除订阅者]   * @param  {[Watcher]} sub [订阅者]   */  removeSub: function (sub) {    let index = this.subs.indexOf(sub);    if (index !== -1) {      this.subs.splice(index ,1);    }  },  // 通知数据变更  notify: function () {    this.subs.forEach(sub => {      // 执行sub的update更新函数      sub.update();    });  },  // add Watcher  depend: function () {    Dep.target.addDep(this);  }}// 结合Watcher/*** Watcher.prototype = {*   get: function () {*     Dep.target = this;*     let value = this.getter.call(this.vm, this.vm);*     Dep.target = null;*     return value;*   },*   addDep: function (dep) {*     dep.addSub(this);*   }* }*/

至此,我们已经实现了数据的劫持以及notify数据变化的功能了。

2、实现Compile

按理说我们应该紧接着实现Watcher,毕竟从上面代码看来,ObserverWatcher关联好多啊,但是,我们在捋思路的时候也应该知道了,WatcherCompile也是有一腿的哦。所以咱先把Compile也给实现了,这样才能更好的让他们3P。


我不是老司机,我只是一个纯洁的开电动车的孩子

原创粉丝点击