首页 > 其他分享 >【Vue】依赖追踪

【Vue】依赖追踪

时间:2023-12-26 13:33:59浏览次数:27  
标签:Vue 渲染 watcher 依赖 计算 追踪 data 模板 属性

在明白原理之前,我们有很多表面现象、使用场景需要记忆。明白了原理后,你会发现它们已经不需要记了,因为从原理出发,你自己都能把它们推导出来,一切是那么的自然而然。感觉就是:这还用记吗?很明显嘛!

之前我对vue的响应式原理,只是一知半解,导致开发中经常会出现疑问,比如:为什么有的数据它不响应?模板中用到的methods方法什么时候会执行?什么时候模板会重新渲染?渲染的过程是什么等等。所有的这些开发过程中的疑惑,都是因为不了解底层原理造成的。

今天我们就来一起捋一下,vue的响应式原理。当然,只是入门级的,可以帮助和我一样不了解原理的同学,大佬勿喷。

 

一、数据劫持:getter和setter

在vue的data初始化阶段,vue会递归地遍历data的每一个属性,把它们处理成响应式数据。这是一个深层次的遍历,也就是说data的属性如果是一个对象,这个对象的属性也是响应式的,不管嵌套几层。

具体来说,vue对每一个属性执行Object.defineProperty(),把每一个属性转换为getter和setter,以此实现对属性取值、赋值的劫持,称为数据劫持。

 

二、watcher和dep

我们知道,模板中会用到data的数据,计算属性也是如此,它们会各用一个列表来保存自己用到了哪些data数据,称为依赖列表。

而data的属性,则可能会被模板以及多个计算属性用到,它也会用一个列表来保存哪些模板或计算属性用到了自己,也叫依赖列表。

模板和计算属性,通过watcher对象来做这件事,依赖列表存放在watcher的一个数组里。每一个vue实例,有一个watcher,称为渲染watcher。每一个计算属性,各自有一个wathcer,称为计算属性watcher。

data的属性,通过dep对象来做这件事,依赖列表存放在dep的一个数组里。data的每一个属性,都有一个dep对象。

watcher的这个数组,成员是dep对象。dep的这个数组,成员是watcher对象。

也就是说,通过维护对方的列表,模板和计算属性,知道我用到了哪些属性。data的属性,也知道哪些模板和计算属性用到了我。

 

三、依赖收集

在模板第一次渲染、计算属性第一次被使用时,它们所依赖属性的getter会触发,然后就把这个模板或计算属性的watcher添加到该属性的依赖列表里(dep对象的数组)。

同时,这些属性的dep对象,也会被添加到模板或计算属性的依赖列表里(watcher对象的数组)。

这个过程是双向的。我曾经疑问为什么需要在watcher里维护依赖列表?因为看上去,属性更新时,通知它的依赖列表里的每一个watcher,让它们去更新,这个模型似乎就可以了。

原来,有时我们是需要模板主动更新的,比如$forceUpdate函数,这时通过watcher的依赖列表,就可以查看这些依赖有没有更新,如果都没有更新,就无需重新渲染,提高了性能。

 

四、依赖更新

在一个属性发生变化时,这个属性的setter被触发,它会通知依赖列表里的每一个watcher,让它们去更新。

渲染watcher接到通知,会重新渲染页面。计算属性watcher接到通知,会进行重新计算。

实际的模型比这要复杂。组件的更新过程是异步的,当被通知重新渲染时,不会立即触发,而是将组件标记为“待更新”。Vue 使用一个异步队列来批量处理这些更新,以提高性能。这意味着在同一事件循环中,多次改变数据只会导致一次组件更新和重新渲染。

同样,通知计算属性重新计算,也不会立即触发,而是把计算属性标记为“待更新”,直到该计算属性下一次被使用时(比如重新渲染),才会重新计算。

 

五、原理之上的应用

明白了原理,我们可以弄清楚很多问题,比如:

(1)vue中的哪些数据是响应式的?

props、data、computed:前两个我们好理解,这里需要注意的是计算属性。思考下面一个问题:

模板中用到一个计算属性,那么它的渲染watcher的依赖列表里,是这个计算属性,还是这个计算属性所依赖的data属性?

答案是:这个计算属性。这是因为,计算属性本身也是响应式的,同样会被Object.defineProperty处理。计算属性的效果就是一层缓存,它不仅会被模板用到,还可能被其他计算属性用到。在这个案例中,当计算属性依赖的data改变时,会先触发计算属性的重新计算,只有计算后的值和原来不同,模板才会重新渲染,反之,就无需重新渲染。

另外,$route和$store.state也是响应式的,原理和其他的一样。意味着,如果模板中用到了它俩,它俩改变时模板是会重新渲染的(计算属性也一样,会重新计算)。

(2)我们知道,一个模板中会用到各种数据:data属性、计算属性、表达式、methods中的方法、全局的自定义函数。那么当模板重新渲染时,它们各自会怎么样呢?

计算属性:只有计算属性的依赖发生变化时,它才会在重新渲染时重新计算。前者会把计算属性标记为“待更新”,重新计算则会等到下一次被使用(比如重新渲染)时才会进行。

表达式、methods中的方法、全局的自定义函数:每次重新渲染都会重新计算,因为它们的值不会被缓存,所以要尽可能多的使用计算属性。

(3)什么会触发组件的重新渲染?

组件只有在模板依赖的数据发生变化时,才会重新渲染。那些模板中没用到的数据,改变并不会让模板重新渲染。并且,这种依赖是属性级别的,也就是说,模板中用到了data中的一个对象,但这个对象的改变不一定导致重新渲染,因为改变的属性不一定是模板用到的那个。

父组件和子组件,它们的渲染也没有必然的联系。子组件的data发生变化,不会导致父组件重新渲染,因为父组件不会用到子组件的数据。父组件的data发生变化,也只有它自己,和用到该数据的子组件会重新渲染。不过要注意,如果父组件是销毁了重新创建,那么子组件也只能跟着销毁重新创建。另外,如果父组件对子组件使用了v-if、v-for(搭配key使用)、key,那么子组件很可能会随着它们的变化而销毁重建。

(4)为什么vue无法监听对象和数组的某些操作?

明白了vue2的响应式原理,也就理解了为什么,vue无法监听到对象属性的添加和删除,因为vue2只能劫持对象属性的取值和赋值。想给响应对象添加属性,应该使用Vue.set()或者this.$set()。

数组的限制是,无法监听到通过索引直接赋值和修改数组的长度。我暂时无法解释,不过我的方法时统一用splice方法来替代。

标签:Vue,渲染,watcher,依赖,计算,追踪,data,模板,属性
From: https://www.cnblogs.com/luyj00436/p/17927938.html

相关文章

  • VUE3 + Three.js 坑
    VUE3+Three.js坑1.问题描述将scene、camera、renderer、controls等变量用reactive变成响应式时,页面渲染会报错:three.module.js?5a89:24471UncaughtTypeError:'get'onproxy:property'modelViewMatrix'isaread-onlyandnon-configurabledatapropertyonthe......
  • lottie 动画在 vue 中的使用
    前言最近我所负责的项目中,我采用了动画效果,并开始使用gif来实现。然而,在实践过程中,我发现gif格式的动画在git中出现了明显的锯齿感,这让我非常困扰。为了追求更完美的表现效果,我最终选择了lottie来实现我的动画需求。我深知动画效果的呈现对于用户体验至关重要,因此我非常......
  • vue 浅解
    Vue(发音为/vjuː/,类似 view)是一款用于构建用户界面的JavaScript框架。它基于标准HTML、CSS和JavaScript构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。无论是简单还是复杂的界面,Vue都可以胜任。vue有自己的语法,一般以v-开头,如:v-for="(item......
  • fortawesome在vue3中的使用
    fortawesome的使用,2023-12-25https://fontawesome.com/search?o=r&m=free&c=coding&s=solid6.5.1,3.0.5,6.5.1yarnadd@fortawesome/fontawesome-svg-coreyarnadd@fortawesome/vue-fontawesomeyarnadd@fortawesome/free-solid-svg-icons/*SetupusingVu......
  • Maven uber-jar(带依赖的打包插件)maven-shade-plugin
    转载自:https://blog.csdn.net/Ares5kong/article/details/128832989文章目录最基础的maven-shade-plugin使用生成可执行的Jar包和常用的资源转换类包名重命名打包时排除依赖与其他常用打包插件比较 本文是对maven-shade-plugin常用配置的介绍,更详细的学习......
  • Maven uber-jar(带依赖的打包插件) spring-boot-maven-plugin
    转载自:https://blog.csdn.net/Ares5kong/article/details/128791102文章目录最基础的spring-boot-maven-plugin使用指定入口类安装部署原始Jar包到仓库保持原始Jar包名称,为spring-boot-maven-plugin生成的Jar包添加名称后缀打包时排除依赖建议将生成的Jar解压后......
  • 依赖注入容器 perRequest(Transient)和Singleton区别
    在CM框架中,"perRequest"和"Singleton"都是生命周期配置选项,用于指示对象的创建和共享方式。它们之间的区别在于对象实例的生命周期和共享方式。对于"perRequest"(有时被称为"Transient"):对象的实例在每个请求处理期间只创建一次,并在同一个请求内共享。每个请求都有自己的对象实例,不......
  • vue3中toRefs用法
    toRefs是Vue.js3中的一个实用函数,用于将响应式对象转换为普通对象,其中每个属性都是一个ref对象。这在某些情况下很有用,特别是想要将一个包含多个响应式属性的对象传递给子组件时。以下是toRefs的基本用法:import{reactive,toRefs}from'vue';constreactiveObject=reac......
  • 深入理解 Spring IoC 和 DI:掌握控制反转和依赖注入的精髓
    在本文中,我们将介绍IoC(控制反转)和DI(依赖注入)的概念,以及如何在Spring框架中实现它们。什么是控制反转?控制反转是软件工程中的一个原则,它将对象或程序的某些部分的控制权转移给容器或框架。我们最常在面向对象编程的上下文中使用它。与传统编程相比,传统编程中我们的自定义......
  • pdm符合最新PEP标准的现代python 包以及依赖管理工具
    pdm符合最新PEP标准的现代python包以及依赖管理工具支持的特性简单快速的维护依赖解析,主要为了构建大的二进制分发PEP517构建后端PEP621项目元数据灵活强大的插件系统多功能用户脚本类似pnpm的中心化安装优化安装命令使用过的mac系统的 brewin......