一文带你深入理解Vue3响应式原理

目录
  • 响应式原理
  • 2.0的不足
  • reactive和effect的实现
  • effect track trigger
  • 测试代码
  • 递归实现reactive
  • 总结

响应式原理

Vue2 使用的是 Object.defineProperty  Vue3 使用的是 Proxy

2.0的不足

对象只能劫持 设置好的数据,新增的数据需要Vue.Set(xxx)  数组只能操作七种方法,修改某一项值无法劫持。

reactive和effect的实现

export const reactive = <T extends object>(target:T) => {
    return new Proxy(target,{
        get (target,key,receiver) {
          const res  = Reflect.get(target,key,receiver) as object

          return res
        },
        set (target,key,value,receiver) {
           const res = Reflect.set(target,key,value,receiver)

           return res
        }
    })
}

Vue3 的响应式原理依赖了 Proxy 这个核心 API,通过 Proxy 可以劫持对象的某些操作。

effect track trigger

实现effect 副作用函数

let activeEffect;
export const effect = (fn:Function) => {
     const _effect = function () {
        activeEffect = _effect;
        fn()
     }
     _effect()
}

使用一个全局变量 active 收集当前副作用函数,并且初始化的时候调用一下

实现track

const targetMap = new WeakMap()
export const track = (target,key) =>{
   let depsMap = targetMap.get(target)
   if(!depsMap){
       depsMap = new Map()
       targetMap.set(target,depsMap)
   }
   let deps = depsMap.get(key)
   if(!deps){
      deps = new Set()
      depsMap.set(key,deps)
   }

   deps.add(activeEffect)
}

执行完成成后我们得到一个如下的数据结构

实现trigger

export const trigger = (target,key) => {
   const depsMap = targetMap.get(target)
   const deps = depsMap.get(key)
   deps.forEach(effect=>effect())
}

当我们进行赋值的时候会调用 set 然后 触发收集的副作用函数

import {track,trigger} from './effect'

export const reactive = <T extends object>(target:T) => {
    return new Proxy(target,{
        get (target,key,receiver) {
          const res  = Reflect.get(target,key,receiver) as object

          track(target,key)

          return res
        },
        set (target,key,value,receiver) {
           const res = Reflect.set(target,key,value,receiver)

           trigger(target,key)

           return res
        }
    })
}

给 reactive 添加这两个方法

测试代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>

    <div id="app">

    </div>

    <script type="module">
        import { reactive } from './reactive.js'
        import { effect } from './effect.js'
        const user = reactive({
            name: "小满",
            age: 18
        })
        effect(() => {
            document.querySelector('#app').innerText = `${user.name} - ${user.age}`
        })

        setTimeout(()=>{
            user.name = '大满很厉害'
            setTimeout(()=>{
                user.age = '23'
            },1000)
        },2000)

    </script>
</body>

</html>

递归实现reactive

import { track, trigger } from './effect'

const isObject = (target) => target != null && typeof target == 'object'

export const reactive = <T extends object>(target: T) => {
    return new Proxy(target, {
        get(target, key, receiver) {
            const res = Reflect.get(target, key, receiver) as object

            track(target, key)

            if (isObject(res)) {
                return reactive(res)
            }

            return res
        },
        set(target, key, value, receiver) {
            const res = Reflect.set(target, key, value, receiver)

            trigger(target, key)

            return res
        }
    })
}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>

    <div id="app">

    </div>

    <script type="module">
        import { reactive } from './reactive.js'
        import { effect } from './effect.js'
        const user = reactive({
            name: "小满",
            age: 18,
            foo:{
                bar:{
                    sss:123
                }
            }
        })
        effect(() => {
            document.querySelector('#app').innerText = `${user.name} - ${user.age}-${user.foo.bar.sss}`
        })

        setTimeout(()=>{
            user.name = '大满很厉害'
            setTimeout(()=>{
                user.age = '23'
                setTimeout(()=>{
                    user.foo.bar.sss = 66666666
                },1000)
            },1000)
        },2000)

    </script>
</body>

</html>

总结

到此这篇关于Vue3响应式原理的文章就介绍到这了,更多相关Vue3响应式原理内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • vue.js数据响应式原理解析

    目录 Object.defineProperty() 定义 defineReactive 函数 递归侦测对象的全部属性 流程分析 observe 函数 Observer 类 完善 defineReactive 函数 One More Thing Object.defineProperty() 得力于 Object.defineProperty() 的特性,vue 的数据变化有别于 react 和小程序,是非侵入式的.详细介绍可以看 MDN 文档,这里特别说明几点: get / set 属性是函数

  • 关于Vue3中的响应式原理

    目录 一.简介 二.响应核心 1.核心源码 2.逐步分析上述示例代码 3.收集依赖和触发依赖更新 三.V3.2的响应式优化 四.后话 一.简介 本章内容主要通过具体的简单示例来分析Vue3是如何实现响应式的.理解本章需要了解Vue3的响应式对象.只注重原理设计层面,细节不做太多讲解. 二.响应核心 1.核心源码 export class ReactiveEffect<T = any> { //是否激活 active = true //依赖列表 deps: Dep[] = [] // can b

  • 浅析一下Vue3的响应式原理

    目录 Proxy Reflect 举个例子 reactive effect track trigger Proxy Vue3 的响应式原理依赖了 Proxy 这个核心 API,通过 Proxy 可以劫持对象的某些操作. const obj = { a: 1 }; const p = new Proxy(obj, {   get(target, property, receiver) {     console.log("get");     return Reflect.get(tar

  • 深入理解Vue3响应式原理

    目录 响应式原理 手写实现 1.实现Reactive 2.实现依赖的收集和触发 effect影响函数 收集/添加依赖 触发依赖 3.移除/停止依赖 衍生类型 1.实现readonly 2.实现shallowReadonly 3.实现ref 4.实现computed 工具类 响应式原理 利用ES6中Proxy作为拦截器,在get时收集依赖,在set时触发依赖,来实现响应式. 手写实现 1.实现Reactive 基于原理,我们可以先写一下测试用例 //reactive.spec.ts describ

  • Vue响应式原理与虚拟DOM实现步骤详细讲解

    目录 一.什么是响应式系统 二.实现原理 三.虚拟DOM实现 四.总结 一.什么是响应式系统 在Vue中,我们可以使用data属性来定义组件的数据.这些数据可以在模板中使用,并且当这些数据发生变化时,相关的DOM元素也会自动更新.这个过程就是响应式系统的核心.例如,我们在Vue组件中定义了一个count属性: <template> <div>{{ count }}</div> </template> <script> export default

  • Vue响应式原理模拟实现原理探究

    目录 前置知识 数据驱动 数据响应式的核心原理 Vue 2.x Vue 3.x 发布订阅和观察者模式 发布/订阅模式 观察者模式 Vue响应式原理模拟实现 Vue Observer对data中的属性进行监听 Compiler Watcher Dep 测试代码 前置知识 数据驱动 数据响应式——Vue 最标志性的功能就是其低侵入性的响应式系统.组件状态都是由响应式的 JavaScript 对象组成的.当更改它们时,视图会随即自动更新. 双向绑定——数据改变,视图改变:视图改变,数据也随之改变 数据

  • Vue响应式原理深入分析

    目录 1.响应式数据和副作用函数 2.响应式数据的基本实现 3.设计一个完善的响应式系统 1.响应式数据和副作用函数 (1)副作用函数 副作用函数就是会产生副作用的函数. function effect() { document.body.innerText = 'hello world.' } ​ 当effect函数执行时,它会设置body的内容,而body是一个全局变量,除了effect函数外任何地方都可以访问到,也就是说effect函数的执行会对其他操作产生影响,即effect函数是一个副

  • 一文带你搞懂Spring响应式编程

    目录 1. 前言 1.1 常用函数式编程 1.2 Stream操作 2. Java响应式编程 带有中间处理器的响应式流 3. Reactor 3.1 Flux & Mono 3.2 Flux Mono创建与使用 4. WebFlux Spring WebFlux示例 基于注解的WebFlux 基于函数式编程的WebFlux Flux与Mono的响应式编程延迟示例 总结 哈喽,大家好,我是指北君. 相信响应式编程经常会在各种地方被提到.本篇就为大家从函数式编程一直到Spring WeFlux做一次

  • 一文详解Vue3响应式原理

    目录 回顾 vue2.x 的响应式 vue3的响应式 Reflect 回顾 vue2.x 的响应式 实现原理: 对象类型:通过object.defineProperty()对属性的读取.修改进行拦截(数据劫持) 数组类型:通过重写更新数组的一系列方法来实现拦截(对数组的变更方法进行了包裹) Object.defineProperty(data,'count ",{ get(){}, set(){} }) 存在问题: 新增属性.删除属性,界面不会更新 直接通过下标修改数组,界面不会自动更新 但是

  • 详解Vue3的响应式原理解析

    目录 Vue2响应式原理回顾 Vue3响应式原理剖析 嵌套对象响应式 避免重复代理 总结 Vue2响应式原理回顾 // 1.对象响应化:遍历每个key,定义getter.setter // 2.数组响应化:覆盖数组原型方法,额外增加通知逻辑 const originalProto = Array.prototype const arrayProto = Object.create(originalProto) ;['push', 'pop', 'shift', 'unshift', 'splic

  • vue3 响应式对象如何实现方法的不同点

    目录 vue3响应式对象实现方法的不同点 Vue2和Vue3响应式原理对比 响应式原理实现逻辑 Vue2响应式原理简化 Vue2响应式原理弊端 Vue3响应式原理简化 vue3响应式对象实现方法的不同点 vue 响应式对象是利用 defindeProperty 实现,vue3 则是使用 Proxy 来实现响应式的. 二者,虽然都实现了 响应式的功能,但实现方式不一样,解决问题也有些不同. vue2 中,如果想要实现,数组元素的更新,则是需要 用到检测更新的方法 set 之类的,但 vue3的响应

  • 详解vue3 响应式的实现原理

    目录 核心设计思想 Vue.js 2.x 响应式 Vue.js 3.x 响应式 依赖收集:get 函数 派发通知:set 函数 总结 源码参考 核心设计思想 除了组件化,Vue.js 另一个核心设计思想就是响应式.它的本质是当数据变化后会自动执行某个函数,映射到组件的实现就是,当数据变化后,会自动触发组件的重新渲染.响应式是 Vue.js 组件化更新渲染的一个核心机制. Vue.js 2.x 响应式 我们先来回顾一下 Vue.js 2.x 响应式实现的部分: 它在内部通过 Object.defi

  • 一文带你搞懂Vue3的基本语法

    目录 1.通过 CDN 使用 Vue3 2.Vue3 模板语法 文本 Html 属性 表达式 指令 参数 3.模板用户输入双向绑定 1.通过 CDN 使用 Vue3 你可以借助 script 标签直接通过 CDN 来使用 Vue: <script src="https://unpkg.com/vue@next"></script> 通过 CDN 使用 Vue 时,不涉及“构建步骤”.这使得设置更加简单,并且可以用于增强静态的 HTML 或与后端框架集成 接下来我

  • 一文带你彻底理解Java序列化和反序列化

    Java序列化是什么? Java序列化是指把Java对象转换为字节序列的过程,Java反序列化是指把字节序列恢复为Java对象的过程. 反序列化: 客户端重文件,或者网络中获取到文件以后,在内存中重构对象. 序列化: 对象序列化的最重要的作用是传递和保存对象的时候,保证对象的完整性和可传递性.方便字节可以在网络上传输以及保存在本地文件. 为什么需要序列化和反序列化 实现分布式 核心在于RMI,可以利用对象序列化运行远程主机上的服务,实现运行的时候,就像在本地上运行Java对象一样. 实现递归保存

  • 一文带你真正理解Java中的内部类

    目录 概述 内部类介绍和分类 常规内部类 局部内部类 匿名内部类 静态内部类 静态内部类和普通内部类的区别 内部类的作用 概述 不知道大家在平时的开发过程中或者源码里是否留意过内部类,那有思考过为什么要有内部类,内部类都有哪几种形式,静态内部类和普通内部类有什么区别呢?本篇文章主要带领大家理解下这块内容. 内部类介绍和分类 顾名思义,内部类是指一个类在另外一个类的内部,是定义在另一个类中的类.根据类的位置和属性不同,可以分为下面几种. 常规内部类 @Data public class Tree

  • 一文带你深入理解Go语言中的sync.Cond

    目录 sync.Cond 是什么 适用场景 sync.Cond 的基本用法 NewCond 创建实例 Wait 等待条件满足 Signal 通知一个等待的 goroutine Broadcast 通知所有等待的 goroutine sync.Cond 使用实例 为什么要用 sync.Cond close channel 广播实例 sync.Cond 基本原理 sync.Cond 的设计与实现 sync.Cond 模型 notifyList 结构体 sync.Cond 的方法 Wait 方法 Si

随机推荐