Vue双向绑定原理MVVM及实现

Vue内部通过Object.defineProperty方法属性拦截的方式,把data对象里每个数据的读写转化成getter/setter,当数据变化时通知视图更新。
所谓MVVM数据双向绑定,即主要是:数据变化更新视图,视图变化更新数据。

我的效果图:

input 里面输入或删除,会同步到h1中
在这里插入图片描述

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <script src="know.js"></script>
  <title>Document</title>
</head>

<body>
  <h1 id="text"></h1>
  <input type="text">
  <script>
    text = document.querySelector("#text");
    input = document.querySelector("input");
//  箭头函数 没有 没有prototype(构造函数),故不能 new ,所以 用 function 了 
    function myVue(data, key, el) {
      this.data = data;
      // 对data中的每个属性都定义 get,set 方法,
      //一旦 data中的属性值发生变化,get、set就能捕捉到,
      // 这也就实时监听 data 的变化了,所以能够双向绑定 
      // 将数据变的可观测
      observable(data);
      el.innerHTML = this.data[key];
      new Watcher(this, key, (val) => {
        el.innerHTML = val;
      });
      return this;
    };
    var myVue = new myVue({
      name: 'HEllo!'
    }, 'name', text);
    input.oninput = (e) => {
    // 触发 set 方法 
    // M 数据发生 变化 ,V 视图同步改变: M---> V
      myVue.data.name = e.target.value;
    }
  </script>

</body>

</html>

know.js

observable = (obj) => {
  if (!obj || typeof obj !== 'object') {
    return;
  }
  // 得到 字典obj的所有 key
  keys = Object.keys(obj);
  keys.forEach(key => {
  //Object.defineProperty方法: 数据的每次读和写能够被我们看的见,
  //即我们能够知道数据什么时候被读取了或数据什么时候被改写了,
  // 我们将其称为数据变的‘可观测’
    defineReactive(obj,key,obj[key])
  })
}
defineReactive = (obj, key, value) => {
  dev = new Dev();
  Object.defineProperty(obj, key, {
    get() {
    // 属性对象(Dev.target)不为 null,即添加订阅者 Watcher
      dev.addWatcher();
      // 实时看到 读取值
      console.log(`${key} 被读取了!: ${value}`);
      return value;
    },
    set(newVal) {
      value = newVal;
      console.log(`${key} 被修改了!:新值:${newVal}`)
      // oninput事件修改了data的话,就会通知data中的所有属性(Watcher)更新
      dev.notify();
    }
  })

}
// 消息订阅器Dev 来专门收集这些订阅者(Watcher)
class Dev {
  constructor() {
  // 页面中 所有的属性对象,有update方法
    this.subs = []
  };
  addWatcher() {
    if (Dev.target) {
      this.subs.push(Dev.target);
    }
  };
  notify() {
    this.subs.forEach(sub => {
      sub.update();
    })
  }

}
class Watcher {
  constructor(myVue, key, callback) {
    this.myVue = myVue;
    this.key = key;
    this.callback = callback;
    // 存放以前的value
    this.value = this.get();

  };
  
  get() {
    Dev.target = this;
     // this.myVue.data[this.key]: 触发 get方法: 
     // 消息订阅器Dev添加当前的class Watcher(Dev.target)
    var value = this.myVue.data[this.key];
    Dev.target = null;
    return value;
  };
  update() {
  // 这就是最新的value
 // this.myVue.data[this.key]: 触发 get方法 
    var newVal = this.myVue.data[this.key];
    if (this.value != newVal) {
      this.value = newVal;
      this.callback(newVal);
    }
  }
}
Dev.target = null;

总结一下:实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dev来专门收集这些订阅者,旨在监听器Observer和订阅者Watcher之间进行统一管理。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值