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

之前我对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的响应式原理:依赖追踪的更多相关文章

  1. vue深入响应式原理

    vue深入响应式原理 深入响应式原理 — Vue.jshttps://cn.vuejs.org/v2/guide/reactivity.html 注意:这里说的响应式不是bootsharp那种前端UI ...

  2. Vue.js响应式原理

      写在前面 因为对Vue.js很感兴趣,而且平时工作的技术栈也是Vue.js,这几个月花了些时间研究学习了一下Vue.js源码,并做了总结与输出. 文章的原地址:answershuto/learnV ...

  3. vue.js响应式原理解析与实现

    vue.js响应式原理解析与实现 从很久之前就已经接触过了angularjs了,当时就已经了解到,angularjs是通过脏检查来实现数据监测以及页面更新渲染.之后,再接触了vue.js,当时也一度很 ...

  4. Vue 数据响应式原理

    Vue 数据响应式原理 Vue.js 的核心包括一套“响应式系统”.“响应式”,是指当数据改变后,Vue 会通知到使用该数据的代码.例如,视图渲染中使用了数据,数据改变后,视图也会自动更新. 举个简单 ...

  5. 深入解析vue.js响应式原理与实现

    vue.js响应式原理解析与实现.angularjs是通过脏检查来实现数据监测以及页面更新渲染.之后,再接触了vue.js,当时也一度很好奇vue.js是如何监测数据更新并且重新渲染页面.vue.js ...

  6. Vue的响应式原理

    Vue的响应式原理 一.响应式的底层实现 1.Vue与MVVM Vue是一个 MVVM框架,其各层的对应关系如下 View层:在Vue中是绑定dom对象的HTML ViewModel层:在Vue中是实 ...

  7. 一探 Vue 数据响应式原理

    一探 Vue 数据响应式原理 本文写于 2020 年 8 月 5 日 相信在很多新人第一次使用 Vue 这种框架的时候,就会被其修改数据便自动更新视图的操作所震撼. Vue 的文档中也这么写道: Vu ...

  8. Vue的响应式原理---(v-model中的双向绑定原理)

    Vue响应式原理 不要认为数据发生改变,界面跟着更新是理所当然. 具体代码实现:https://gitee.com/ahaMOMO/Vue-Responsive-Principle.git 看下图: ...

  9. vue系列---响应式原理实现及Observer源码解析(一)

    _ 阅读目录 一. 什么是响应式? 二:如何侦测数据的变化? 2.1 Object.defineProperty() 侦测对象属性值变化 2.2 如何侦测数组的索引值的变化 2.3 如何监听数组内容的 ...

  10. VUE响应式原理-如何追踪变化

    Vue 最独特的特性之一,是其非侵入性的响应式系统.数据模型仅仅是普通的 JavaScript 对象.而当你修改它们时,视图会进行更新.这使得状态管理非常简单直接 如何追踪变化 当你把一个普通的 Ja ...

随机推荐

  1. 程序后台运行方法:使用守护进程 或 screen软件

    我们常需要SSH远程登录到Linux 服务器,经常运行一些需要很长时间才能完成的任务,在此期间不能关掉窗口或者断开连接,否则这个任务会被杀掉,一切就半途而废了. 可以使用以下两个方法: 方法一:noh ...

  2. SpringCloudAlibaba框架学习

    遇到问题找了各种办法都没有解决,就reload maven,再不行就重启idea,重启电脑.(有奇效,我好几次就这么解决的,可能是我电脑配置太拉了) 注册中心 - Nacos 配置文件优先级:本地配置 ...

  3. 极简工作流「GitHub 热点速览」

    原以为 LLM 很难,但其实可以很简单,比如 Flowise 拖拽拖拽就能出来一个 LLM 流程,非常简单你的 LLM 就可以 run 起来了.同样的 web-check 也能极快速地帮你解决 Web ...

  4. 论文解读(KD-UDA)《Joint Progressive Knowledge Distillation and Unsupervised Domain Adaptation》

    Note:[ wechat:Y466551 | 可加勿骚扰,付费咨询 ] 论文信息 论文标题:Joint Progressive Knowledge Distillation and Unsuperv ...

  5. Spark RDD惰性计算的自主优化

    原创/朱季谦 RDD(弹性分布式数据集)中的数据就如final定义一般,只可读而无法修改,若要对RDD进行转换或操作,那就需要创建一个新的RDD来保存结果.故而就需要用到转换和行动的算子. Spark ...

  6. 开源Word文字替换小工具更新 增加文档页眉和页脚替换功能

    ITGeeker技术奇客发布的开源Word文字替换小工具更新到v1.0.1.0版本啦,现已支持Office Word文档页眉和页脚的替换. 同时ITGeeker技术奇客修复了v1.0.0.0版本因替换 ...

  7. 安装iTerm2和oh-my-zsh

    安装iTerm2和oh-my-zsh 此文是在参考许多教程(见目录:参考)并结合本人安装经历写下的一篇关于iTerm2和oh-my-zsh的认识和超级详细安装教程.全文所有图片均为本人截屏拍摄.希望能 ...

  8. Oracle:Ora-01652无法通过128(在temp表空间中)扩展temp段的过程-解决步骤

    现象:查询select * from v$sql时提示"Ora-01652无法通过128(在temp表空间中)扩展temp段的过程" 临时文件是不存储的,可以将数据库重启,重启后重 ...

  9. Docker常见使用

    DockerFile: FROM openjdk:8 MAINTAINER guotong ADD lpl-1.0-SNAPSHOT.jar lpl-1.0-SNAPSHOT.jar EXPOSE 8 ...

  10. eclipse使用技巧和插件

    eclipse使用技巧和插件 本篇文章只列举了一部分技巧和插件,并没有包括大家都知道的快捷键和技巧,而是一些不经常用但又很方便的功能. 一,技巧 给Eclipse添加更方便的提示功能:Windows– ...