vue2.0响应式原理,vue响应式原理实现
本文主要介绍vue3.0响应式功能的原理。Vue3的响应系统可以监控动态添加的属性、删除属性、索引数组和修改长度属性。另外,Vue3的响应式系统也可以单独作为一个模块使用。更多信息,请需要的小伙伴参考。
目录
1 .反应式2 . ref 3 . ref 4 .计算式前言:
Vue3改写了反应系统。与Vue2相比,底层是通过代理对象实现的。在初始化过程中,没有必要遍历所有属性,然后通过defineProperty将它们转换为get和set。另外,如果有多层嵌套的属性,只有访问一层属性时,才会递归处理下一层属性,所以Vue3中的响应式系统性能要优于Vue2。
接下来,我们将实现Vue3响应式系统的核心功能(反应式/参考式/参考式/计算式/效果式/跟踪式/触发式)来学习响应式原理。
首先,我们使用代理来实现响应中的第一个函数,reactive。
1.reactive
当reactive接收到一个参数时,它必须首先确定这个参数是否是一个对象。如果不是直接返回,reactive只能把对象转换成响应对象,这和ref不同。
然后,将创建拦截器对象处理程序,它包括get、set、deleteProperty等拦截方法。最后,将创建并返回代理对象。
//确定它是否是对象
const isObject=val=val!==null类型的值===object
//如果是对象,则调用reactive
const convert=target=is object(target)?反应(目标) :目标
//确定对象是否具有key属性。
const haown property=object . prototype . hasownproperty
const hasOwn=(target,key)=haOwnProperty.call(target,key)
导出函数反应(目标){
如果(!isObject(target)) {
//如果不是对象,直接返回
返回目标
}
常量处理程序={
get(目标,密钥,接收方){
//收集依赖项
const result=Reflect.get(target,key,receiver)
//如果属性是对象,则需要递归处理。
返回转换(结果)
},
集合(目标、关键字、值、接收者){
const old value=reflect . get(target,key,receiver)
设结果=真;
//需要判断当前传入的新值是否等于旧值,如果不等于,则覆盖旧值并触发更新。
if (oldValue!==值){
result=Reflect.set(target,key,value,receiver)
//触发更新.
}
set方法需要返回一个布尔值
返回结果;
},
deleteProperty (target,key) {
//首先判断当前目标是否有自己的key属性。
//如果有关键属性,删除它来触发更新
const hasKey=hasOwn(target,Key)
const result=reflect . delete property(target,key)
if (hasKey结果){
//触发更新.
}
返回结果;
}
}
返回新的代理(目标,处理程序)
}
至此,反应式函数已经写好了,接下来我们来写收集依赖关系的过程。
在依赖关系收集的过程中,创建了三个集合,即targetMap、depsMap和dep。
其中,targetMap用于记录目标对象和字典。他用的是weakMap,key是目标对象,targetMap的值是depsMap,类型是Map。这里的关键是目标对象的属性名,值是一个集合集合,集合中存储的元素是效果函数。因为可以多次调用同一个效果来访问效果中的同一个属性,所以这个属性会集合多个效果函数对应多个依赖关系。
一个属性可以对应多个效果函数。当触发更新时,可以通过属性找到相应的效果函数并执行。
这里,我们分别实现两个函数,效果和轨道。
效果函数接收一个函数作为参数。我们首先在外面设置一个变量来存储回调,这样track函数就可以访问回调了。
设activeEffect=null
导出函数效果(回调){
activeEffect=回调;
//访问响应对象属性并收集依赖关系。
回调();
//依赖项集合的结尾应设置为null。
activeEffect=null
}
track函数接收两个参数,即目标对象和属性,并将目标存储在targetMap中。您需要首先定义一个这样的映射。
let targetMap=new WeakMap()
导出功能跟踪(目标,关键){
//确定activeEffect是否存在
如果(!activeEffect) {
返回;
}
//depsMap存储对象和效果的对应关系。
let deps map=target map . get(target)
//如果不存在,则创建一个地图并存储在targetMap中
如果(!depsMap) {
targetMap.set(target,(depsMap=new Map()))
}
//根据属性找到对应的dep对象
let dep=depsMap.get(key)
//dep是用于存储属性对应的效果函数的集合。
如果(!副){
//如果不存在,创建一个新的集合并将其添加到depsMap
depsMap.set(key,(dep=new Set()))
}
dep.add(activeEffect)
}
Track是一个依赖于集合的函数。需要在反应函数的get方法中调用。
get(目标,密钥,接收方){
//收集依赖项
轨迹(目标,关键点)
const result=Reflect.get(target,key,receiver)
//如果属性是对象,则需要递归处理。
返回转换(结果)
},
这就完成了整个依赖集合。接下来会实现触发器更新,对应的函数是trigger,和track的过程正好相反。
触发器函数接收两个参数,即target和key。
导出函数触发器(目标,键){
const deps map=target map . get(target)
//如果没有找到,直接返回
如果(!depsMap) {
返回;
}
const dep=depsMap.get(key)
if (dep) {
dep.forEach(effect={
效果()
})
}
}
触发函数将在反应函数的set和deleteProperty中触发。
集合(目标、关键字、值、接收者){
const old value=reflect . get(target,key,receiver)
设结果=真;
//需要判断当前传入的新值是否等于旧值,如果不等于,则覆盖旧值并触发更新。
if (oldValue!==值){
result=Reflect.set(target,key,value,receiver)
//触发更新.
触发器(目标,键)
}
set方法需要返回一个布尔值
返回结果;
},
deleteProperty (target,key) {
//首先判断当前目标是否有自己的key属性。
//如果有关键属性,删除它来触发更新
const hasKey=hasOwn(target,Key)
const result=reflect . delete property(target,key)
if (hasKey结果){
//触发更新.
触发器(目标,键)
}
返回结果;
}
2.ref
Ref接收一个可以是原始值或对象的参数。如果是ref传入并创建的对象,则直接返回。如果是普通对象,会调用reactive创建一个响应式对象。否则,它将创建一个只有value属性的响应对象。
导出函数ref (raw) {
//判断raw是否是ref创建的对象,如果是,直接返回
if (isObject(raw) raw。__v__isRef) {
返回原始数据
}
convert函数之前已经定义过了。如果参数是对象,将调用反应函数来创建响应。
让value=convert(raw);
const r={
__v__isRef: true,
获取值(){
轨道(r,值)
返回值
},
设定值(新值){
//确定新值和旧值是否相等。
if (newValue!==值){
raw=新值
值=转换(原始)
//触发更新
触发器(r,值)
}
}
}
返回r
}
3.toRefs
ToRefs接收reactive函数返回的responsive对象,如果不是responsive对象则直接返回。将传入对象的所有属性转换为类似于ref返回的对象,并将转换后的属性挂载到新的对象上返回。
导出函数引用(代理){
//如果是数组,创建一个相同长度的数组,否则返回一个空对象
const ret=数组的代理实例?新数组(proxy.length) : {}
for(代理中的常量键){
ret[key]=toProxyRef(proxy,key)
}
返回ret
}
函数toProxyRef (proxy,key) {
const r={
__v__isRef: true,
Get value () {//这已经是一个响应式对象,所以不需要收集依赖关系。
返回代理[密钥]
},
设定值(新值){
代理[键]=新值
}
}
返回r
}
toRefs的作用其实就是让reactive中的每一个属性都有反应。reactive方法将创建一个responsive对象,但如果reactive返回的对象被解构,则不会使用该方法。
它有反应。toRefs的作用是支持解构并保持响应。
4.computed
然后模拟计算函数的内部实现。
Computed需要接收一个返回值作为参数的函数。该函数的返回值是计算属性的值。它需要监控函数内部响应数据的变化,最终返回函数执行的结果。
导出计算函数(getter) {
const result=ref()
效果(()=(result.value=getter()))
回送结果
}
Computed function会通过effect来监控getter内部响应数据的变化,因为当getter被effect执行时,会访问响应数据的属性来收集依赖关系。当数据改变时,效果函数将被重新执行,并且getter的结果将被存储在结果中。
这篇关于vue3.0响应式功能原理的详细文章到此为止。关于vue3.0响应式功能原理的更多信息,请搜索我们之前的文章或者继续浏览下面的相关文章。希望大家以后能多多支持我们!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。