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


相关推荐

  • ACM计算几何篇_acm数学

    ACM计算几何篇_acm数学1前言1.1计算几何算法1.2计算几何题目特点及要领1.3预备知识2凸包2.1定义2.1.1凸多边形2.1.2凸包2.2颜料配色问题2.2.1问题描述2.2.2问题简化2.2.3问题抽象2.2.4数学抽象2.2.4.1ConvexCombinationAndAffineCombination2.2.4.2区别与联系…

    2022年10月23日
    0
  • Java Integer 与 int 深刻理解

    Java Integer 与 int 深刻理解Java Integer 与 int 深刻理解

    2022年4月24日
    43
  • cv2 imread()函数[通俗易懂]

    Reason    这两天学习OpenCV-Python时,也就是cv2库,读取图像时不时出现和预料之外的结果。于是作者从源头来考究一下cv2.imread(filename,flags)Result这里参考文章cv2.imread(filename,flags)cv2.imread(filename,flags)参数:filepath:读入imge的完整路径flags:标志位,{cv2.IMREAD_COLOR,cv2.IMREAD_GRAYSC

    2022年4月14日
    59
  • spel表达式的用法_el表达式判断是否为空

    spel表达式的用法_el表达式判断是否为空spel表达式运算使用翻看公司代码,这一块不是很懂,查资料,记一下,还是太菜1.常用的对象Expression:表达式对象SpelExpressionParser:表达式解析器EvaluationContext:上下文2.使用本文参考了下面的几篇文章https://www.cnblogs.com/shihuc/p/9338173.htmlhttps://blog.csdn.net/f641385712/article/details/90812967下面的例子主要是来源于第一

    2022年9月4日
    2
  • dpkg命令的用法

    dpkg命令的用法dpkg命令的用法dpkg是Debianpackage的简写,为”Debian“操作系统专门开发的套件管理系统,用于软件的安装,更新和移除。所有源自"Debian"的Linux的发行版都使用dpkg,  例如"Ubuntu" 阅读目录 安装软件列出与该包先关联的文件显示包的版本移除软件(保留配置)移除软件(不保留配置)查找包的详细信息列出deb包的内容 安装软件命令:dpkg-i&…

    2022年5月11日
    69
  • Response.ContentType详细说明

    Response.ContentType详细说明不同的ContentType会影响客户端所看到的效果.默认的ContentType为text/html也就是网页格式.代码如:显示的为网页,而则会显示html原代码.以下为一些常用的ContentTypeGIFimagesJPEGimagesTIFFimagesMICR

    2022年7月19日
    40

发表回复

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

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