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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • c++的字符串数组string[通俗易懂]

    c++的字符串数组string[通俗易懂]用字符数组来存放字符串并不是最理想和最安全的方法。C++提供了一种新的数据类型——字符串类型(string类型),在使用方法上,它和char、int类型一样,可以用来定义变量,这就是字符串变量——用一个名字代表一个字符序列。实际上,string并不是C++语言本身具有的基本类型,它是在C++标准库中声明的一个字符串类,用这种类可以定义对象。每一个字符串变量都是string类的一个对象。一、字符…

    2022年10月31日
  • Python 生成随机数_python 随机字符串

    Python 生成随机数_python 随机字符串python中生成随机数主要使用random模块和numpy库中的random函数。前者主要用于生成随机数和实现序列中随机数和随机数列的挑选,后者用来生成a*b维的随机矩阵。一、random模块random模块中将近有7个函数都是可以用来生成随机数,列举如下:1.random.random()功能:随机生成一个[0,1)的浮点数示例:importrandomnumber=random.random()print(number)print(‘%.2f’%num

    2022年9月28日
  • 用基尔霍夫定律求解电路时_基尔霍夫定律总结

    用基尔霍夫定律求解电路时_基尔霍夫定律总结1.先了解几个名词(1)支路一个二端原件视为一条支路–图中6个二端原件所以有6条支路。(2)结点两条或以上的支路连接的点。d,e可以看做一个结点。(3).回路(4).网孔1,22,

    2022年8月1日
  • hdfs常用操作命令

    hdfs常用操作命令hdfs常用操作命令

    2022年4月23日
  • 基本粒子群算法小结及算法实例(附Matlab代码)

    基本粒子群算法小结及算法实例(附Matlab代码)1、基本粒子群算法假设在一个D维的目标搜索空间中,有N个粒子组成一个群落,其中第i个粒子表示为一个D维的向量:$$X_i=(x_{i1},x_{i2},\cdots,x_{iD}),\quadi=1,2,\cdots,N\quad\text{(1)}$$第i个粒子的“飞行”速度也是一个D维的向量,记为:$$V_i=(v_{i1},v_{i2},\cdots,v_{iD}),\quadi=1,2,\cdots,N\quad\te…

    2022年5月29日
  • 博客园h1h2h3笔记和文章写作规范

    博客园h1h2h3笔记和文章写作规范为什么要整理?2016-06-16之前写的大多数文章使用的标题都是h3,h4,而并不是h1,h2,h3。在为文章生成目录后有以下问题:1.文章大纲不清晰;2.由于h2h3没有规划好,生成出来

    2022年8月5日

发表回复

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

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