vue3解读—reactivity响应式实现
前言:Vue3 中引入了proxy
进行数据劫持,而effect
是响应式系统的核心,而响应式系统又是 vue3
中的核心,所以vue3的解读要从 effect
开始讲起。
1.reactivity和effect的使用
目前vue3的各个模块都可以单独安装,首先我们需要安装npm i @vue/reactivity
,从中引入我们需要的方法,下面是我简单写一段测试代码。
const { reactive, effect } = require("@vue/reactivity");
let a = reactive({value: 1,
});
let b;
effect(() => {b = a.value + 10;console.log(b);
});
a.value = 10;
先别急着看我的输出,想想自己心中的答案是什么?
下面公布控制台的实际输出:
总结:我们会发现effect
函数执行了两次,一次是我们往effect
函数传入匿名函数时,它立即执行了一次;接下来是我们对响应式变量a.value
进行重新赋值时(或者说effect
内函数所依赖的响应式变量发生变化时)它又执行了一次。
接下来我们就好好思考下,如何设计一个reactive
方法,将传入的值变成响应式,以及实现一个effect
函数,其内函数所依赖的响应式数据变化时,该函数会再次执行。
2.Dep和effect的实现
reactive
会将传入的变量变成响应式数据,包括对象等数据;地基需要从下往上一层一层的搭建,我们这里先实现将单一变量编译成响应式数据,并用effct
对其监听。废话不多说,上代码:
第一步:
class Dep {constructor(val) {this._val = val;}get value() {return this._val;}set value(newVal) {this._val = newVal}
}
const dep = new Dep(10);
console.log('取值:', dep.value);
dep.value = 20;
console.log('赋值:', dep.value);
我们通过class
类去创建一个简单的变量,用其构造器constructor
初始化对象属性,使用setter
和getter
存取器拦截该属性的存取行为。此时dep
就是响应式数据吗?想啥呢!但我们会很熟悉后面的取存值行为,这不就是我们在vue3中使用ref
声明变量时,取值和赋值的写法吗!
有了这一步的铺垫,我们接下来的思路是不是更清晰了,只需要在初始化一个Dep
时,收集依赖该dep
的函数,并在dep
值发生变化时,再次执行这个依赖函数,是不是就能实现前面reactive
和effect
所达到的效果。ready go!
第二步:
let currentEffect;
class Dep {constructor(val) {this.effects = new Set(); // 储存依赖当前变量的函数,并去重this._val = val;}get value() {this.depend();return this._val;}set value(newVal) {this._val = newValthis.notice(); // 赋值时触发依赖}// 收集依赖depend() {if(currentEffect) { // 要记得判空this.effects.add(currentEffect);}}// 触发依赖notice() {this.effects.forEach(effect => {effect();});}
}
const effectWatch = (effect) => {currentEffect = effect;effect(); // 别忘了首先就会执行一次currentEffect = null;
}const dep = new Dep(10);
let b;
effectWatch(() => {b = dep.value + 10;console.log('effectWatch', b);
});
dep.value = 20;
为了讲解时方便区分,所以我在实现effect
时,重新取名叫effectWatch
;不墨迹了,赶紧看代码,相对于第一步,Dep类新增了两个方法,分别是储存依赖的函数和触发依赖的函数。首先effectWatch
在调用时就会执行一次依赖函数,并且是在effectWatch
的参数中取dep.value
值时,就会将当前依赖函数储存,当我们对dep.value
赋值时,会再次触发依赖的函数。
到此为止我们只是实现了一个简化单一的‘reactive’
和effect
,真正的reacive
可不仅仅只是传一个普通字符的功能,一起想想下一步该怎么做呢?
3.reactive实现
前面我们已经实现了简单地Dep
,它只是一个单一的变量,远远不能满足我们的开发需求。如果我们通过嵌套调用Dep
,是不是就能实现reactive
的功能了?而且开局我们已经了解到需要使用到proxy
对数据进行劫持,那么是不是就好实现多了。
第一步
const reactive = (raw) => {return new Proxy(raw, {get(target, key) {console.log('get----', target[key]);return Reflect.get(target, key);},set(target, key, value) {console.log('set----', key, value);return Reflect.set(target, key, value);}});
}
const user = reactive({name: '春赏百花冬观雪',
});
user.name;
user.age = 24;
user.age;
我们先科普下proxy
,就作者而言,很早就学习过该方法,但对于它的应用确实少的可怜(唯唯诺诺的小菜鸡)。proxy
就是在我们访问对象前添加了一层拦截,从而实现基本操作的拦截和自定义(我理解为过滤),而且proxy
常常与Reflect
成对出现,Reflect
也就是反射,它的出现简化了我们调用_Object对象_的代码,保持JS的简单,它们之间的基情还需要读者自行去了解哦。
对于user
我们可以理解为一个较为复杂的对象,此时的它的每一个属性key
所对应的值是不是相当于我前面的dep
呢,那么我们只需要再访问该key
值时将它通过Dep
初始化,从而使整个user
的任意属性值发生变化,那么所依赖的函数也能再次执行了,整个user
也就成了响应式对象了。从代码来看,第一步依旧是 利用存取器对该user
对象进行监听。
第二步
const targetMap = new Map(); // 用于搜集经过reactive初始化的变量
const getDep = (target, key) => {let depsMap = targetMap.get(target); // 从targetMap取,如果有的话if (!depsMap) { // 没有就先储存depsMap = new Map();targetMap.set(target, depsMap);}let dep = depsMap.get(key); // 并将dep与target的key建立连接if (!dep) {dep = new Dep();depsMap.set(key, dep);}return dep;
};
const reactive = (raw) => {return new Proxy(raw, {get(target, key) {const dep = getDep(target, key);dep.depend(); // 当我们访问对象每个属性时,都会收集依赖return Reflect.get(target, key);},set(target, key, value) {const dep = getDep(target, key);const result = Reflect.set(target, key, value); // 重新设置值之后dep.notice(); // 触发依赖return result; // 再return}});
};
const user = reactive({name: '春赏百花冬观雪',
});
effectWatch(() => {user.name;console.log('effect---', user.name);
});
user.name = '晓看天色暮观云';
这里要注意为什么接收Reflect.set
后再return
抛出,因为我们需要将user.name
的值更新后,紧接着触发我们收集到的依赖,最后才能抛出以完成set
。
作者也是前端小白一枚,只是通过自己的学习并记录以方便自己的回顾,也希望读者大佬们能提出宝贵意见,共同进步。
vue3解读—reactivity响应式实现相关推荐
- vue3之实现响应式数据ref和reactive
用途 ref.reactive都是vue3提供实现响应式数据的方法 ref() 接受一个内部值,返回一个响应式的.可更改的ref对象,此对象只有一个指向其内部的属性.value ref可以说是简化版的 ...
- 【Vue3中的响应式原理】
Vue3响应式原理 在Vue2的响应式中,存在着新增属性,删除属性以及直接通过下标修改数组,但页面不会自动更新的问题.但是在Vue3中,这些问题都得以解决. Vue2中的响应式原理 首先我们先看一下V ...
- vue3.0初体验(例子解读reactive响应式)
目录 准备 vue3 reactive原理例子重点讲解 vue3 reactive原理例子完整代码 准备: 下载vue-next 安装依赖 npm install 核心部分package,里面的vue ...
- Vue源码--解读vue响应式原理
原文链接:https://geniuspeng.github.io/2018/01/05/vue-reactivity/ Vue的官方说明里有深入响应式原理这一节.在此官方也提到过: 当你把一个普通的 ...
- Vue3通透教程【五】Vue3中的响应式数据 reactive函数、ref函数
文章目录
- Vue3.0源码解读 - 响应式系统
一.目标对象标识 ** 类似于渲染系统,vue3.0的响应式系统也有自己的一套flag,用于标记目标对象target(通常是我们传入的数据源)的一些特性 export const enum React ...
- vue3.0 Beta发布,顺便来看看Vue3 的响应式和以前有什么区别
前言 vue 3.0 Beta 测试版发布 可以通过vue-cli-plugin-vue-next创建一个vue3.0的beta项目. 目前尚不支持IE11,后期可能会处理兼容版本. 对vue3源码有 ...
- 彻底理解Vue数据响应式原理
彻底理解Vue数据响应式原理 当创建了一个实例后,通过将数据传入实例的data属性中,会将数据进行劫持,实现响应式,也就是说当这些数据发生变化时,对应的视图也会发生改变. const data = { ...
- react全局状态管理_rxv: 在React中用Vue3的reactivity包实现状态管理。
前言 React的状态管理是一个缤纷繁杂的大世界,光我知道的就不下数十种,其中有最出名immutable阵营的redux,有mutable阵营的mobx,react-easy-state,在hooks ...
最新文章
- vs2010发布、打包安装程序(超全超详细)
- 高校邦python程序设计基础篇_高校邦Python程序设计基础【实境编程】章节答案
- Greenplum5单机部署连接报错 System was started in master-only utility mode问题修复
- 一元三次方程求解(信息学奥赛一本通-T1238)
- 信息学奥赛C++语言: 判决素数个数
- 多iframe下的html同名id,获得同级iframe页面的指定ID元素的几种实现方法
- java 蓝桥杯 算法训练 区间k大数查询(题解)
- listview-android:打造万能通用适配器(转)
- Node-ipc 热门包作者投毒“社死‘’,谁来保护开源软件供应链安全?
- mongodb 启动 WARNING: soft rlimits too low, transparent_hugepage/enabled is 'always'. never
- seaborn无法下载数据的问题
- python调用通达信函数_Funcat 将同花顺、通达信等的公式写法移植到了 Python 中
- Spire.Doc for Java-根据表格模板生成word表格
- 高德导航过程中实时获取道路信息
- 关于XD卡写保护问题!
- Unity资源处理机制(Assets/WWW/AssetBundle/...)读取和加载资源方式详解
- 中美跨境电商贸易投资云洽会成功举办;TT Shop和TTforBusiness将互通;PhonePe月破20亿...|洞悉跨境
- 固网服务器win7系统驱动,固网HU-1608n驱动
- 使用VNC远程连接云服务器,连接超时问题
- 大数据在金融领域主要面临哪些风险,应该怎么解决?
热门文章
- 大二女生web开发成长之路——讲述我从软妹子到女汉子的进阶过程
- 基于规则的中文地名识别系统的设计与实现
- 云闪付怎么对接三方php,第三方支付-银联云闪付开发教程
- 【随手记】fatal: cannot do a partial commit during a merge. 解决
- Redis基础(二)—— 基本命令与数据类型
- python 安卓模拟器 抓包_python + 爬虫 + fiddler + 夜神模拟器 爬取app(1)
- 华为云CDN的初次实践总结
- 解决Android 模拟机开机黑屏问题、npm内存溢出问题
- RTX4060参数 RTX4060功耗 RTX4060 显卡性能
- redis中使用lua脚本