谁有做网站比较厉害的,wordpress flashfxp,做阿里巴巴网站费用吗,内蒙古建设工程造价信息网官网入口提示#xff1a;看到我 请让我滚去学习 文章目录 vue3源码剖析reactivereactive使用proxy代理一个对象1.首先我们会走isObject(target)判断#xff0c;我们reactive全家桶仅对对象类型有效#xff08;对象、数组和 Map、Set 这样的集合类型#xff09;#xff0c;而对 str… 提示看到我 请让我滚去学习 文章目录 vue3源码剖析reactivereactive使用proxy代理一个对象1.首先我们会走isObject(target)判断我们reactive全家桶仅对对象类型有效对象、数组和 Map、Set 这样的集合类型而对 string、number 和 boolean 这样的 原始类型 无效。2.判断是否已经代理返回对象本身。3.判断在对象的代理对象在weakmap中是否已经存在是的话返回存储的代理对象4.判断对象是否为可扩展对象5.使用new proxy代理对象 reactive使用proxy代理的陷阱封装get陷阱代码分析1.首先在get方法中局部化isReadonly和isShallow标识然后走ifelse判断返回相应的值2.判断是目标对象是否是数组如果是数组并且访问的是数组的一些方法那么返回对应的方法3.判断是否访问对象上的hasOwnProperty属性返回对象原型上的方法并收集依赖4.最后如果是内置stmbol或者是不可追踪的key直接返回res不进行依赖收集5.如果不是只读调用进行依赖收集触发track6.如果是浅层代理不需要对访问的属行进行深层代理返回访问属性值7.访问属性若是对象那么就递归将子元素也变成代理对象 set陷阱分析1.拿取当前值和旧值判断是否目标对象是只读对象若是不做任何处理返回false2.通过hadKey判断操作类型类型是修改旧属性还是新增属性在副作用函数触发时做不同处理3.对比新旧值触发依赖收集 deleteProperty、has、ownKeys陷阱 track函数trigger函数 vue3源码剖析
vue代码以模块形式放置在packages文件夹下分模块打包可以使用treesharking可以在项目中只应用需要的模块甚至我们可以只使用单一模块实现相应功能例如我只使用reactive模块实现和拓展响应式数据。(monorepo) reactive
我们这次学习的响应式相关api都在reactive文件夹中那么就让我们看看reactive-api在vue中是怎么实现的
reactive使用proxy代理一个对象 在reactive文件中我们以上4个方法都是使用createReactiveObject高阶函数实现参入不同的方法。这是因为我们vue官网的reactive、shallowReactive、redonly、shallowReadonly都是使用这个方法实现的让我们看看这个函数做了什么处理 这个函数传入5个参数 target:目标对象target isReadonly:布尔值isReadonly表示是否创建只读对象 baseHandlers:基础处理器baseHandlers用于普通对象的代理处理 collectionHandlers:集合处理器collectionHandlers专门用于处理如数组和Map等集合类型的代理 proxyMap:用于存储代理映射的WeakMap
1.首先我们会走isObject(target)判断我们reactive全家桶仅对对象类型有效对象、数组和 Map、Set 这样的集合类型而对 string、number 和 boolean 这样的 原始类型 无效。
因为底层使用proxy代理proxy只能代理对象确定目标是否为可被观察的类型,如果代理目标不是对象直接返回目标本身dev环境控制台warn
2.判断是否已经代理返回对象本身。
isReadonly传入参数是否只读 target[ReactiveFlags.IS_REACTIVE]判断对象是否已经被代理代理对象会拦截ReactiveFlags中属性(特殊字符串)如果有值说明已被代理
ReactiveFlags是vue在对象上的标识我们可以在传入目标上直接加上相应属性会影响数据的绑定例如 上图这样会影响reactive的正确执行。
3.判断在对象的代理对象在weakmap中是否已经存在是的话返回存储的代理对象 4.判断对象是否为可扩展对象 getTargetType函数会根据传入对象返回相应code码
1------传入对象是Object或者Array类型2------传入对象是map、set类型 这两者在proxy陷阱中处理方式不同0------传入对象是不可扩展对象那么就不用代理
Object.isExtensible(value) 方法会返回 true 当
对象是可扩展的默认情况下JavaScript 中的对象是可扩展的这意味着你可以向它们添加新的属性。如果一个对象没有被冻结Object.freeze()或密封Object.seal()那么 Object.isExtensible(value) 将返回 true。Object.freeze()(不可写,不可配置,可枚举,不可描述) Object.freeze()方法可以冻结一个对象。 对象没有被设置为不可扩展如果对象在创建后没有通过 Object.preventExtensions() 方法使其变得不可扩展那么它依然可扩展。 对象不是原始值Object.isExtensible() 只能用于对象如果 value 是一个原始值如字符串、数字、布尔值、null 或 undefined该方法会抛出错误因此在这种情况下不会返回 true 或 false。 markRaw()-api在Vue3.0中的作用是标记一个对象使其永远不会再成为响应式对象。其给对象属性赋值ReactiveFlags.SKIP为true那么再使用reactive给次对象做响应式时默认就会识别为不可扩展对象不会在做响应式代理
5.使用new proxy代理对象
通过targetType TargetType.COLLECTION判断对象是否为集合类型走collectionHandlers或者baseHandlers陷阱函数并将代理对象存储在proxyMap中 到这里我们得到了proxy对象那么接下来我们看看传入的这个baseHandlers做了什么
reactive使用proxy代理的陷阱封装
baseHandlers即传入函数mutableHandlers
get陷阱代码分析
MutableReactiveHandle对proxy的get、set、deleteProperty、has、ownKeys陷阱做了相应代理这些方法我们上一篇都介绍过继承了BaseReactiveHandler公用类事实上 mutableHandlers,readonlyHandlers,shallowReactiveHandlers, shallowReadonlyHandlers都继承自这个基础类这个基础类定义了公用的get陷阱依照我们上一篇实现的简易版本的reactive中我们猜测陷阱一个做了两件事1.返回访问值 2.收集依赖源码如下
1.首先在get方法中局部化isReadonly和isShallow标识然后走ifelse判断返回相应的值
这也就是我们上面代理时为什么可以从ReactiveFlags特殊属性做判断可以看出是在这里对特殊属性做了相应拦截
2.判断是目标对象是否是数组如果是数组并且访问的是数组的一些方法那么返回对应的方法 Vue3中使用arrayInstrumentations对数组的部分方法做了处理为什么要这么做呢 这个方法可以分为2部分
1.对includes、indexOf、lasetindexOf方法进行拦截重写先调用了数组原型方法进项查找如果没找到将查找对象的原型又查找了一次,为什么这么做我们来看示例代码
1.
let obj{}
let arrreactive([obj])
console.log(arr.includes(obj)) ///不重写includes方法输出 false我们在代理对象arr中去查找obj原始数据但是reactive在代理[obj]也会递归把obj对象进行代理这样会导致arr中存储的其实是proxy对象在arr中找obj会找不到所以要把arr使用toRaw在arr原始数据上找2.
let obj {a:1}
let obj2 reactive(obj)
let arr reactive([obj])
console.log(arr.includes(obj2)) ///不重写includes方法输出 false然后如果是这种在arr原型上也是obj原始数据找代理对象obj2也找不到会进入逻辑res-1||resfalse,将obj2也使用toRaw得到原始数据再次查找一遍。2.对push, “pop”, “shift”, “unshift”, splice方法进行重写上一篇中我们提到这些方法会隐式的修改数组长度而这就会触发length的收集依赖这显然不是我们想要的所以在运行这些方法时需要暂停依赖收集
例如:在两个独立的effect中修改长度不仅会读取‘length’还会设置‘lenth’这样就会溢栈
const arrreactive([])
// 第一个副作用函数
effect(() {arr.push(111)})// 第二个副作用函数effect(() {arr.push(1222)})当然vue3中还有running状态检测如果当前effect函数还在运行就不会再次运行这也使得但是还是会走依赖收集和触发只是不会再溢栈而已,但是length属性还是会收集当前effect当你再次修改length属性时会再次触发effect。
effect(() {arr.push(111)})
effect(() {
arr.push(1222)})
setTimeout(() {arr.pop()console.log( ~ setTimeout ~ arr:, arr)
}, 1000);
// ~ setTimeout ~ arr:[111,1222,111,1222]3.判断是否访问对象上的hasOwnProperty属性返回对象原型上的方法并收集依赖 4.最后如果是内置stmbol或者是不可追踪的key直接返回res不进行依赖收集
这一步是为了过滤一些特殊的属性例如原生的Symbol类型的属性如Symbol.iterator、Symbol.toStringTag等等这些属性不需要进行依赖收集因为它们是内置的不会改变还有一些不可追踪的属性如proto、__v_isRef、__isVue这些属性也不需要进行依赖收集
5.如果不是只读调用进行依赖收集触发track 6.如果是浅层代理不需要对访问的属行进行深层代理返回访问属性值
### 6.如果是浅层代理不需要### 6.如果访问属性是一个已经使用ref代理的对象对属性值进行.value结构
7.访问属性若是对象那么就递归将子元素也变成代理对象 set陷阱分析
当我们看完set我们知道它主要做了访问数据返回和依赖收集那么我们之前实现的set中应该是数据修改和副作用函数触发
1.拿取当前值和旧值判断是否目标对象是只读对象若是不做任何处理返回false 2.通过hadKey判断操作类型类型是修改旧属性还是新增属性在副作用函数触发时做不同处理 3.对比新旧值触发依赖收集 (target toRaw(receiver))此处判断如果目标是原创原型链中的某个上游则不要触发。
例如
const obj{}
const proto{bar:1}
const childreactive(obj)
const parentreactive(proto)
Object.setPrototypeOf(child, parent)
effect((){console.log( ~ child:, child.bar)})
child.bar 2
// ~ child:,1
// ~ child:,1
//在effect访问child.barchild不存在就去原型链找找到parent.bar,但是parent是响应式对象这样parent.bar和effect就建立联系了所以第一次依赖收集收集了child.bar和parent.bar。而对对象设置属性如果对象不存在此属性就会找到这个对象的原型触发原型的[set]内部方法即parent的[set]方法所以会被拦截到这样就解释了为什么会触发2次deleteProperty、has、ownKeys陷阱
deleteProperty、has陷阱都是常规去收集和触发副作用函数而ownKeys是有些特别的。 ownKeys在对象或数组for…in遍历时触发而我们遍历重新触发的条件为数组或对象key长度改变对象变量在get中我们可以清楚的知道我们要获取的是哪个属性但是ownKeys中并不能所以我们在track函数传入ITERATE_KEY作为key const data [1,2,3,4,{a:111}]const obj reactive(data)watchEffect(function effectFn111 () {console.log(11111)for (const key in obj) {}})obj.a6//11111//11111tigger函数中对象新增和删除属性都会影响for…infor…in依赖的对象key为ITERATE_KEY所以都要重新执行ITERATE_KEY的副作用函数执行当判断对象新增删除值时都要重新执行key为ITERATE_KEY的副作用函数即重新运行for…in存在的副作用函数
到此我们reactive使用了new Proxy代理了对象返回了一个代理对象实现了对对象属性访问、更改的拦截那我们在看下track依赖收集和trigger函数依赖触发
track函数
track函数就如我们上一篇中将对象-对象属性map-efftct副作用函数map关联存储在了targetMap全局的weakMap中结构我们非常熟悉。 其中值得一提的是在创建dep时使用的是createDep这个方法如下这是为了给dep上挂载一个清除自身的函数。例如当我们这个属性的effects依赖为0时即这个dep没有依赖那么我们就可以从调用此方法将属性从tagerMap表上面将其删除。 。
当然还有一些其他操作例如shouldTrack判断是否收集依赖我们上面重写数组push等方法是会暂停收集就是pauseTracking函数更改了这个全局属性来暂停依赖收集resetTracking重新开启收集依赖。还有一些其他参数和effect函数相关我们看effect函数时在细说
trigger函数
tigger函数会读取 track函数收集到的在访问属性上绑定的effect副作用函数加入一个队列中循环执行这样当前修改属性所有依赖都会重新执行更新。 当然在其中也会有一些其他操作例如我们上一篇说数组直接修改length属性会隐式的改变数组内元素那么就需要修改属性’length’时对于数组中所有索引大于等于新长度的元素全部进行副作用触发还有执行元素新增、删除操作时触发ITERATE_KEY即对象for…in循环收集的副作用函数。 还有一些其他参数和effect函数相关我们看effect函数时在细说 总结vue3的reactive能够代理对象reactive、shallowReactive、redonly、shallowReadonly都是使用同一个高阶函数实现在数据访问时收集依赖数据修改时触发依赖重新执行。其中做了很多的操作判断保证其能够正常运行例如对数组一些方法的特殊等。