广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >vue3.0响应式函数原理详细
  • 581
分享到

vue3.0响应式函数原理详细

2024-04-02 19:04:59 581人浏览 薄情痞子
摘要

目录1.Reactive2.ref3.toRefs4.computed前言: vue3重写了响应式系统,和Vue2相比底层采用Proxy对象实现,在初始化的时候不需要遍历所有的属性再

前言:

vue3重写了响应式系统,和Vue2相比底层采用Proxy对象实现,在初始化的时候不需要遍历所有的属性再把属性通过defineProperty转换成get和set。另外如果有多层属性嵌套的话只有访问某个属性的时候才会递归处理下一级的属性所以Vue3中响应式系统的性能要比Vue2好。

接下来我们自己实现Vue3响应式系统的核心函数(reactive/ref/toRefs/computed/effect/track/trigger)学习一下响应式原理。

首先我们使用Proxy来实现响应式中的第一个函数reactive

1.reactive

reactive接收一个参数,首先要判断这个参数是否是一个对象,如果不是直接返回,reactive只能将对象转换成响应式对象,这是和ref不同的地方。

接着会创建拦截器对象handler, 其中抱哈get,set,deleteProperty等拦截方法,最后创建并返回Proxy对象。

// 判断是否是一个对象
const isObject = val => val !== null && typeof val === 'object'
// 如果是对象则调用reactive
const convert= target => isObject(target) ? reactive(target) : target
// 判断对象是否存在key属性
const haOwnProperty = Object.prototype.hasOwnProperty
const hasOwn = (target, key) => haOwnProperty.call(target, key)

export function reactive (target) {
    if (!isObject(target)) {
        // 如果不是对象直接返回
        return target
    }

    const handler = {
        get (target, key, receiver) {
            // 收集依赖
            const result = Reflect.get(target, key, receiver)
            // 如果属性是对象则需要递归处理
            return convert(result)
        },
        set (target, key, value, receiver) {
            const oldValue = Reflect.get(target, key, receiver)
            let result = true;
            // 需要判断当前传入的新值和oldValue是否相等,如果不相等再去覆盖旧值,并且触发更新
            if (oldValue !== value) {
                result = Reflect.set(target, key, value, receiver)
                // 触发更新...
            }
            // set方法需要返回布尔值
            return result;
        },
        deleteProperty (target, key) {
            // 首先要判断当前target中是否有自己的key属性
            // 如果存在key属性,并且删除要触发更新
            const hasKey = hasOwn(target, key)
            const result = Reflect.deleteProperty(target, key)
            if (hasKey && result) {
                // 触发更新...
            }
            return result;
        }
    }
    return new Proxy(target, handler)
}

至此reactive函数就写完了,接着我们来编写一下收集依赖的过程。

在依赖收集的过程会创建三个集合,分别是targetMap,depsMap以及dep

其中targetMap是用来记录目标对象和字典他使用的是weakMap,key是目标对象,targetMap的值是depsMap, 类型是Map,这里面的key是目标对象的属性名称,值是一个Set集合,集合中存储的元素是Effect函数。因为可以多次调用同一个Effect在Effect访问同一个属性,这个时候这个属性会收集多次依赖对应多个Effect函数。

一个属性可以对应多个Effect函数,触发更新的时候可以通过属性找到对应的Effect函数,进行执行。

我们这里分别来实现一下effecttrack两个函数。

effect函数接收一个函数作为参数,我们首先在外面定一个变量存储callback, 这样track函数就可以访问到callback了。

let activeEffect = null;
export function effect (callback) {
    activeEffect = callback;
    // 访问响应式对象属性,收集依赖
    callback();
    // 依赖收集结束要置null
    activeEffect = null;
}

track函数接收两个参数目标对象和属性, 他的内部要将target存储到targetMap中。需要先定义一个这样的Map。

let targetMap = new WeakMap()

export function track (target, key) {
    // 判断activeEffect是否存在
    if (!activeEffect) {
        return;
    }
    // depsMap存储对象和effect的对应关系
    let depsMap = targetMap.get(target)
    // 如果不存在则创建一个map存储到targetMap中
    if (!depsMap) {
        targetMap.set(target, (depsMap = new Map()))
    }
    // 根据属性查找对应的dep对象
    let dep = depsMap.get(key)
    // dep是一个集合,用于存储属性所对应的effect函数
    if (!dep) {
        // 如果不存在,则创建一个新的集合添加到depsMap中
        depsMap.set(key, (dep = new Set()))
    }
    dep.add(activeEffect)
}

track是依赖收集的函数。需要在reactive函数的get方法中调用。

get (target, key, receiver) {
    // 收集依赖
    track(target, key)
    const result = Reflect.get(target, key, receiver)
    // 如果属性是对象则需要递归处理
    return convert(result)
},

这样整个依赖收集就完成了。接着就要实现触发更新,对应的函数是trigger,这个过程和track的过程正好相反。

trigger函数接收两个参数,分别是target和key。

export function trigger (target, key) {
    const depsMap = targetMap.get(target)
    // 如果没有找到直接返回
    if (!depsMap) {
        return;
    }
    const dep = depsMap.get(key)
    if (dep) {
        dep.forEach(effect => {
            effect()
        })
    }
}

trigger函数要在reactive函数中的setdeleteProperty中触发。

set (target, key, value, receiver) {
    const oldValue = Reflect.get(target, key, receiver)
    let result = true;
    // 需要判断当前传入的新值和oldValue是否相等,如果不相等再去覆盖旧值,并且触发更新
    if (oldValue !== value) {
        result = Reflect.set(target, key, value, receiver)
        // 触发更新...
        trigger(target, key)
    }
    // set方法需要返回布尔值
    return result;
},
deleteProperty (target, key) {
    // 首先要判断当前target中是否有自己的key属性
    // 如果存在key属性,并且删除要触发更新
    const hasKey = hasOwn(target, key)
    const result = Reflect.deleteProperty(target, key)
    if (hasKey && result) {
        // 触发更新...
        trigger(target, key)
    }
    return result;
}

2.ref

ref接收一个参数可以是原始值也可以是一个对象,如果传入的是对象并且是ref创建的对象则直接返回,如果是普通对象则调用reactive来创建响应式对象,否则创建一个只有value属性的响应式对象。

export function ref (raw) {
    // 判断raw是否是ref创建的对象,如果是直接返回
    if (isObject(raw) && raw.__v__isRef) {
        return raw
    }

    // 之前已经定义过convert函数,如果参数是对象就会调用reactive函数创建响应式
    let value = convert(raw);

    const r = {
        __v__isRef: true,
        get value () {
            track(r, 'value')
            return value
        },
        set value (newValue) {
            // 判断新值和旧值是否相等
            if (newValue !== value) {
                raw = newValue
                value = convert(raw)
                // 触发更新
                trigger(r, 'value')
            }
        }
    }

    return r
}

3.toRefs

toRefs接收reactive函数返回的响应式对象,如果不是响应式对象则直接返回。将传入对象的所有属性转换成一个类似ref返回的对象将准换后的属性挂载到一个新的对象上返回。

export function toRefs (proxy) {
    // 如果是数组创建一个相同长度的数组,否则返回一个空对象
    const ret = proxy instanceof Array ? new Array(proxy.length) : {}

    for (const key in proxy) {
        ret[key] = toProxyRef(proxy, key)
    }

    return ret;
}

function toProxyRef (proxy, key) {
    const r = {
        __v__isRef: true,
        get value () { // 这里已经是响应式对象了,所以不需要再收集依赖了
            return proxy[key]
        },
        set value (newValue) {
            proxy[key] = newValue
        }
    }
    return r
}

toRefs的作用其实是将reactive中的每个属性都变成响应式的。reactive方法会创建一个响应式的对象,但是如果将reactive返回的对象进行解构使用就不再

是响应式了,toRefs的作用就是支持解构之后仍旧为响应式。

4.computed

接着再来模拟一下computed函数的内部实现

computed需要接收一个有返回值的函数作为参数,这个函数的返回值就是计算属性的值,需要监听函数内部响应式数据的变化,最后将函数执行的结果返回。

export function computed (getter) {
    const result = ref()

    effect(() => (result.value = getter()))

    return result
}

computed函数会通过effect监听getter内部响应式数据的变化,因为在effect中执行getter的时候访问响应式数据的属性会去收集依赖,当数据变化会重新执行effect函数,将getter的结果再存储到result中。

到此这篇关于vue3.0响应式函数原理详细的文章就介绍到这了,更多相关vue3.0响应式函数原理内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: vue3.0响应式函数原理详细

本文链接: https://www.lsjlt.com/news/139081.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • vue3.0响应式函数原理详细
    目录1.reactive2.ref3.toRefs4.computed前言: Vue3重写了响应式系统,和Vue2相比底层采用Proxy对象实现,在初始化的时候不需要遍历所有的属性再...
    99+
    2022-11-13
  • vue3.0响应式函数原理是什么
    这篇文章将为大家详细讲解有关vue3.0响应式函数原理是什么,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。前言:Vue3重写了响应式系统,和Vue2相比底层采用Proxy对象实现,在初始化的时候不需要遍历...
    99+
    2023-06-29
  • 详解VUE响应式原理
    目录1、响应式原理基础2、核心对象:Dep与Watcher3、收集依赖与更新依赖3.1 收集依赖3.2 更新依赖4、源码调试4.1 测试的页面代码1、对象说明2、Dep与Watche...
    99+
    2022-11-12
  • 详解vue数据响应式原理之数组
    目录src/core/observer/index.jssrc/core/observer/array.js arrayMethods总结src/core/observer/inde...
    99+
    2022-11-13
  • 一文详解Vue3响应式原理
    目录回顾 vue2.x 的响应式vue3的响应式回顾 vue2.x 的响应式 实现原理: 对象类型:通过object.defineProperty()对属性的读取、修改进行拦截(数据...
    99+
    2022-11-13
  • Vue响应式原理与虚拟DOM实现步骤详细讲解
    目录一、什么是响应式系统二、实现原理三、虚拟DOM实现四、总结一、什么是响应式系统 在Vue中,我们可以使用data属性来定义组件的数据。这些数据可以在模板中使用,并且当这些数据发生...
    99+
    2023-05-13
    Vue响应式原理 Vue虚拟DOM
  • 详解vue3 响应式的实现原理
    目录核心设计思想Vue.js 2.x 响应式Vue.js 3.x 响应式依赖收集:get 函数派发通知:set 函数总结源码参考核心设计思想 除了组件化,Vue.js 另一个核心设计...
    99+
    2022-11-13
  • Vue响应式系统的原理详解
    目录vue响应式系统的基本原理1.回顾一下Object.defineProperty的用法2.实战1:使用 Object.defineProperty 对 person的age属性 ...
    99+
    2022-11-12
  • Vue响应式原理的示例详解
    Vue 最独特的特性之一,是非侵入式的响应系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。聊到 Vue 响应式实现原理,众多开发者都知道实现...
    99+
    2022-11-13
  • VUE响应式原理的实现详解
    目录总结前言 相信vue学习者都会发现,vue使用起来上手非常方便,例如双向绑定机制,让我们实现视图、数据层的快速同步,但双向绑定机制实现的核心数据响应的原理是怎么样的呢,接下来让我...
    99+
    2022-11-13
  • Vue3 Reactive响应式原理逻辑详解
    目录前言一、怎么实现变量变化二、怎么实现变量变化三、将多个dep存储在Map中四、将多个object的depsMap继续存储起来五、核心六、源码解析(TypeScript)前言 本篇...
    99+
    2022-11-13
  • 详解Vue3的响应式原理解析
    目录Vue2响应式原理回顾Vue3响应式原理剖析嵌套对象响应式避免重复代理总结 Vue2响应式原理回顾 // 1.对象响应化:遍历每个key,定义getter、setter //...
    99+
    2022-11-12
  • 一文详解Vue中响应式原理
    废话不多说,直接进入正题,响应式在日常开发中的应用是很常见的,这里举个简单的例子:let a=3 let b=a*10 console.log(b)//30 a=4 console.log(b)//40这时候我们想让b=4*10,这样显然是...
    99+
    2023-05-14
    前端 Vue.js 设计模式
  • 一文详解Vue2/Vue3的响应式原理
    this.$delete(this.student, 'name');// 删除student对象属性name this.$set(this.student, 'age', '21');// ...
    99+
    2023-05-14
    Vue.js
  • vue.js数据响应式原理解析
    目录Object.defineProperty()定义 defineReactive 函数递归侦测对象的全部属性流程分析observe 函数Observer 类完善 defineRe...
    99+
    2022-11-13
    vue.js数据响应式原理 vue.js数据响应式
  • Vue数据响应式原理是什么
    本文小编为大家详细介绍“Vue数据响应式原理是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“Vue数据响应式原理是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。响应式是什么简而言之就是数据变页面变如何实...
    99+
    2023-06-30
  • Vue深入讲解数据响应式原理
    目录响应式是什么如何实现数据响应式实现对象属性拦截通用的劫持方案总结响应式是什么 简而言之就是数据变页面变 如何实现数据响应式 在Javascript里实现数据响应式一般有俩种方案,...
    99+
    2022-11-13
  • Vue 中数据响应式的原理是什么
    Vue 中数据响应式的原理是什么,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。改造数据我们先来尝试写一个函数,用于改造对象:为什么要先写这个函...
    99+
    2022-10-19
  • Vue数据响应式原理实例代码分析
    本文小编为大家详细介绍“Vue数据响应式原理实例代码分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“Vue数据响应式原理实例代码分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。改造数据我们先来尝试写一个函数...
    99+
    2023-07-04
  • vue数据响应式原理中数组的示例分析
    这篇文章主要介绍vue数据响应式原理中数组的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!src/core/observer/index.jssrc/core/observer/array.js arrayM...
    99+
    2023-06-29
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作