02-详细介绍Vue中的数据代理和数据响应式

news2025/7/20 2:15:12

数据代理

MVVM

原生的Javascript代码Model和View没有分离,如果数据发生任意的改动, 接下来我们需要编写大篇幅的JS代码操作DOM元素更新视图

MVVM是目前前端开发领域当中倡导Model和View进行分离的开发思想或者架构模式,大部分主流框架如Vue和React都借鉴了这个MVVM思想

  • Model和View分离之后出现了一个核心VM负责更新,当Model发生改变之后VM自动去更新View,同理当View发生改动之后VM自动去更新Model

Vue框架中对应MVVM中的角色

  • M(Model): 对应data配置项中的数据

  • V(View): 对应容器中的模板语句, Vue实例的所有属性及Vue原型上的所有属性在模板语句中都可以直接使用

  • VM(ViewModel): 对应Vue的实例对象也是MVVM中核心部分

在这里插入图片描述

<!-- 准备容器 -->
<!-- View V-->
<div id="app">
    姓名:<input type="text" v-model="name">
</div>

<!-- vue程序 -->
<script>
    // ViewModel  vm表示Vue实例
    const vm = new Vue({
        el : '#app',
        // Model  M
        data : {
            name : 'zhangsan'
        }
    })
</script>

代理机制的原理

Vue实例可以访问的属性有三种: 以$或以_开始的属性用来和数据代理的属性区分开, Vue实例对象的原型对象的属性,Vue实例对象代理的目标对象data上的属性

  • 以$开始的属性: 可以看做是公开的属性,这些属性是供程序员使用的
  • 以_开始的属性: 可以看做是私有的属性,这些属性是Vue框架底层使用的, 程序员很少使用

对象新增属性的方法: Object.defineProperty(新增属性的对象, ‘新增的属性名’, {新增属性的相关配置项key:value})

  • 当配置项当中有setter和getter的时候,value和writable配置项存在会报错, enumerable和configurable可以存在
属性配置项作用
value设置新增属性的值
writable设置新增属性的值是否可以被修改, true表示可以修改 , 默认是false表示不能修改
enumerable设置新增属性是否可以遍历,true表示可以遍历的,默认是false表示不可遍历,Object.keys(对象)可以遍历对象的属性
configurable设置新增属性是否可以被删除,true表示可以被删除, ,默认是false表示不可删除,delete 对象.属性可以删除对象的属性
getter方法当读取新增属性值的时候,getter()方法被自动调用, 返回新增属性的值
setter方法当给新增属性赋值的时候,setter(val)方法被自动调用,val参数可以接收修改后的值
<script>
    // 这是一个普通的对象
    let phone = {}

    // 临时变量
    let temp

    // 给phone对象新增一个color属性并设置setter和getter方法
    Object.defineProperty(phone, 'color', {
        //value : '太空灰',
        //writable : true,
        enumerable : false,
        configurable : false
        
        // getter方法配置项
        get : function(){
            console.log('getter方法执行');
            return temp
            // 以下这种写法会造成死循环,一直读取新增属性值一直调用get方法
            //return this.color
        },
        // setter方法配置项
        set : function(val){
            console.log('setter方法执行',val);
            temp = val
            // 以下这种写法会造成死循环,一直给新增属性赋值一直调用set方法
            //this.color = val
        }
        
        // 在ES6对象中的函数/方法:function是可以省略的
         get(){
            console.log('getter方法执行');
            return temp
        },
        // setter方法配置项
        set(val){
            console.log('setter方法执行',val);
            temp = val
        }
    })
</script>

代理机制的实现

数据代理机制就是通过访问代理对象的属性来间接访问目标对象的属性

<script>
    // 目标对象
    let target = {
        name : 'zhangsan'
    }

    // 代理目标对象的name属性加给代理对象新增一个name属性(属性名要一致)
    let proxy = {}

    Object.defineProperty(proxy, 'name', {
        get(){
            return target.name
        },
        set(val){
            target.name = val
        }
    })
</script>

在Vue框架中,代理对象是Vue的实例对象vm,目标对象是参数中的data对象,vm新增属性给data对象的属性做数据代理

  • Vue实例不会给以_和$开始的属性名做数据代理,即在Vue当中给data对象的属性名命名的时候不能以这两个符号开始(防止和Vue框架自身的属性名冲突)
<script>
    const vm = new Vue({
        el : '#app',
        data : {
            msg : 'Hello Vue!',
            // 以下这两个属性就是data对象的属性,vm不会做数据代理
            _name : 'zhangsan',
            $age : 20
        }
    })
</script>

Vue框架数据代理的实现

// 定义一个Vue类
class Vue {
    // 定义构造函数
    constructor(options){// options是一个简单的纯粹的JS对象{},有一个data配置项
        // 获取data对象的所有的属性名
        Object.keys(options.data).forEach((propertyName, index) => {
            //console.log(typeof propertyName, propertyName, index)
            // 如果是以_和$开始的属性名就不做数据代理
            let firstChar = propertyName.charAt(0)
            if(firstChar != '_' && firstChar != '$'){
                // this就是Vue的实例对象
                Object.defineProperty(this, propertyName, {
                    get(){
                        // propertyName是个字符串,通过对象["属性名"]的方式读取属性值
                        return options.data[propertyName]
                    },
                    set(val){
                        options.data[propertyName] = val
                    }
                })
            }
        })
    }
}

_data和$data

<script>
    function Vue(options){
        this._init(options);
        Vue.prototype._init = function (options){
            // 调用方法将options合并到$options,即$options含有options的所有属性
            
            // 程序执行到这里的时候vm上还没有_data属性
    		var data = vm.$options.data;
            
            // 程序执行完这个代码之后,vm对象上多了一个_data这样的属性
            // 如果data是函数,则调用getData(data, vm)来获取data对象
            // 如果data不是函数,则直接将data对象返回给data变量, 并且同时将data对象赋值给vm._data属性
            data = vm._data = isFunction(data) ? getData(data, vm) : data || {};
            
            // 对于Vue实例vm来说,_data和$data都直接指向了底层真实的data对象,_data是私有的用于框架内部使用的, $data是公开的是给程序员使用
            // 如果我们程序员不想走代理的方式读取data(不走getter和setter方法),可以通过_data和$data属性直接读取data当中的数据
            
            // 判断字符串是否以_和$开始,true表示是, false表示否
            function isReserved(str) {
                var c = (str + '').charCodeAt(0);
                return c === 0x24 || c === 0x5f;
            }
            
            // 数据代理: 给vm新增属性,代理_data即data对象的所有属性
            while (i--) {
                var keys = Object.keys(data);
                // key是data对象的一个属性名
                var key = keys[i];
                
                // sharedPropertyDefinition是新增属性的配置项
                var sharedPropertyDefinition = {
                    enumerable: true,
                    configurable: true,
                    get: noop,
                    set: noop
              	};
                proxy(vm, "_data", key)
                function proxy(target, sourceKey, key) { 
                    sharedPropertyDefinition.get = function proxyGetter() {
                        return this[sourceKey][key];
                    };
                    sharedPropertyDefinition.set = function proxySetter(val) {
                        this[sourceKey][key] = val;
                    };
                    Object.defineProperty(vm, key, sharedPropertyDefinition);
                }   
        }   
    }
</script>

数据响应式

当修改data配置项中的数据后,页面实现自动改变/刷新的响应式效果

数据劫持

Vue的响应式是通过数据劫持机制实现的: 底层使用了Object.defineProperty方法给新增的属性都配置了setter方法对数据进行劫持

  • 当给新增属性赋值时setter方法则被自动调用,setter方法的主要作用是修改属性值和重新渲染页面
  • Vue会给创建Vue实例时data中所有的属性(包括属性中的属性)都添加响应式

后期给Vue实例动态追加的属性默认没有添加响应式处理的

  • Vue.set/$set(目标对象,‘属性名’, 值)
  • 追加响应式属性时目标对象不能直接是vm或vm.$data,所以vm和data中的属性只能在声明时提前定义好

通过数组的下标去修改数组中的元素,默认情况下是没有添加响应式处理的(数组本身或者数组元素的内部属性都是有响应式处理的)

  • 第一种方案:vm.$set/set(数组对象, 下标, 值)
  • 第二种方案:push(),pop(),reverse(),splice(), shift(),unshift(),sort(),Vue对这些方法进行了重写增加了响应式处理的功能
<body>
    <div id="app">
        <h1>{{msg}}</h1>
        <div>姓名:{{name}}</div>
        <div>年龄:{{age}}岁</div>
        <div>数字:{{a.b.c.e}}</div>
        <div>邮箱:{{a.email}}</div>
        <!--数组中的元素没有响应式处理-->
         <ul>
            <li v-for="user in users">{{user}}</li>
        </ul>
        <!--如果数组元素是个对象,对象的属性是有响应式处理的-->
        <ul>
            <li v-for="vip in vips" :key="vip.id">{{vip.name}}</li>
        </ul>
        
    </div>
    <script>
        const vm = new Vue({
            el : '#app',
            // Vue会给创建Vue实例时data中所有的属性(包括属性中的属性)都添加响应式
            data : {
                msg : '响应式与数据劫持',
                name : 'jackson',
                age : 20,
                a : {
                    b : {
                        c : {
                            e : 1
                        }
                    }
                }
                users : ['jack', 'lucy', 'james'],
                vips : [
                    {id:'111', name:'zhangsan'},
                    {id:'222', name:'lisi'}
                ]
            }
        })
        // vm后期追加的属性并没有添加响应式处理
        //vm.$data.a.email = 'jack@126.com'
        
        // 调用以下的两个方法给后期追加的属性添加响应式处理
        //Vue.set(vm.a, 'email', 'jack@123.com')
        vm.$set(vm.a, 'email', 'jack@456.com')
        
        // 不能直接给vm/vm.$data追加响应式属性,只能在声明时提前定义好
        //Vue.set(vm, 'x', '1')
        //Vue.set(vm.$data, 'x', '1')
        
        // 直接通过数组下标修改数组中的没有响应式效果
        vm.users[0] = "李四"
        vm.vips[0] = {id:'333',name:'wangwu'}
        
        // 数组中元素的属性有响应式效果
        vm.vips[0].name = "张三"
        
        
		// 操作数组元素并具有响应式效果
        Vue.$set(vm.users,0,"李四")
        Vue.$set(vm.vips,2,{id:'333',name:'wangwu'})
        vm.users.push('王五')
        // 修改数组从0位置开始的一个元素
        vm.users.splice(0,1'杰克')
    </script>
</body>

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

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

相关文章

介绍几款Linux 下终极SSH客户端

安全外壳协议&#xff08;Secure Shell&#xff0c;简称 SSH&#xff09;是一种网络连接协议&#xff0c;允许您通过网络远程控制计算机。特别是在Linux命令行模式下&#xff0c;使用SSH&#xff0c;可以很方便管理linux上的运维工作。以下是一些最受欢迎的Linux SSH客户端&…

mathematica解非齐次常微分方程通用写法。解RC微分方程,输入硬写为Cos,通用写法:将微分方程的解函数表达式转为mathematica的纯函数

输入电压为余弦信号, mathematica解微分方程举例&#xff08;mathematica解非齐次常微分方程通用写法&#xff09;

苹果相册回收站在哪里?删除的照片如何恢复?(已解决)

苹果手机的相册回收站是一个非常重要的功能&#xff0c;可以帮我们把一些不小心删除的重要照片给恢复回来。这个功能对于一些比较粗心的小伙伴来说简直是救星&#xff01; 但是&#xff0c;还有许多第一次使用苹果手机的朋友不知道相册回收站在哪里。苹果相册回收站在哪里&…

东莞理工学院第四届“火焰杯”软件测试高校就业选拔赛颁奖典礼

10月10日下午&#xff0c;由软件测试就业联盟主办的第四届“火焰杯”软件测试高校就业选拔赛颁奖典礼在9A206举行。本次比赛邀请了测吧&#xff08;北京&#xff09;科技有限公司项目总监王雪冬、计算机科学与技术学院副院长刘文果和计算机科学与技术学院软件工程系主任张福勇为…

10 _ 递归:如何用三行代码找到“最终推荐人”?

推荐注册返佣金的这个功能我想你应该不陌生吧?现在很多App都有这个功能。这个功能中,用户A推荐用户B来注册,用户B又推荐了用户C来注册。我们可以说,用户C的“最终推荐人”为用户A,用户B的“最终推荐人”也为用户A,而用户A没有“最终推荐人”。 一般来说,我们会通过数据…

GSA、GSEA、ssGSEA、GSVA的算法原理及它们的联系与区别

一、 简述 为了从基因的表达水平中得到更加具体直观的生物学功能变化的信息&#xff0c;多种基于已知的基因集的分析方法应运而生。其中&#xff0c;基因集分析&#xff08;Gene Set Analysis&#xff09;、基因集富集分析&#xff08;Gene Set Enrichment Analysis&#xff09…

上网行为审计软件丨上网行为审计解决方案

很多人一听到上网行为审计就会心有余悸&#xff0c;认为是公司侵犯员工隐私的一种不良的方式&#xff0c;但是对于上网行为审计软件&#xff0c;我们要辩证的看待。 上网行为审计确实有一定的好处&#xff1a; 1、监控网络行为&#xff0c;防止不当行为&#xff1a; 上网行为…

jdk官网下载(详细步骤)

jdk全部版本下载网址 Java Archive | Oraclehttps://www.oracle.com/java/technologies/downloads/archive/ 下载之前先建立oracle账号(免费创建)&#xff0c;不用特意去搜&#xff0c;你点击下载jdk的时候会自动弹出来&#xff0c;自己建立一个账号就能下载了 找到自己要下载…

【论文笔记】Unifying Large Language Models and Knowledge Graphs:A Roadmap

&#xff08;后续更新完善&#xff09; 2. KG-ENHANCED LLMS 2.1 KG-enhanced LLM Pre-training 以往将KGs集成到大型语言模型的工作主要分为三个部分:1)将KGs集成到训练目标中&#xff0c;2)将KGs集成到LLM输入中&#xff0c;3)将KGs集成到附加的融合模块中。 2.1.1 Integr…

Sci Immunol丨先天性 IL-25-ILC2-MDSC 轴

今天和大家分享一篇发表于2022年6月的文章&#xff0c;题目为“An innate IL-25-ILC2-MDSC axis creates a cancer-permissive microenvironment for Apc mutation-driven intestinal tumorigenesis”&#xff0c;发表在《Sci Immunol》杂志上。文章主要研究了Interleukin-25 (…

数据智能化管理:企业网站备案信息API的应用案例

引言 在数字化时代&#xff0c;企业备案信息管理变得愈发重要。无论是为了合规性还是提高业务运营效率&#xff0c;企业都需要有效管理其网站备案信息。幸运的是&#xff0c;现代技术为企业提供了强大的工具&#xff0c;如企业网站备案信息API&#xff0c;可帮助他们更智能地管…

第一个QT程序

新建工程&#xff1a; 1. 点击“New Project” 2. 选择“Qt Widgets Application” 3. 工程名和路径 4. 构建系统选择 5. Details 一些细节 6. 选择Kits 7. 完成工程创建 点完成按钮 8. 运行下看 9. 一些示例代码 //main.cpp #include "mywidget.h"#include <Q…

关于宝塔面板提示“upgrade your ACME client to support TLSv1.2 or better”的解决办法

今天续期SSL证书的时候提示“upgrade your ACME client to support TLSv1.2 or better”&#xff0c;这一般是旧系统情况下TLS版本过低&#xff1a;acme.sh版本低于2.8所引起的&#xff0c;也就是提示&#xff1a;升级你的系统至 TLS 1.2 协议或更高版本。 但是国内服务器无法…

Java模块化应用实践之精简JRE | 京东云技术团队

导语 Java9及以后的版本引入了模块化特性&#xff0c;但是直到今天JDK21都发布了&#xff0c;依然没有被大量使用起来&#xff0c;那么这个特性就真的没啥意义了吗&#xff1f; 别忘了&#xff0c;Java本身可是把模块化做到了极致的&#xff0c;所以可以利用这个特性对JRE本身…

Vue解决img路径报错

问题&#xff1a; 我们有一组图片的地址&#xff0c;使用v-for遍历img标签&#xff0c;设置src属性&#xff0c;却无法获取图片 解决方法&#xff1a; 因为我们将图片路径放入data中后会被webpack进行打包&#xff0c;这时候的路径仅仅只是一个字符串&#xff0c;无法进行解析…

diffusers-Tasks

https://huggingface.co/docs/diffusers/using-diffusers/unconditional_image_generationhttps://huggingface.co/docs/diffusers/using-diffusers/unconditional_image_generation1.Unconditional image generation 无条件图像生成是一个相对简单的任务。模型仅生成图像&…

QT5交叉编译保姆级教程(arm64、mips64)

什么是交叉编译&#xff1f; 简单说&#xff0c;就是在当前系统平台上&#xff0c;开发编译运行于其它平台的程序。 比如本文硬件环境是x86平台&#xff0c;但是编译出来的程序是在arm64架构、mips64等架构上运行 本文使用的操作系统&#xff1a;统信UOS家庭版22.0 一、安装…

生产环境超时问题最佳实践-从timeout导致500错误获得

最近发现线上系统的一个功能出现500错误。该功能是调用外部服务&#xff08;内含把一个文件传给另一个平台&#xff09;&#xff0c;用同样文件&#xff0c;测试环境下测试可以&#xff0c;线上环境不行。先记录解决思路如下&#xff1a; 1、比较环境&#xff1a;测试环境和线上…

计算机视觉的监督学习与无监督学习

什么是监督学习&#xff1f; 监督学习是一种机器学习算法&#xff0c;它从一组已标记的 合成数据生成器中生成的训练数据中学习。这意味着数据科学家已经用正确的标签&#xff08;例如&#xff0c;“猫”或“狗”&#xff09;标记了训练集中的每个数据点&#xff0c;以便算法可…

NFC芯片MS520:非接触式读卡器 IC

MS520 是一款应用于 13.56MHz 非接触式通信中的高集成 度读写卡芯片。它集成了 13.56MHz 下所有类型的被动非接触 式通信方式和协议&#xff0c;支持 ISO14443A 的多层应用。 主要特点 ◼ 高度集成的解调和解码模拟电路 ◼ 采用少量外部器件&#xff0c;即可将输…