首页 > 其他分享 >Vue3响应式概念

Vue3响应式概念

时间:2023-12-16 17:32:28浏览次数:36  
标签:function target 概念 watcher 响应 effects key Vue3 函数

响应式的基本概念

响应式是指当数据发生变化时,系统会自动更新与数据相关的 DOM 结构。

在 Vue2 中,响应式系统的实现基于 Object.defineProperty。然而,Object.defineProperty 有一些局限,如:无法监听数组的变化、需要遍历对象的每个属性进行监听、性能开销较大。

在 Vue3 中,响应式系统的实现基于 ES6 的 Proxy 对象。Proxy 可以直接监听对象和数组的变化,而无需对每个属性进行监听,从而大大提高性能。同时,Proxy 也可以解决 Object.defineProperty 无法监听数组的问题。

响应式的关键在于vue的依赖收集机制。


简化模型

为了更直观的理解vue依赖收集的模型,我们先来看一个“简单”的功能描述:

已知watcher函数,调用了一些“外部函数”:

function watcher () {
    console.log('watcher start')
    函数1(); 
    函数2();
    console.log('watcher end')
}

能否设计一个依赖收集系统,使这些“外部函数”运行时,watcher也会随之运行?


关键:如何判断函数间的调用关系?

看似有点难,实际一点也不简单,我们需要知道函数间调用关系。我们先看个例子:

function A() { console.log('A') }
function B() { console.log('B') }
function C() { console.log('C') }
...


function watcher () {
    console.log('watcher start!')
    /* *这里调用了上面的某些函数* */
    console.log('watcher end!')
}
/* *这里运行了某些函数* */
watcher();






- watcher start!
- A
- B
- wathcer end! 
- C

运行结果我们可以看出watcher内部一定调用了A、B函数:

为啥?js是单线程的。

C函数一定在watcher外面吗?不一定。例如:

function watcher () { console.log('start') A() B() setTimeout(()=>{ C() }) console.log('end') } watcher();

C函数这种咋办?不管!我们只管肯定没问题的!

我们由此可以确定


函数watcher执行期间,凡是运行过的函数,一定是watcher内部调用过的函数

根据这个原理,我们设计依赖收集系统如下:


// 当前的监听函数
let activeEffect = null
// 副作用函数
function effect (watcher) {
    activeEffect = watcher
    // watcher执行的期间就是依赖收集的阶段
    watcher(true)
    activeEffect= null
}
// isTracking:是否是依赖收集阶段
function A (isTracking = false) {
    if (isTracking) {
        // 依赖收集阶段,effects就是A的监听函数集合
        A.effects = A.effects || new Set()
        A.effects.add(activeEffect)
    } else {
        // 依赖运行阶段
        console.log('A触发了')
        A.effects.forEach(fn => fn(true))
    }
}
function B (isTracking = false) {
    /*** 与A类似 ***/
}


测试一下效果

Vue3响应式概念_vue3响应式

Vue3响应式概念_响应式_02

看起来达到了要求。


将上面代码优化一下,最终如下

let activeEffect = null;
function effect (watcher) {    
    activeEffect = watcher;    
    watcher(true);
    
    activeEffect = null;
}




const bucket = new WeakMap();


function track (target) {
    const effects = bucket.get(target) || new Set();
    activeEffect && effects.add(activeEffect);
    bucket.set(target, effects);
}




function trigger (target) {
    bucket.get(target)?.forEach?.(fn => fn(true));
}
function A (isTracking = false) {
    if (isTracking) {
        
        track(A);
    } else {
        console.log('A触发了')
        
        trigger(A);
    }
}
function B (isTracking = false) {
    
}


这里将之前 A.effects = A.effects || new Set();依赖收集流程提取成track函数,监听函数的触发流程抽离为trigger函数;这样,我们实现了一个简单的依赖收集系统。


Vue依赖收集模型

我们知道Vue3是通过Proxy实现的依赖收集流程,Proxy示例:

Vue3响应式概念_vue3_03


1. Proxy对象get监听,set触发


Vue3中,Proxy代理数据在被读取时“依赖收集”,在被赋值时会“触发依赖”;我们试一下上面完成的依赖收集系统,看下效果:

const data = {
    value: 1,
}
const proxyData = new Proxy(data, {
    get(target, key) {
        
        track(target);
        return target[key];
    },
    set(target, key, value) {
        
        trigger(target);
        target[key] = value;
    }
})


测试一下


测试代码如下:

Vue3响应式概念_vue3响应式_04

终端运行结果:

Vue3响应式概念_vue3_05


看起来效果不错!但是下面的例子里有问题:


Vue3响应式概念_vue3_06

Vue3响应式概念_vue3_07

一个无关的属性key的赋值也会触发监听函数!这不是我们想要的。为了精确监听,还需要细化依赖收集系统。


2. “key”级依赖

我们可以将对象的属性作为基本单位进行依赖收集。改造如下:

// 依赖收集函数,这里精确到keyfunction track (target, key) {    const effects = bucket.get(target) || new Map();    const keyMap = effects.get(key) || new Set();    effects.set(key, keyMap);    bucket.set(target, effects);    activeEffect && keyMap.add(activeEffect);}// 依赖触发函数,这里精确到keyfunction trigger (target, key) {    const effects = bucket.get(target);    if (!effects) return;    const keyMap = effects.get(key);    if (!keyMap) return;    keyMap.forEach(effect => effect());}
const data = {    value: 1}const proxyData = new Proxy(data, {    get(target, key) {
        // 具体到key进行收集        track(target, key);        return target[key]    },    set(target, key, value) {
        // 触发到key        trigger(target, key);        target[key] = value    }})

这里试一下效果


Vue3响应式概念_vue3_08

Vue3响应式概念_响应式_09


这样就实现了精确到属性的监听系统。看到这里,似乎完成的很不错了,但是看到下面的例子:


Vue3响应式概念_响应式_10


这里value属性由false变为true后,属性data的就已不再参与监听函数内的逻辑了;监听函数不应该再响应data属性,但实际上并没有。因为依赖关系已经固化,data属性只要变化就一定会触发监听,不管是否真的需要:


Vue3响应式概念_vue3_11


3. 分支切换

为了优化这一点,应将依赖关系实时更新,将多余的监听去除。为此,vue采取的策略是:

每次监听函数运行前,都要将自己的依赖关系清除;然后在运行期间重建依赖关系。(版权归掘金硬毛巾原作者所有,侵删)


标签:function,target,概念,watcher,响应,effects,key,Vue3,函数
From: https://blog.51cto.com/jowin/8853224

相关文章

  • ml.net例子笔记2-概念和Widnows AI Studio
    一机器学习和ml.net1Python机器学习库在Python中,工具和库的生态系统可以分为五个主要领域:数据处理数据可视化数值计算模型训练神经网络这可能不全,因为此外还有其他许多的库,它们负责其他任务,并专注于机器学习的一些特定领域,比如自然语言处理和图像识别。使用Python......
  • 双指针算法概念
    "双指针"是一种在数组或链表中使用两个指针来进行操作的技术。这两个指针通常被称为“快”指针和“慢”指针,或者“左”指针和“右”指针,根据其在数据结构中的移动速度或位置来命名。双指针算法在处理数组或链表的问题中非常有效,可以帮助我们以更优的时间复杂度解决问题。常见的应......
  • 接口的概念
      java中的接口是一种特殊的抽象类,它定义了一组方法,但没有实现这些方法。接口为java程序提供了一种灵活的方式来定义类型,并且可以让不同的类实现相同的接口。接口的实现在java中,使用‘implements’关键字来实现接口,表示该类必须实现接口中声明的所有方法。publicinterfaceAni......
  • vue3Cron表达式组件
    npm安装no-vue3-cron引入报错,就直接把代码拿来自己改了no-vue3-cron仓库地址:https://github.com/wuchuanpeng/no-vue3-cronvue-cron.vue<stylelang="scss">.no-vue3-cron-div{.language{position:absolute;right:25px;z-index:1;}.el-tabs{......
  • 05-模块和包的概念
    模块和包模块是python的源文件,即.py文件。模块支持导入,一个模块可以导入其他系统提供或第三方模块,可以使用其中提供好的全局变量、函数等。若导入的模块名字过长,也可以使用as使用别名。import会导入一个模块中所有内容,如果只想使用部分内容,可使用from模块import部分这......
  • windows安全基本概念
    基本概念账户安全账号信息存储(SAM)SAM:securityaccountmanagerSAM对账户的管理是通过安全标识进行的,每个账户的安全标识是唯一的,账户被创建时,安全标识就会产生。SAM文件是windows的一个账户数据库,存储了登录名、密码等信息。该文件是加密存储的,只有system权限可以访问路径:1......
  • Docker相关概念
    镜像(Image):Docker将应用程序及其所需的依赖、函数库、环境、配置等文件打包在一起,称为镜像。(安装包)容器(Container):镜像中的应用程序运行后形成的进程就是容器,只是Docker会给容器进程做隔离,对外不可见。仓库(repository):仓库就是存放镜像的地方!仓库分为公有仓库和私有仓库! 一切......
  • vue3.0项目搭建
    一、安装vue3脚手架卸载vue2脚手架npmuninstall-gvue-cli清除缓存npmcacheclen--force安装最新脚手架npminstall-g@vue/cli查看脚手架版本vue-V二、构建项目创建项目vuecreate项目名选择配置自定义配置,回车上下键选择Linter/......
  • FastAPI-响应处理和配置
    前篇学习了关于请求参数相关的约束验证,Request包括路径参数,查询参数,枚举参数,文件参数,类型验证,多参数嵌套验证,请求体嵌套验证,Cookie和Header等,了解完这些对于接口的请求基本就到位了.这篇便主要针对响应处理进行介绍Response项目结构主要来自慕课网......
  • 关于响应式布局,你需要了解的知识点
    大家好,我是树哥。相信大家都知道我最近在学习前端知识,最近学到了响应式这块的内容。既然学到这块内容,那我必然会遵循「理论-实践-总结」的学习方法,这篇文章就是我对响应式知识的简单总结。什么是响应式布局?响应式布局,就是根据不同设备展示不同的布局,以免更方便用户浏览页面......