一、响应式基础
1、数组变化侦测
- 原数组变更,push,pop,shift,unshift,splice,sort,reverse
- 返回新数组,赋值给原数组,filter,concat,slice
2、setup,script setup(语法糖)
- 语法糖支持顶层的导入,声明的变量和函数可直接在同一组件的模板中使用,无需手动暴露大量状态和方法;
- 模板可以理解为同一作用域内声明的一个JS函数,因此可以访问与它一起声明的所有内容
3、响应式状态
ref
- Ref可以持有任何类型的值,包括深层嵌套的对象,数组或JS内置的数据结构,如Map
- ref是将内部值包装在一个特殊对象中,然后以value的getter,setter的形式来实现追踪和触发的
- 将ref直接作为参数传递给函数,可以保留响应式访问,以访问最新的值
- ref底层是基于reactive实现的
- 浅层ref—shallow ref,没有深层响应性
- 解包细节详见官网
reactive
- 只能用于对象类型,如对象,数组和如Map,Set这样的集合类型,不能持有原始类型
- reactive直接使对象本身具有响应性,是通过JS代理Proxy的方式来实现依赖追踪和触发更新的
- 不能替换整个对象,且对解构操作不友好
- 为保持访问代理一致性,对同一个原始对象调用reactive方法,总是返回同样的代理对象,而对一个已存在的代理对象调用reactive方法会返回其本身,但代理对象与原始对象是不相等的
- 浅层reactive—shallowReactive
computed
- 基于响应式依赖被缓存,优先使用计算属性,而不是方法,因为计算属性只有依赖更新,才会重新计算,而方法总是会执行
- Getter不应该有副作用,即不要改变其他状态,在getter中做异步请求或更改DOM,应使用侦听器
4、侦听器
更改了响应式状态后,可能同时触发Vue组件更新和侦听器回调
默认情况,回调会在父组件更新之后,所属组件DOM更新之前被调用,所以无法访问更新后的DOM
但是可以通过flush: post来设置,从而可以访问所属组件更新之后的DOM;
也可以设置flush:sync来设置同步侦听,以在vue进行任何更新之前触发,但它不会进行批处理,每当检测到响应式数据发生变化时就会触发,可以用来监听简单的布尔值,但应避免在可能多次同步修改的数据源如数组上使用
(1)watch
- 参数是一个ref(包含计算属性),一个响应式对象,一个getter函数,或多个数据源的数组
- deep
- immediate
(2)watchEffect
- 无需设置,自动会立即执行
- 可能比深度监听更有效,只跟踪回调中被使用到的属性,而不是递归跟踪所有属性
- 没有明确跟踪源,会在副作用发生期间追踪依赖,会在同步执行过程中,自动追踪所有能访问到的响应式属性,而在异步回调中,只有在第一个await正常工作前访问到的属性才会被追踪
(3)停止侦听器:同步创建的侦听器会自动绑定到宿主组件实例上,并且会在宿主组件卸载时自动停止,但异步创建的不会,需要手动停止
(4)DOM更新时机——nextTick
DOM更新不同步,会在这个方法更新周期中缓冲所有状态的修改,以确保你不管进行过多少次状态修改,每个组件都只更新一次
二、渲染机制
1、虚拟dom(virtual dom,VDOM)
vnode,纯JS对象,实际保存在内存
2、渲染管线
2-1、编译
模板编译为渲染函数
构建时编译;运行时编译
2-2、挂载:
运行时渲染器调用渲染函数,基于遍历返回的虚拟DOM树创建实际DOM节点
这一步会作为响应式副作用执行,因此它会追踪其中所用到的所有响应式依赖
2-3、更新
一个依赖发生变化后,副作用会重新运行,会创建一个新的虚拟DOM树,会与旧树对比,然后将必要的更新应用到真实DOM上去
3、带编译时信息的虚拟DOM
3-1、静态提升
完全静态的节点会被提升成vnode创建函数到渲染函数之外,每次渲染时都使用相同vnode,渲染器会完全跳过对它们的差异对比;
足够多连续的静态元素,还会被压缩成一个静态vnode,其中包含的是这些节点相应的纯HTML字符串,会直接通过innerHTML来挂载,同时还会在首次挂载后被缓存相应的DOM,如果被重用,会用原生cloneNode方法克隆新的节点
3-2、更新类型标记
有动态绑定的单个元素,在为这些元素生成渲染函数时,创建vnode时会添加更新类型标记,一个元素可以有多个标记,会被合并成数字,然后运行时渲染器也将会使用位运算来检查标记,确定更新操作,这是为了更新时做最少的操作
3-3、树结构打平
内部结构稳定的区块内,每一个块都会追踪所有带有更新类型标记的后代节点,不只是直接子节点,编译的结果会被打平为一个数组,仅包含所有动态的后代节点,当这个组件重渲染时,只需遍历打平的树,而不是整棵树,这大大减少了我们在虚拟DOM协调时需要遍历的节点数量,静态部分会被高效略过;使用了v-if或v-for指令创建新的区块节点时,子区块会在父区块的动态子节点数组中被追踪,这为他们的父区块保留了一个稳定的结构
3-4、对SSR激活的影响
详见官网
三、Vue中的响应性是如何工作的
依赖,订阅者,副作用,响应式副作用,响应式渲染
1、变量被读取时,进行追踪—getter,setter,Proxy
2、若变量在副作用中被读取,刚将该副作用设为此变量的订阅者
3、一个变量变化,通知所有订阅副作用重新执行