vue 的双向绑定原理「建议收藏」

vue 的双向绑定原理「建议收藏」vue采用“数据劫持”和“观察者模式(又叫做发布者-订阅者模式)”相结合的方式,通过Object.defineProperty()来劫持各个属性的setter、getter,在数据变动时发布消息给订阅者,触发相应的监听回调。vue的双向绑定原理,分三步:第一步,“数据劫持”:vue用Object.defineProperty()方法实现数据劫持,为每个属性分配一个订阅者集合的管理数组dep; 第二步,“添加观察者”:在编译的时候在该属性的数组dep中添加订阅者,添加方式包括:v

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

目录

一、一句话描述 vue 的双向绑定原理

二、细说 vue 的双向绑定原理

1、vue 2.x 的双向绑定

2、vue 3.x 的双向绑定

3、一个完整的案例


一、一句话描述 vue 的双向绑定原理(vue 的响应式原理)

vue 在实例化的时候,使用 Object.definePropery() 方法Proxy 构造函数,对 data 进行 getter 和 setter 的处理。在组件渲染时,若用到 data 里的某个数据,这个数据就会被依赖收集进 watcher 里。当数据更新,如果这个数据在 watcher 里,就会收到通知并更新,否则不会更新——vue 采用“数据劫持”+“观察者模式(发布者-订阅者模式)”相结合的方式实现了双向绑定——vue 的响应式原理。

【拓展】

“数据劫持”+“观察者模式(发布者-订阅者模式)”:通过 Object.defineProperty() 方法(Vue 2.x)或 ES6 的 Proxy 构造函数(Vue 3.x)来劫持各个属性的 setter、getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

 

二、细说 vue 的双向绑定原理(vue 的响应式原理)

  • 第一步,“数据劫持”:vue 2.x 用 Object.defineProperty() 方法来实现数据劫持,为每个属性分配一个订阅者集合的管理数组 dep;vue 3.x 用 ES6 的 Proxy 构造函数来实现数据劫持。
  • 第二步,“添加订阅者”:在编译的时候在该属性的数组 dep 中添加订阅者,添加方式包括:v-model 会添加一个订阅者,{
    {}} 也会,v-bind 也会,只要用到该属性的指令理论上都会。
  • 第三步,“为 input 添加监听事件”:为 input 添加监听事件,修改值就会为该属性赋值,触发该属性的 set() 方法,在 set() 方法内通知订阅者数组 dep,订阅者数组循环调用各订阅者的 update() 方法更新视图。

1、vue 2.x 的双向绑定

–> 发布者-订阅者模式:

// 订阅者(观察者)
let uid = 0;
class Dep {
  constructor(){
    this.id = uid++;
    this.subs = [];
  }
  addSub(sub){
    this.subs.push(sub);
  }
  removeSub(sub){
    const arr = this.subs,
          item = sub;
    if(arr.length){
      const index = arr.indexOf(item);
      if(index > -1){
        return arr.splice(index, 1);
      }
    }
  }
  depend(){
    if(window.target){
      window.target.addDep(this);
    }
  }
  notify(){
    const subs = this.subs.slice();
    for(let i = 0, len = subs.lengths; i < len; i++){
      subs[i].update();
    }
  }
}

// 发布者(被观察者)(不考虑深度监听)
class Watcher {
  constructor(vm, expOrFn, cb) {
    this.vm = vm;
    this.deps = [];
    this.depIds = new Set();
    this.getter = expOrFn;
    this.cb = cb;
    this.value = this.get();
  }
  get() {
    window.target = this;
    var value = this.getter.call(this.vm, this.vm);
    window.target = undefined;
    return value;
  }
  addDep() {
    const id = dep.id;
    if(!this.depIds.has(id)){
      this.depIds.add(id);
      this.deps.push(dep);
      dep.addSub(this);
    }
  }
  update() {
    console.log("更新, value:", this.value);
  }
}

/* “发布者-订阅者模式”的使用示例 */
// 创建 发布者 实例
var watcher = new Watcher({ x: 1 }, (val) => val);
watcher.get();

// 创建 订阅者 实例
var dep = new Dep();

// 订阅者 监听 发布者 对象
dep.depend();
dep.notify();

–> 数据劫持: 

// 数据劫持
function defineReactive(obj, key, val){
  let dep = new Dep();
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter() {
      dep.depend();
      return value;
    },
    set: function reactiveSetter(newVal) {
      if(val === newVal){
         return;
      }
      val = newVal;
      dep.notify();
    }
  });
}

2、vue 3.x 的双向绑定

vue 3.x 的双向绑定与 vue 2.x 的双向绑定,都采用 发布者-订阅者模式,不同的是 数据劫持 的实现,vue 3.x 采用的是 ES6 的 Proxy 构造函数实现的。

Proxy(data, {
  get(target, key) {
    return target[key];
  },
  set(target, key, value) {
    let val = Reflect.set(target, key, value);
      _that.$dep[key].forEach(item => item.update());
    return val;
  }
})

3、一个完整的案例

<body>
	<div id="demo"></div>
	<input type="text" id="inp">
</body>
<script type="text/javascript">
	var obj = {};
	var demo = document.querySelector('#demo')
	var inp = document.querySelector('#inp')
	Object.defineProperty(obj, 'name', {
		get: function() {
			return val;
		},
		set: function(newVal) { //当该属性被赋值的时候触发
			inp.value = newVal;
			demo.innerHTML = newVal;
		}
	})
	inp.addEventListener('input', function(e) {
		// 给obj的name属性赋值,进而触发该属性的set方法
		obj.name = e.target.value;
	});
	obj.name = 'fei'; //在给obj设置name属性的时候,触发了set这个方法
</script>

 

 

 

参考文件:

使用Proxy实现Vue数据劫持:https://zhuanlan.zhihu.com/p/50547367

用一句话说明 Vuex工作原理:https://zhuanlan.zhihu.com/p/106838529

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/183903.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号