vue2的数据数据双向绑定

news2024/10/6 16:19:08

遍历data里面的属性,通过Object.defineProperty的set方法监听属性值的变化,最后通知视图更新

先问自己两个问题: 
    1.app.message修改数据的时候,Vue内部是如何监听message数据发生改变的   
         使用Object.defineProperty ->监听对象属性的改变
    2.当数据发生改变后,Vue是如何知道 通知哪些 '人',界面发生刷新呢  (张三,李四,王五
         发布订阅者模式
        
<div id='app'>
    {{ message }} //张三 
    {{ message }} //李四
    {{ message }} //王五  这里假设他们分别对应这三个message
         
    {{ name }}
</div>

<script src='./node_modules/vue/dist/vue.js'></script>
<script>
    const app = new Vue({
        el:'#app',
        data:{//注意!这个是对象
            message:'哈哈哈',
            name:'kobe '
        }
    })
</script>

  第一步肯定是创建Vue实例。  注意这里的data是 !对象

可以理解为我们把  这个obj对象传入Vue,Vue内部拿到的就是一个obj对象(data对象)

const obj = {
   message:'哈哈哈',
   name:'kobe '
}

拿到这个对象后,Vue先用Object.keys(obj)拿到一个包含obj对象的所有属性(message,name)的数组,然后进行forEach遍历,拿到每一个属性对应的value值

Object.keys(obj).forEach(key => {
        let value = obj[key]

再进行‘数据劫持’       Object.defineProperty字面意思就是   给  obj对象重新 定义 属性。因为obj对象内的属性不容易监听

 Object.defineProperty(obj, key, {
              set(newValue){
                  console.log('监听' + key + '改变' ) !!!注意这里的监听
                       value = newValue
                  },
              get(){
                      console.log('获取'+ key +'对应的值')
                     return value
                 }
             })
     }) 
          
    此时我们在控制台给message重新赋值   
    app.message = '老詹'   
    就会触发set方法,打印出:
       '监听message改变' 
      '老詹' 
    直接app.message则触发get

当我们设置或者访问对象的属性的时候,都会触发相应的函数,然后在这个函数里进行打印/返回/或者设置属性的值

既然如此,我们当然可以在触发函数的时候动一些手脚做点我们自己想做的事情,这也就是“劫持”操作。

------------------------------------------------------------------------------------------------------------

既然set内监听到数据发生了变化

set(newValue){
               console.log('监听' + key + '改变' ) !!!注意这里的监听
                    value = newValue
               },

那么监听到值改变后,告诉谁?谁在用呢?(记不记得一开始的 张三,李四,王五。让我们进行‘拟人’,更好理解)

谁在用其实是解析HTML代码,获取到哪些人有用到我们的属性

{{ message }} //张三 
 {{ message }} //李四
 {{ message }} //王五 

哎!!获取!获取!那么它肯定会调用一次message的get,那我就知道是张三,李四,王五你们在用这个message属性
那么到时候!一旦message属性的值发生变化set,那我再去通知你们三个。   
---即发布者订阅者模式
class Dep{  //Dep   即 Depdency 依赖  存储所有对我这个属性有依赖的
    constructor(){
        this.subs = [] //用来记录现在是谁要订阅我们的属性的   subs 即subscribe订阅            
    }      
}

const dep = new Dep()  //这个Dep对象就可以用subs这个数组去记录所有的订阅者   (就是刚刚的张三,李四,王五啊)

那我怎么知道所有的订阅者在哪里呢  ,定义一个addSub方法,之后往里面传入sub形参,代表即将要传入进来的订阅者

class Dep {
        constructor(){
            // 这个数组是用来记录现在是谁要订阅我们的属性的 
            this.subs = []
        }

        addSub(){  //定义一个addSub方法,之后往里面传入sub形参,代表即将要传入进来的订阅者  为了拿到订阅者,我们得再创建一个类  class Watcher
    
        }
}
为了拿到订阅者,我们得再创建一个类  class Watcher
class Dep {
        constructor(){
            // 这个数组是用来记录现在是谁要订阅我们的属性的 
            this.subs = []
        }

        addSub(){
         
        }
    }

    // 监听观察
    class Watcher{//订阅者
        constructor(name){
            this.name = name;
        }

        update(){
            console.log(this.name + '发送update') //update  你细想,通知到张三,李四的时候,是不是需要他们自己更新一下,把界面更新一下
        }
    }
到时候我们创建一个watcher 实例,就可以实例化出来张三 w1对象  李四w2  对象,  !!就可以把这些w1,w2实例对象放进dep实例的addSub内
   addSub(watcher){               !形参
    this.subs.push(watcher)
   }

之后,谁用message属性了,我们就赶紧创建一个w1shiliduix

const w1  = new Watcher('张三') //意味着张三使用了一次

就把w1传进  dep.addSub(w1)

const obj = {
        message:'哈哈哈',
        name:'kobe '
    }

    Object.keys(obj).forEach(key => {
        let value = obj[key]

        Object.defineProperty(obj, key, {
            set(newValue){
                console.log('监听' + key + '改变' )
                    // 监听到值改变后告诉谁?谁在用呢?
                    // 解析HTML代码,获取到哪些人有用我们的属性  (获取一次--谁用-谁就调用一次get)
                    value = newValue

                    // dep.notify()//通知  !!!如果有一天,值发生改变了,我们拿到这个dep实例对象,调用notify
                },
                get(){
                    console.log('获取'+ key +'对应的值')
                    // 张三 get ->通知到就需要自己 update一下
                    // 李四 get -> update
                    // 王五 get -> update

                    return value
                }
            })
    })

    // 发布订阅者模式   Dependency  subscribe订阅
    class Dep {//发布者
        // 存储所有对我这个属性有依赖的
        constructor(){
            // 这个数组是用来记录现在是谁要订阅我们的属性的 
            this.subs = []
        }

        addSub(watcher){
            this.subs.push(watche r)
        }

    再定义一个notify方法
        notify(){
            this.subs.forEach( item => {
                item.update()             //拿到我们的subs,遍历找到里面所有的订阅者,让他去调用自己的update
            })
        } 
    }

    // 监听观察
    class Watcher{//订阅者
        constructor(name){
            this.name = name;
        }

        update(){
            console.log(this.name + '发送update')
        }
    }

    const dep = new Dep()

    const w1 = new Watcher('张三')
    dep.addSub(w1)

    const w2 = new Watcher('李四')
    dep.addSub(w2)

    const w3 = new Watcher('王五')
    dep.addSub(w3)

以下是copy来的,更为干练

数据双向绑定作为 Vue 核心功能之一,Vue 则采用的是数据劫持与发布订阅相结合的方式实现双向绑定。

其中数据劫持是利用了 Object.defineProperty() 方法重新定义了对象获取属性值get和设置属性值set的操作来实现的;

劫持了数据之后,我们就需要一个监听器 Observer 来监听属性的变化。得知属性发生变化之后我们需要一个 Watcher 订阅者来更新视图,我们还需要一个 compile 指令解析器,用于解析我们的节点元素的指令与初始化视图。

  • Observer 监听器:用来监听属性的变化通知订阅者
  • Watcher 订阅者:收到属性的变化,然后更新视图(这个过程中我们可能会有很多个订阅者 Watcher 所以我们要创建一个容器 Dep 去做一个统一的管理)
  • Compile 解析器:解析指令,初始化模版,绑定订阅者

​ 当我们访问或设置对象的属性的时候,都会触发相对应的函数,然后在这个函数里返回或设置属性的值。既然如此,我们当然可以在触发函数的时候动一些手脚做点我们自己想做的事情,这也就是“劫持”操作。

​ 在Vue中其实就是通过Object.defineProperty来劫持对象属性的settergetter操作,并“种下”一个监听器,当数据发生变化的时候发出通知。

注意: 该方法每次只能设置一个属性,那么就需要遍历对象来完成其属性的配置:

Object.keys(student).forEach(key => defineReactive(student, key))

​ 另外还必须是一个具体的属性,这也非常的致命。假如后续需要扩展该对象,那么就必须手动为新属性设置 setter 和 getter 方法,这就是为什么不在 data 中声明的属性无法自动拥有双向绑定效果的原因 。这时需要调用 Vue.set() 手动设置。

​ 针对 Array 类型的劫持

​ 数组是一种特殊的对象,其下标实际上就是对象的属性,所以理论上是可以采用 Object.defineProperty() 方法处理数组对象。 

​ 但是 Vue 并没有采用上述方法劫持数组对象,原因分析:1、特殊的 length 属性,相比较对象的属性,数组下标变化地相对频繁,并且改变数组长度的方法也比较灵活,一旦数组的长度发生变化,那么在无法自动感知的情况下,开发者只能手动更新新增的数组下标,这可是一个很繁琐的工作。2、数组主要的操作场景还是遍历,而对于每一个元素都挂载一个 get 和 set 方法,恐怕也是不小的性能负担。

​ 数组方法的劫持:最终 Vue 选择劫持一些常用的数组操作方法,从而知晓数组的变化情况:push', 'pop', 'shift', 'unshift', 'sort', 'reverse', 'splice'。数组方法的劫持涉及到原型相关的知识,首先数组实例大部分方法都是来源于 Array.prototype 对象。顺便提一下,采用 Vue.set() 方法设置数组元素时,Vue 内部实际上是调用劫持后的 splice() 方法来触发更新。

总结:由上述内容可知,Vue 中的数据劫持分为两大部分:

针对 Object 类型,采用 Object.defineProperty() 方法劫持属性的读取和设置方法;

针对 Array 类型,采用原型相关的知识劫持常用的函数,从而知晓当前数组发生变化。

并且 Object.defineProperty() 方法存在以下缺陷:每次只能设置一个具体的属性,导致需要遍历对象来设置属性,同时也导致了无法探测新增属性;属性描述符 configurable 对其的影响是致命的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/757306.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

概率论的学习和整理17:EXCEL里直接对应的分布公式计算概率(未完成)

1EXCEL计算这些特殊分布的方差 1.1 用原始的概率&#xff0c;期望和方差的方法 虽然计算概率&#xff0c;需要用对应分布的公式P(xn) 想了解的随机变量是总次数n&#xff0c;需要对应几何分布&#xff0c;负二项分布P(xk) 想了解的随机变量是成功次数k&#xff0c;需要对应超几…

算法竞赛,机器学习,深度学习ai学习方向如何规划,搭建环境等答疑

目录 1了解人工智能的背景知识 2 补充数学或编程知识 3 熟悉机器学习工具库 4 系统的学习人工智能 5 动手去做一些AI应用 1了解人工智能的背景知识 本文可以让你避免踩坑走弯路&#xff0c;一些虽然存在但是在研究或者工业上不常用的知识&#xff0c;为自己腾出更多的时间…

Ubuntu软件包安装失败:代码 bionic 和 focal的区别

问题 我在Ubuntu上使用apt安装软件时总是报一些错误&#xff0c;不是版本不对&#xff0c;就是依赖关系不对。尝试了各种方法&#xff0c;突然想到是不是软件源有问题。 查看/etc/apt/sources.list文件&#xff0c;发现使用了阿里云的软件源&#xff1a; deb http://mirrors…

FreeRTOS实时操作系统(十六)内存管理

系列文章 FreeRTOS实时操作系统&#xff08;一&#xff09;RTOS的基本概念 FreeRTOS实时操作系统&#xff08;二&#xff09;任务创建与任务删除&#xff08;HAL库&#xff09; FreeRTOS实时操作系统&#xff08;三&#xff09;任务挂起与恢复 FreeRTOS实时操作系统&#x…

Android调用google原生裁剪,兼容三方相册裁剪功能

Android调用google原生裁剪&#xff0c;兼容三方相册裁剪功能 效果图实现功能编写CropImage类继承 ActivityResultContract调用 效果图 实现功能 本篇文章裁剪功能实现兼容Android6&#xff0c;解决部分google手机&#xff08;有部分Android10的Google手机无法使用google自带裁…

“遇见0和1”小程序正式开源

开源地址 https://gitee.com/lingstudy/meet0and1-applets-share 纯云开发&#xff1a;微信小程序 —“遇见0和1”开源发布 关于小程序 小程序前端使用 ColorUI 组件库&#xff0c;并且参考了大佬“爱敲代码的猫” 的开源项目 WeHalo 的页面设计&#xff0c;后端使用小程序纯云…

stm32读写nand flash

文章目录 1.简介2.频率设置3.FSMC参数设置4.修改宏定义 NAND_DEVICE5.程序测试5.1.简单测试5.2.擦除、写入、读取测试 注意 1.简介 目前我在使用stm32f407ZGT6来读写三星的nand flash【K9F1G08U0E】。 板子我是在这里买的 【STM32F407ZGT6最小系统板/核心板/转接板/开发板/加1…

Python应用实例(二)数据可视化(一)

数据可视化&#xff08;一&#xff09; 1.安装Matplotlib2.绘制简单的折线图2.1 修改标签文字和线条粗细2.2 矫正图形2.3 使用内置样式2.4 使用scatter()绘制散点图并设置样式2.5 使用scatter()绘制一系列点2.6 自动计算数据2.7 自定义颜色2.8 使用颜色映射2.9 自动保存图表 数…

QT实现雷达扫描

参考链接&#xff1a;https://www.jb51.net/article/279998.htm 在此基础上做了优化。 效果图&#xff1a; 鼠标左键点击显示当前点相对于圆心的距离和方位 // 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QPainter> #include <Q…

Java并发编程(11) —— CountDownLatch原理详解

一、CountDownLatch介绍 在日常开发中经常会遇到需要在主线程中开启多个线程去并行执行任务&#xff0c;并且主线程需要等待所有子线程执行完毕后再进行汇总的场景。在 CountDownLatch 出现之前一般都使用线程的join()方法来实现这一点&#xff0c;但是 join 方法不够灵活&…

jq实现网站-点击标签,添加到一个盒子中,再次点击去掉该标签

实现效果&#xff1a; 代码逻辑&#xff1a; 首先使用hasClass()方法判断点击的标签是否已经存在于盒子中。如果标签已经存在于盒子中&#xff0c;则使用removeClass()方法移除标签的’active’类名&#xff0c;并使用filter()方法找到盒子中与点击的标签文本相同的元素&#…

CAN总线(三)CAN总线链路层的三个标准

1、高速CAN总线 ISO 11898-2中定义了通信速率为125Kbps~1Mbps的高速闭环CAN通信标准,当通信总线长度≤40米,最大通信速率可达到1Mbps,高速闭环CAN(高速CAN)通信如下图所示: 1.1、电气特性 高速CAN总线上为显性电平(逻辑0)时,CAN_H为3.5V、CAN_L为1.5V,此时电压差是…

Qt应用开发——下载安装和HelloWorld

目录 1、下载和安装 2、HelloWorld 1、下载和安装 工欲善其事&#xff0c;必先利其器。第一步环境安装好是必要的过程。Qt 在23年4月份已经更新到了6.5.0&#xff0c;相对于其他的工具&#xff0c;Qt不断在维护升级这一点就非常的友好&#xff0c;这里对版本的迭代更新内容不…

由变上限积分求导到随机变量的概率分布

变上限积分求导书推导 推导过程根据导数的定义和积分的几何意义&#xff0c;看图&#xff1a; 随机变量的概率密度推导 若随机变量x 在 &#xff08;负无穷&#xff0c;正无穷&#xff09;的区间上服从f(x)的概率密度&#xff0c;设y g(x), x h(y)&#xff0c;求y 的概率…

web前端开发工程师的工作职责(合集)

web前端开发工程师的工作职责1 职责&#xff1a; 1.Web前端功能设计、开发和实现&#xff0c;与后台工程师协作&#xff0c;完成数据交互、动态展现; 2.对UI设计的结果进行页面制作(CSS/css3xhtml[表情]ml5JS); 3.熟悉编写可复用的用户接口组件; 4.从视觉和易用性角度&…

Git 安装设置

一&#xff1a;介绍 Git 是一个开源的分布式版本控制系统&#xff0c;用于敏捷高效地处理任何或小或大的项目。 二&#xff1a;安装 安装 Git for Windows&#xff0c;网址&#xff1a;https://git-scm.com/ 选择安装组件&#xff1a; 上图红框内的选项是默认勾选的&#xff…

设备集中监控,半导体CMS系统的优势解析

设备集中监控是半导体制造企业中的一项重要任务。传统的设备管理往往存在着分散的监控系统和孤立的报警中心的问题&#xff0c;给企业管理带来了一系列的挑战。而半导体CMS系统的出现&#xff0c;为企业解决了这些问题&#xff0c;并带来了明显的优势。 半导体CMS系统实现了设备…

初识mysql数据库之复合查询

目录 一、多表查询的概念 二、笛卡尔积 1. 笛卡尔积的概念 2. 笛卡尔积使用案例 2.1 显示雇员名、雇员工资以及所在部门的名字 2.2 显示部门号为10的部门名&#xff0c;员工名和工资 2.3 显示所有员工的姓名、工资和工资级别 3. 自连接 3.1 自连接的概念 3.2 自连接案…

idea 中的 pom.xml 文件变为灰色

idea 中的 pom.xml 文件变为灰色被忽略掉了 可能是新建 Module 之前创建了同名 Module&#xff0c; 并进行删除&#xff0c;idea 自动认为该排除此 Module 解决方法&#xff1a; 我们只要到 File → Settings → Build,Execution,Deployment →Ignored Files&#xff0c; …

数据结构(王道)——栈

一、栈的定义&#xff1a; 二、栈的基本操作&#xff1a; 对于栈的出栈顺序的理解&#xff1a; 栈总结&#xff1a; 三、顺序栈 栈的基本操作&#xff1a; 静态方式创建栈&#xff1a; 初始化&#xff1a; 进栈&#xff08;插入&#xff09;&#xff1a; 出栈&#xff08;删除&…