大连将30个中风险地区调整为低风...
大连将30个中风险地区调整为低风险地区 ...
本篇文章带大家学习vue,聊聊vue如何处理响应式数据?希望对大家有所帮助!
关于vue的响应式数据,你可能有很多疑惑。
(资料图片)
比如为什么要改用proxy?
比如为什么有reactive和ref两个api?
比如vue是如何实现响应式的?
其实这些在源码中,都能找到答案。
在第一篇vue3有哪些升级中,我也提了一下使用proxy的好处,以及Object.defineProperty的缺陷。但是今天,我想换个角度,让chatGPT来回答一下这个问题。【相关推荐:vuejs视频教程、web前端开发】
嗯,我感觉比我总结的好。
那继续下一个问题。
这个回答感觉就比较官方了,没有回答到我想要的答案。
理由其实很简单,因为proxy方法不能代理值类型,只能代理对象。所以需要一个额外的方法,来处理值类型的数据。
当然你也可以一个ref走天下,vue源码做了兼容会自动进行转换
有了这些理解之后,我们来看今天的重头戏,自己仿照vue3源码,实现一个响应式系统。
相关代码可以点击查看,根据文章的标题选择不同的分支即可。
在前文我们也介绍了,reactive其实用的是proxy代理对象。
我们可以使用proxy实现一个简单的代理函数reactive。
function reactive(target) { const isObject = (val) => val !== null && typeof val === "object" if (!isObject(target)) { console.warn(`数据必须是对象: ${String(target)}`) return target } const proxy = new Proxy(target, { get: (target, key, receiver) => { console.log("get", key) const res = Reflect.get(target, key, receiver) // track(target, key) // 这句很关键,不然嵌套数据,里面的不会响应 if (isObject(res)) { return reactive(res) } return res }, set: (target, key, value, receiver) => { console.log("set", key, value) const result = Reflect.set(target, key, value, receiver) // trigger(target, key, value) return result }, deleteProperty: () => { const result = Reflect.deleteProperty(target, key) return result } }) return proxy } const person = reactive({ name: "", age: 18, like: ["apple"] }) person.name = "vue test"复制代码
注意:Reflect
Reflect.get(target, key)跟直接访问target[key]是有一点差别的。
当代理对象中,有get,set等this指向的时候,可以重定向this。
举个例子:
const person = new Proxy({ name: "vue test", age: 18, get info() { return "name is :" + this.name + " age is:" + this.age } }, { get: (target, key, receiver) => { console.log("get", key) // return target[key] return Reflect.get(target, key, receiver) } }) console.log(person.info)复制代码
使用Reflect,我们在访问name, age的时候均可以触发。
改成target之后,则只会在info触发一次。
return target[key]复制代码
在vue3中,ref是通过get和set来实现的。
跟上面类似,也还是先声明一个函数,然后通过get,set来访问数据。
function ref(value) { return new RefImpl(value) } class RefImpl { constructor(value) { // 如果值是对象,则用reactive处理 this._value = isObject(value) ? reactive(value) : value // 记录一下初始值 this._rawValue = value } get value() { // trackRefValue(this) return this._value } set value(newVal) { if (!Object.is(newVal, this._rawValue)) { // 更新原始数据 this._rawValue = newVal // 更新 .value 的值 this._value = isObject(newVal) ? reactive(newVal) : value // triggerRefValue(this) } } }复制代码
源码也很直观的解释了,为什么ref的使用一定要用.value的方式使用,因为get/set就是通过value来设置的。
数据代理我们已经完成了,但是数据更改之后,怎么通知组件实现双向绑定呢?
答案就是依赖收集与触发,也就是当触发get的时候,我把触发get的这个条件【函数】给保存起来,当触发set的时候,重新执行一下触发一下这个条件【函数】不就行了吗。
还是来看代码,我加一个track收集的方法,加一个target触发的方法。(也就是上面代码片段注释掉的两行代码)
除此之外,还需要一个effect函数来管理触发函数。
let activeEffect = null const targetMap = new WeakMap() // 依赖收集/触发 function track(target, key) { let depsMap = targetMap.get(target) if (!depsMap) { targetMap.set(target, (depsMap = new Map())) } let dep = depsMap.get(key) if (!dep) { dep = new Set() } dep.add(activeEffect) depsMap.set(key, dep) } function trigger(target, key, value) { const depsMap = targetMap.get(target) if (!depsMap) { return } const deps = depsMap.get(key) if (!deps) { return } deps.forEach(effectFn => { if (effectFn.scheduler) { effectFn.scheduler() } else { effectFn() } }) } function effect(fn,options = {}) { const effectFn = () => { try { activeEffect = effectFn return fn() } catch (error) { activeEffect = null } } if (!options.lazy) { effectFn() } effectFn.scheduler = options.scheduler return effectFn } const person = reactive({ name: "hello world" }) effect(() => { console.log("effect person", person.name) }) setTimeout(() => { person.name = "setTimeout world" }, 2000)复制代码
activeEffect用来存触发的条件函数。
targetMap用来存放依赖字典,格式如下
{ target: { key: [] }}复制代码
输出结果为hello world,2秒之后,重新执行依赖性函数,输出setTimeout world
数据的代理并不复杂,就是在proxy的基础上,加上一些边界处理。而要实现响应式则需要加上依赖收集,依赖触发的实现。
effect是一个很重要的函数,很多api是基于这个函数开发的,比如useEffect, watch。组件的更新也是基于effect函数,这个后续还会提到。
如果不太理解effect,可以梳理一下执行顺序。
1.调用effect函数,并传入参数fn2.申明一个effectFn函数,并执行,将函数保存为activeEffect3.执行fn,读取person.name4.走proxy的get代理5.收集依赖,将activeEffect保存的函数存到全局map中6.此时,effect函数执行完毕,等待数据更新7.2s后,走proxy的set代理8.执行全局map中保存的函数,重新执行effect函数,再次回到步骤1相关代码可以点击查看,选择lesson3分支即可。
文章相关代码,可在vue/examples中查看,仿vue实现版本可在packages/reactivity中查看。
(学习视频分享:vuejs入门教程、编程基础视频)
以上就是深入浅析vue处理响应式数据的方法的详细内容,更多请关注php中文网其它相关文章!
关键词:
本篇文章带大家学习vue,聊聊vue如何处理响应式数据?希望对大家有所帮助!
php中文网 2023-03-28智通财经APP讯,塞力医疗(603716 SH)发布公告,公司于3月28日收到控股股东赛海科技的通知,赛海科技将其持有的公司1200万股无限售流通股办理了
智通财经 2023-03-28多民族小朋友正在西北街社区上手工艺课光明网讯3月25日下午,10余名汉、藏、羌等多民族小朋友在西北街社区认真地制作着手工艺品,这是该社区组
光明网 2023-03-28为有效预防和打击诈骗犯罪活动,提高辖区群众防范诈骗的意识,进一步维护人民群众根本利益,3月28日,郑州二七区长江路司法所协同长江路街道综
大河报网 2023-03-28产品3月27日3月28日涨跌单位:元 吨-10 柴油75507650100元 吨92 汽油84008500100元 吨95 汽油85508650100元 吨3月
生意社 2023-03-28有种重庆人的浪漫,叫追天逐月!
金台资讯 2023-03-28碳酸锂报价跌至263000元 吨今年迄今价格已经腰斩:据上海有色网发布的数据显示,今日锂电材料报价全线下跌,碳酸锂(99 5%电池级 国产)跌7000元 吨,报
和讯马金露 2023-03-28本篇文章带大家学习vue,聊聊vue如何处理响应式数据?希望对大家有所帮助!
php中文网 2023-03-28智通财经APP讯,塞力医疗(603716 SH)发布公告,公司于3月28日收到控股股东赛海科技的通知,赛海科技将其持有的公司1200万股无限售流通股办理了
智通财经 2023-03-28多民族小朋友正在西北街社区上手工艺课光明网讯3月25日下午,10余名汉、藏、羌等多民族小朋友在西北街社区认真地制作着手工艺品,这是该社区组
光明网 2023-03-28为有效预防和打击诈骗犯罪活动,提高辖区群众防范诈骗的意识,进一步维护人民群众根本利益,3月28日,郑州二七区长江路司法所协同长江路街道综
大河报网 2023-03-28产品3月27日3月28日涨跌单位:元 吨-10 柴油75507650100元 吨92 汽油84008500100元 吨95 汽油85508650100元 吨3月
生意社 2023-03-28有种重庆人的浪漫,叫追天逐月!
金台资讯 2023-03-28碳酸锂报价跌至263000元 吨今年迄今价格已经腰斩:据上海有色网发布的数据显示,今日锂电材料报价全线下跌,碳酸锂(99 5%电池级 国产)跌7000元 吨,报
和讯马金露 2023-03-28大连将30个中风险地区调整为低风险地区 ...
中新网成都11月22日电 (记者 刘忠俊)四...
(北京冬奥会)北京规划超270公里冬奥道路网...
中新网杭州11月22日电(郭其钰 张益聪)“...
中新网北京11月22日电 (记者 杜燕)今天...
山西长子“羊倌”养羊20余年 带动700余户养殖户发“羊财”
上海首个“两山”实践创新基地成功创建
广西医科大一附院向越南捐赠医疗防疫物资
黄埔海关破获案值5.5亿元走私进口二手挖掘机案
黄河内蒙古段今年首次出现流凌
Copyright 2015-2022 华东仓储网版权所有 备案号:京ICP备2022016840号-41 联系邮箱:2 913 236 @qq.com