Vue双向绑定原理

  1. Vue双向绑定原理的时候,发现订阅器Dep是可以直接调用他自己的target的属性,但是class本质是一个函数,为此确认了一下
//为啥Dep类本质是个函数,可以直接调用属性?
    class HanClass{
     
      constructor(){
     
        this.name = 'xixixix'
      }
    }
    function HanFn(){
     
      this.name = 'gogogo'
    }
    console.log(HanFn.name);//函数也是可以直接调用自己的属性的,无需实例化!
    console.log(HanClass.name);

结论:函数是可以直接调用到自己本身的方法的

  1. 如何解释Vue的双向绑定原理?
双向绑定原理是利用发布订阅模式结合数据劫持来实现的,发布订阅模式是指我们有一个监听器observer,一个订阅器Dep,
一个订阅者watcher。
我们把data里的属性通过监听器observer全部绑定了数据劫持,数据劫持的get方法中判断是否要添加watcher到订阅器数组subs里面,也就是在我们初始化的data的时候,这个data的属性其实就是一个订阅者watcher,
set方法执行了订阅器的notify方法通知watcher,
他在初始化的时候construtor传入的vm为Vue实例,exp为这个data的属性,最后一句this.value = this.get()强制执行了get方法,
而此时get方法里Dep.target = this让订阅器的target为自己,然后将为自己添加到订阅器Dep的订阅者数组subs里,执行完之后让
Dep.target = null即可

源码:

HTML:

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

<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>

<body>
  <h1 id="name"></h1>
  <input type="text">
  <input type="button" value="改变data内容" onclick="changeInput()">

  <script src="./observer.js"></script>
  <script src="./watcher.js"></script>
  <script>
    function myVue(data, el, exp) {
     
      console.log('进入了myVue......');
      this.data = data;
      observable(data); //将数据变的可观测
      el.innerHTML = this.data[exp]; // 初始化模板数据的值
      new Watcher(this, exp, function (value) {
     //在这个函数里只执行一次,但是此时已经添加到subs数组里了,里面的这个回调函数非常关键!!!因为是引用类型,所以notify每次去updata的时候,其实也执行了这段代码,所以起到了修改innerHTML的作用!!!
        el.innerHTML = value;
        console.log('进入了Watcher类.....');
      });
      console.log('进入了myVue函数.....');
      return this;
    }

    var ele = document.querySelector('#name');
    var input = document.querySelector('input');

    var myVue = new myVue({
     
      name: 'hello world'
    }, ele, 'name');

    //改变输入框内容
    input.oninput = function (e) {
     
      myVue.data.name = e.target.value
    }
    //改变data内容
    function changeInput() {
     
      myVue.data.name = "难凉热血"
    }
  </script>
</body>

</html>

observer.js:

//observer
  /**
	 * 把一个对象的每一项都转化成可观测对象
	 * @param { Object } obj 对象
	 */
	function observable (obj) {
     
		if (!obj || typeof obj !== 'object') {
     
        	return;
    	}
		let keys = Object.keys(obj);
		keys.forEach((key) =>{
     
			defineReactive(obj,key,obj[key])
		})
		return obj;
	}
	/**
	 * 使一个对象转化成可观测对象
	 * @param { Object } obj 对象
	 * @param { String } key 对象的key
	 * @param { Any } val 对象的某个key的值
	 */
	function defineReactive (obj,key,val) {
     
    let dep = new Dep();
		Object.defineProperty(obj, key, {
     
			get(){
     
        console.log('Dep.target。。。。。。', Dep.target);
				dep.depend();
				console.log(`${
       key}属性被读取了`);
				return val;
			},
			set(newVal){
     
				val = newVal;
				console.log(`${
       key}属性被修改了`);
				dep.notify()                    //数据变化通知所有订阅者
			}
		})
	}
	class Dep {
     
		
		constructor(){
     
			this.subs = []
		}
		//增加订阅者
		addSub(sub){
     
			this.subs.push(sub);
		}
        //判断是否增加订阅者
		depend () {
     
		    if (Dep.target) {
     
		     	this.addSub(Dep.target)
		    }
		}

		//通知订阅者更新
		notify(){
     
			this.subs.forEach((sub) =>{
     
        console.log('此时的sub....',sub);
				sub.update()
			})
		}
		
	}
	// Dep.target = null;

watcher.js

//watcher
class Watcher {
     
  constructor(vm, exp, cb) {
     
    this.vm = vm;
    this.exp = exp;
    this.cb = cb;
    this.value = this.get(); // 将自己添加到订阅器的操作
  }
  get() {
     
    Dep.target = this; // 缓存自己
    let value = this.vm.data[this.exp] // 强制执行监听器里的get函数
    Dep.target = null; // 释放自己
    return value;
  }
  update() {
     
    let value = this.vm.data[this.exp];
    let oldVal = this.value;
    if (value !== oldVal) {
     
      console.log('sub数组的订阅者Watcher实例的updata方法......');
      this.value = value;
      this.cb.call(this.vm, value, oldVal);
    }
  }
}

你可能感兴趣的:(脚踏实地)