通过一个简单的例子了解vue双向数据绑定的核心原理

发布于:2024-10-24 编辑:匿名 来源:网络

vue的一大优势就是双向数据绑定,但是在react或者小程序中,我们需要手动setState和setData来修改视图数据。 vue2中使用Object.defineProperty来劫持对象属性的getter和setter,因此data函数需要返回一个对象。

如果data中没有定义该属性,则不会进行双向绑定,因为它没有被劫持。双向数据绑定也使用了设计模式中的发布/订阅模式。

当触发getter时,执行依赖收集,当触发setter时,通知相应的执行收集的依赖回调。 Object.defineProperty使用语法:Object.defineProperty(obj,prop,descriptor)。

具体使用请参考下面的demo。请注意,使用单独的值变量来存储年龄值。

如果没有,直接在get函数中写入person.age来获取值,会再次触发get死循环。集合中可以直接通过修改value的值来改变人物的年龄属性值。

这是因为我们使用了外部值变量。直接在set中修改value的值。

当获取值时,get返回的值实际上就是返回的值。 。

代码语言:javascript copy let person = { name: '周小黑',age: 18} let value = person.ageObject.defineProperty(person, 'age', { get() { console.log('获取年龄:' + value) return value }, set(e) { console.log('修改年龄:' + e) ??value = e }})console.log(person.age) // 18person.age = 20console.log(person .age) // 20 依赖于收集并执行数据变化时需要完成的所有操作。我们需要提前收集它们。

当真正发生变化的时候,就会把一些东西拿出来执行。简单理解双向数据绑定就是指当属性值发生变化时,我们需要程序自动做一些依赖于当前值的操作。

代码语言:javascript copy let person = { name: '周小黑' ,age: 18}let value = person.ageObject.defineProperty(person, 'age', { get() { console .log('获取年龄:' + value) 返回值 }, set(e) { console.log( '修改年龄:' + e) value = e action() }})function action() { console. log('我是数据变化时要执行的操作') const val = person.age * person.money = val console .log(person)}person.age = 20//修改age:20//我是数据变化要执行的操作//获取age:20// { name: '周小黑', Age: [Getter/Setter ], Money: 0 } 为了简单模拟,当人的年龄发生变化时,我们添加一个钱归因于该人。这里的代码执行逻辑:我们提前定义了一个action函数来执行操作。

当我们修改age属性时,该集合就会被触发。当设置被触发时,就意味着数据发生了变化。

直接执行集合中的动作函数即可。不过上面的代码有一个明显的问题,就是没有自动收集action函数。

我们不能为每个属性定义额外的action1,action2...操作函数。自动依赖收集为了实现自动依赖收集,我们修改上述代码,通过封装一个onChange公共函数来专门收集依赖。

它的参数是一个执行操作的函数: 代码语言:javascript copy let person = { name : '周小黑',age: 18}let value = person.ageObject.defineProperty(person, 'age', { get() { console.log('获取年龄:' + value) return value }, set(e ) { console.log('修改年龄:' + e) value = e action() }})let action = nullconst onChange = (callback) => { action = callbackcallback() // 这里先执行,触发获取依赖集合}onChange(() => { console.log('我是数据变化时要执行的操作') const val = person.age * person.money = val console.log(person)})person.age = 20//我是数据变化时要执行的操作 // 获取age:18 // { name: '周小黑',age: [Getter/Setter], Money: 0 }//修改age:20 //我是数据变更要执行的操作 //获取age: 20 // { name: '周小黑',age: [ Getter/Setter], Money: 0 } 在调用依赖收集函数onChange时,我们首先将依赖收集到外部action中,当修改年龄触发set时,我们可以直接执行action,这样就可以收集多个依赖可以实现回调。不过,上面的代码仍然存在一个问题:需要手动调用onChange函数。

只会执行最后一次调用onChange收集的回调,并且无论当前依赖属性是否发生变化都会执行。下面继续改造: 代码语言:javascript copy let person = { name: '周小黑',age: 18} let value = person.ageObject.defineProperty(person, 'age', { get() { onCollect('age' ) console.log('获取年龄:' + value) return value }, set(e) { console.log('修改年龄:' + e) ??value = e onExecute('age') }})let action = null const onChange = (callback) => { action = callback callback() // 这里先执行,触发获取依赖收集}//收集所有依赖框 const eventBox = {}//收集依赖 function onCollect(key) { let arr = 事件框[键] || [] arr.push(action) eventBox[key] = arr}//执行函数 onExecute(key) { let arr = eventBox[key] || [] arr.map(fn => fn( ))}onChange(() => { console.log('我是数据变化时要执行的操作') const val = person.age * person.money = val console.log(person)})onChange(() = > { console.log('我是数据更改2要执行的操作') const val = person.age * person.money= val console.log(person)})onChange(() => { console.log('我是数据变化时要执行的操作,但我没有依赖')})person.age = 20//我是数据变化要执行的操作 // 获取age: 18 // { name: '周小黑',age: [Getter/Setter], Money: 0 }//我是要执行的操作the data change 2 // 获取age: 18 // { name: '周小黑',age: [Getter/Setter], Money: 0 }//我是数据变化要执行的操作,但是我没有任何依赖 // 修改age: 20 // 我是数据变化要执行的操作 // 获取age: 20 // { name: '周小黑',age: [Getter/Setter], Money: 0 }// I am 数据变化要执行的操作 2 // 获取age :20 // { name: '周小黑',age: [Getter/Setter], Money: 0 } 定义一个eventBox对象,用于存储所有属性的依赖回调。

当 get 被触发时,会调用 onCollect 将依赖项收集到框中。 ,当修改数据触发set时,会从eventBox框中取出对应属性的依赖回调来执行核心代码。

理解上面的代码并不难。也许最难理解的是get中如何完成自动依赖收集。

,当我们调用onChange时,外部action会存储当前要收集的依赖回调(记住这一点很重要),然后直接执行回调函数触发获取依赖收集。如果回调内部触发了get(比如上面的代码中,通过person.age获取年龄),那么就会走内部get函数。

我们只需要在get中调用onCollect即可将action收集到eventBox盒子对应的key值中。如果还是看不懂,可以在断点处运行一下代码,你就会明白了。

其实到这里你就基本可以了解Vue双向数据绑定的实现原理和步骤了:getter自动将依赖收集到一个盒子里,setter取出收集到的对应依赖并遍历执行。核心是发布/订阅模式。

。其实上面的代码还有一个问题:执行set中的回调会触发get,然后往box里添加重复的回调。

可以通过将之前的数组数组改为Set数据结构来存储key对应的回调来解决这个问题。解决方案;另外,上面的代码有一个没有依赖的回调,也添加到了age对应的回调中。

这里,每次执行action时都需要将action重置为null,然后在get中还需要对action进行判断。仅当它不为空时才会收集依赖项。

之前的版本为了理解简单的数据存储,直接使用了最简单的Object和Array。实际中需要结合使用WeakMap、Map、WeakSet、Set来存储数据。

完整的修改代码请参考下面的代理版本。 vue3中的proxyvue2中,使用Object.defineProperty来劫持对象的getter和setter。

在vue3中,被proxy替代。其实核心原理还是和上面一样,只不过收集和执行依赖改为proxy来劫持getter和setter。

。将上面的 demo 替换为 proxy 来实现: 代码语言:javascript copy let person = { name: '周小黑',age: 18} let action = nullconst onChange = (callback) => { action = callback callback() //这里先执行一个触发器 get 依赖收集 action = null }//收集所有依赖框 const eventBox = {}//收集依赖 function onCollect(key) { let arr = eventBox[key] || new Set() arr.add (action) eventBox[key] = arr}//执行函数 onExecute(key) { let arr = eventBox[key] || [] arr.forEach(fn => fn())}let value = person.ageconst proxyPerson = new Proxy(person, { get(target, key) { action && onCollect(key) console.log('get' + key ) return target[key] }, set(target, key, newValue) { target[key] = newValue console.log('修改' + key + ':' + newValue) onExecute(key) }})onChange(() => { console.log('我是数据变化时要执行的操作') const val = proxyPerson .age * proxyPerson.money = val console.log(proxyPerson)})onChange(() => { console.log('我是数据变更2要执行的操作') const val = proxyPerson.age * proxyPerson.money = val console.log(proxyPerson)})onChange (( ) => { console.log('我是要进行数据变更的操作,但我没有任何依赖')})proxyPerson.age = 20我正在参加第三期腾讯科创特训营会包括获奖论文和团队组建。

通过一个简单的例子了解vue双向数据绑定的核心原理

站长声明

版权声明:本文内容由互联网用户自发贡献,本站不拥有所有权,不承担相关法律责任。如果发现本站有涉嫌抄袭的内容,欢迎发送邮件 举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。

标签:

相关文章

  • 富森美身为LP,一周投三笔

    富森美身为LP,一周投三笔

    短短一周,富森美又出手了。 投资圈-解码LP消息,昨日(5月16日),上市公司Fusemi发布两份公告,公布其全资子公司海南Fusemi投资有限公司(简称“海南投资”)的最新投资状况。 具体如下:海南投资与鼎立资本投资管理(徐州)有限公司(简称“鼎立资本”)等共同投资设立宁波

    06-18

  • 「奥创光年」获数千万美元A轮融资,由凯辉资本领投

    「奥创光年」获数千万美元A轮融资,由凯辉资本领投

    投资界(ID:pedaily)8月9日消息,据悉,AI全链路营销公司奥创光年已完成1万A轮融资,由凯辉资本领投,老股东真格基金跟投。 几个月前,奥创光年完成了真格基金和万物资本的天使轮融资。 奥创Lightnian成立于2017年,是一家AI原生营销公司,由前飞书产品经理徐喆和前雅诗兰黛

    06-17

  • 巨龙呼吁:半导体产业链复工复产的步伐和策略要加快

    巨龙呼吁:半导体产业链复工复产的步伐和策略要加快

    SEMI China 5月17日下午,由浦东新区财政局、区科委主办经委、中国人民银行上海总部、上海证券交易所等单位。 半导体产业发展趋势与投资策略专题沙龙,围绕金融支持、保障半导体产业链复工复产、安全稳定等问题展开讨论与交流。 威尔半导体、晶峰明源、中升光电、岭东微电子

    06-06

  • 不只是后视镜,人机交互才是嘟嘟智能的未来

    不只是后视镜,人机交互才是嘟嘟智能的未来

    “科技”,对于IT专家来说,光是听到这个词就眼睛一亮,已经成为方便工作、办公的必备工具。 公众的生活。 不可或缺的技术手段。 从飞向月球到螺丝钉的应用,科技给人类的使用带来了令人惊讶的变化。 后视镜作为汽车不可或缺的部件,自然离不开时代和科技赋予其的至高使命。

    06-17

  • 马斯克跌下神坛,特斯拉接手广告,

    马斯克跌下神坛,特斯拉接手广告,

    ,《广告时代》(广告时代)评选特斯拉为“美国最受欢迎的品牌”之一。 与汽车品牌每年花费数亿美元做广告不同,特斯拉并不依赖传统广告,而是通过产品质量和用户声誉来宣传公司。 仔细想想,特斯拉和桥头的大排档是一样的。 菜品不需要太多,一盘恰到好处的炒牛肉就能通过邻

    06-21

  • 月球将有4G网络,但地球上还有数十亿人没有连上互联网

    月球将有4G网络,但地球上还有数十亿人没有连上互联网

    月球上有生命吗?随着人类对月球的探索越来越近,这个问题的答案得到了更多证据的支持。 为了进一步探索月球,人类首先回答了“月球上有网络吗?”的问题。 问题。 诺基亚表示有可能,2020年月球上就能开通4G网络。 诺基亚想要在月球上建立网络,宇航员计划在阿尔忒弥斯旁边拍

    06-21

  • 首次发布 -建云科技完成新一轮融资近千万元,甲子启航持续追投

    首次发布 -建云科技完成新一轮融资近千万元,甲子启航持续追投

    投资圈(微信ID:pedialy)据11月10日消息,建云科技完成新一轮融资,融资金额近千万元。 本轮融资将由天使轮唯一投资方甲子启航跟投,清科资本将担任唯一投资方。 据了解,融资资金将主要用于投资农作物数据采集技术升级、人工智能算法更新、供应链技术平台建设等。 建云科技

    06-17

  • 鸿海第一季净利润为新台币281.6亿元

    鸿海第一季净利润为新台币281.6亿元

    鸿海第一季度净利润为新台币6亿元,而市场预估为新台币5亿元。

    06-18

  • 2025年将形成8000亿新一代信息技术产业集群

    2025年将形成8000亿新一代信息技术产业集群

    郑州日报 市政府办公厅近日发布培育壮大新一代信息技术产业,加快新一代信息技术与制造业融合发展《关于加快新一代信息技术产业发展的实施意见》。 《意见》提到,到2020年,我市力争培育5家主营业务收入超百亿元的企业,形成十亿级新一代信息技术产业集群,基本建成开放、共

    06-06

  • 阿里巴巴2021年第一财季净利润394.7亿元,淘宝直播GMV同比增长超100%

    阿里巴巴2021年第一财季净利润394.7亿元,淘宝直播GMV同比增长超100%

    8月20日,阿里巴巴发布第一财季财报。 财报显示,本季度营收为5000万元,市场预期为5500万元,去年同期为2亿元;净利润为7000万元,市场预期为8800万元,去年同期为5200万元。 财报显示,第一财季GMV同比增长27%。 其中,淘宝直播GMV同比持续增长超过%,商家直播贡献了淘宝直

    06-17

  • “汇玩科技”完成数百万元Pre-A轮融资

    “汇玩科技”完成数百万元Pre-A轮融资

    “汇玩科技”近日完成数百万元Pre-A轮融资,由海尔海创汇独家投资。 本轮资金主要用于建设成都研发中心和组建销售团队。 汇万科技成立于2017年,专注于为英语教学培训机构提供定制化教学平台解决方案。

    06-18

  • 微众税务银行完成1亿元B轮融资,打造以税定信用、做银行信用的智能决策引擎

    微众税务银行完成1亿元B轮融资,打造以税定信用、做银行信用的智能决策引擎

    据投资界5月16日消息,微众税务银行宣布本次融资早间获悉,今年7月已完成上市公司奥马电气股份有限公司领投的A亿元B轮融资。 今年4月,微众税务银行完成IDG领投的数千万元A轮融资。   维众税银是一家成立于深圳的税银信贷大数据金融科技公司。 帮助银行为中小企业“绘制”企

    06-18