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

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 我的第一次WebService接口开发

    我的第一次WebService接口开发前言最近项目上需要对接WebService接口,之前从来没有用过,这次都遇见了。记录下基础的使用和我遇见的问题。正文概述WebService接口百度一搜,各个介绍的都非常详细,由于刚开始没接触,看的也不是很懂。首先记住一句话:WebService是一种跨编程语言和跨操作系统平台的远程调用技术。跨编程语言和跨操作系统平台:也就是说Asp.net开发的WebService我用java代码调用…

  • matlab自带的插值函数interp1的几种插值方法[通俗易懂]

    matlab自带的插值函数interp1的几种插值方法[通俗易懂]插值法    插值法又称“内插法”,是利用函数f(x)在某区间中已知的若干点的函数值,作出适当的特定函数,在区间的其他点上用这特定函数的值作为函数f(x)的近似值,这种方法称为插值法。如果这特定函数是多项式,就称它为插值多项式。线性插值法    线性插值法是指使用连接两个已知量的直线来确定在这两个已知量之间的一个未知量的值的方法。    假设我们已知坐标(x0,y0)与…

  • 2019 Python最新面试题及答案16道题

    在Python相关的工作岗位面试中,基础语法是必考的一个部分,它考察求职者对Python语言的最基本掌握情况;其次是数据库相关的问题,如查询,修改,插入数据等,数据库所占的比重也很大,不容小觑。

  • c++将十进制数转化为二进制数_十六进制小数转二进制

    c++将十进制数转化为二进制数_十六进制小数转二进制#include"stdafx.h"#include&lt;iostream&gt;#include&lt;math.h&gt;usingnamespacestd;voidchange(doublex);voidchange(doublex){ doublen=0;…

  • Tomcat server missing

    Tomcat server missingTomcat server missing

  • python爬虫库_python爬虫实战百度云盘

    python爬虫库_python爬虫实战百度云盘如何使用爬虫与JieBa库制作词云所需库的安装所需第三方库为如下:importrequestsfrombs4importBeautifulSoupfromwordcloudimportWordCloudimportmatplotlib.pyplotaspltimportjiebaimportnumpyasnpfromPILimportImage此网址内含大量python第三方库下载安装即可:链接:https://www.lfd.uci.edu/~g

发表回复

您的电子邮箱地址不会被公开。

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