vue2.0响应式原理,vue响应式原理实现

  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的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: