Vue3学习笔记
Vue3学习笔记
吴华锦全局API
nextTick
等待下一次DOM
更新刷新的工具方法。
在Vue
中更改响应式状态时,最终的DOM
更新并不是同步生效,而是由Vue
将它们缓存在一个队列中,直到下一个tick
才一起执行。这样是为了确保每个组件无论发生多少状态改变,都仅执行一次更新。
nextTick()
可以在状态改变后立即使用,以等待DOM
更新完成。
app.provide
提供一个值,可以在应用中的所有后代组件中注入使用。
第一个参数应当是注入的key
,第二个参数则是提供的值。返回应用实例本身。
1 | import { provide } from 'vue'; |
app.config.errorHandler
用于为应用内抛出的未捕获错误指定一个全局处理函数。
访问Props
setup
函数的第一个参数是组件的props
。和标准的组件一致,一个setup
函数的props
是响应式的,并且会在传入新的props
时同步更新。
如果解构了props
对象,解构出的变量将会丢失响应性。因此我们推荐通过props.xxx
的形式来使用其中的props
。
如果确实需要解构props
对象,或需要将某个props
传到一个外部函数中并保持响应性,那么你可以使用toRefs()
和toRef()
这两个工具函数。
1 | import { toRef, toRefs } from 'vue'; |
组合式API
setup
传入setup
函数的第二个参数是一个setup
上下文。
1 | export default { |
该上下文对象是非响应式的,可以安全地解构:
1 | export default { |
attrs
和slots
都是有状态的对象,它们总是会随着组件自身的更新而更新。这意味着应当避免解构它们,并始终通过attrs.x
或slots.x
的形式使用其中的属性。此外还需注意,和props
不同,attrs
和slots
的属性都不是响应式的。如果想要基于attrs
或slots
的改变来执行副作用,那么你应该在onBeforeUpdate
生命周期狗子中编写相关逻辑。
expose
expose
函数用于显示地限制该组件暴露出的属性,当父组件通过模板引用访问该组件的实例时,将仅能访问expose函数暴露出的内容
:
1 | export default { |
h
setup
也可以返回一个渲染函数,此时在渲染函数中可以直接使用在同一作用域下声明的响应式状态:
1 | import { h, ref } from 'vue'; |
返回一个渲染函数将会阻止我们返回其他东西。对于组件内部来说,这样没问题,但如果我们想通过模板引用将这个组件的方法暴露给父组件,那就有问题了。
可以通过调用expose()
解决这个问题:
1 | import { h, ref } from 'vue'; |
此时父组件可以通过模板引用来访问这个increment
方法。
响应式API:核心
ref
接受一个内部值,返回一个响应式的、可更改的ref
对象,此对象只有一个指向其内部值的属性.value
。
如果将一个对象赋值给ref
,那么这个对象将通过reactive()
转为具有深层次响应式的对象。这也意味着如果对象中包含了嵌套的ref
,它们将被深层地解包。
如果要避免这种深层次的转换,请使用shallowRef()
来替代。
computed
接受一个getter
函数,返回一个只读的响应式ref
对象。该ref
通过.value
暴露getter
函数的返回值。它可以接受一个带有get
和set
函数的对象来创建一个可写的ref
对象。
reactive
返回一个对象的响应式代理。
响应式转换是“深层”的:它会影响到所有嵌套的属性。一个响应式对象也将深层地解包任何ref
属性,同时保持响应性。
值得注意的是,当访问到某个响应式数组或Map
这样的原生集合类型中的ref
元素时,不会执行ref
的解包。
若要避免深层响应式转换,只想保留对这个对象顶层次访问的响应性,请使用shallowReactive()
来替代。
返回的对象以及其中嵌套的对象都会通过ES Proxy
包裹,因此不等于源对象,建议只使用响应式代理,避免使用原始对象。
注意当访问到某个响应式数组或Map
这样的原生集合类型中的ref
元素时,不会执行ref
的解包:
1 | import { ref, reactive } from 'vue'; |
将一个ref
赋值给一个reactive
属性时,该ref
会被自动解包。
readonly
接受一个对象(响应式或普通对象)或是一个ref
,返回一个原值的只读代理。
只读代理师深层的:对任何嵌套属性的访问都将是只读的。它的ref
解包行为与reactive
相同,但解包得到的值是只读的。
要避免深层级的转换行为,请使用shallowReadonly()
来替代。
1 | import { reactive, readonly,watchEffect() } from 'vue'; |
watchEffect
立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。
第一个参数就是要运行的副作用函数。这个副作用函数的参数也是一个函数,用来注册清理毁掉。清理回调会在该副作用下一次执行前被调用,可以用来清理无效的副作用。
第二个参数是一个可选的选项,可以用来调整副作用的刷新时机或调试副作用的依赖。
默认情况下,侦听器将在组件渲染之前执行。设置flush: 'post'
将会使侦听器延迟到组件渲染之后再执行。在某些特殊情况下(例如要使缓存失效),可能有必要在响应式依赖发生改变时立即触发侦听器。这可以通过设置flush: 'sync'
来实现。然而,该设置应谨慎使用,因为如果有多个属性同时更新,这将导致一些性能和数据一致性的问题。
返回值是一个用来停止该副作用的函数。
1 | const count = ref(0); |
停止侦听器:
1 | const stop = watchEffect(() => {}); |
暂停/恢复侦听器:
1 | const { stop, pause, resume } = watchEffect(() => {}); |
副作用清理:
1 | watchEffect(async onCleanUp => { |
watch
侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。
watch
默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数。
第一个参数是侦听器的源。这个来源可以是一下几种:
- 一个函数,返回一个值
- 一个
ref
- 一个响应式对象
- 有以上类型的值组成的数组
第二个参数是在发生变化时要调用的回调函数。这个回调函数接受三个参数:新值、旧值,以及一个用于注册副作用清理的回调函数。该回调函数会在副作用下一次重新执行前调用,可以用来清楚无效的副作用,例如等待中的异步请求。
当侦听多个来源时,回调函数接受两个数组,分别对应来源数组中的新值和旧值。
第三个可选的参数是一个对象,支持以下这些选项:
immediate
:在侦听器创建时立即触发回调。第一次调用时旧值是undefined
。deep
:如果源时对象,强制深度遍历,以便在深层级变更时触发回调。在3.5+
中,此参数还可以是指示最大遍历深度的数字。flush
:调整回调函数的刷新时机。onTrack/onTrigger
:调试侦听器的依赖。once
:3.4+
回调函数只会运行一次。侦听器将在回调函数首次运行后自动停止。
与watchEffect()
相比,watch
使我们可以:
- 懒执行副作用;
- 更加明确是应该由哪个状态触发侦听器重新执行;
- 可以访问所侦听状态的前一个值和当前值。
1 | // 侦听一个getter函数 |
当侦听多个来源时,回调函数接受两个数组,分别对应来源数组中的新值和旧值:
1 | let foo = ref('foo'); |
当使用getter
函数作为源时,回调只在此函数的返回值变化时才会触发。如果你想让回调在深层级变更时也能触发,你需要使用{ deep: true }
强制侦听器进入深层级模式。在深层级模式时,如果回调函数由于深层级的变更而被触发,那么新值和旧值将是同一个对象。
1 | const state = reactive({ count: 0 }); |
当直接侦听一个响应式对象时,侦听器会自动启用深层模式。
watch()
和watchEffect()
享有相同的刷新时机和调试选项:
1 | watch(source, callback, { |
停止侦听器:
1 | const { stop, pause, resume } = watch(source, callback); |
副作用清理:
1 | watch(id, async (newId, oldId, onCleanup) => { |
onWatcherCleanup() 3.5+
注册一个清理函数,在当前侦听器即将重新运行时执行。只能在watchEffect
作用函数或watch
回调函数的同步执行期间调用(即不能在异步函数的await
语句之后调用)。
响应式API:工具
isRef()
检查某个值是否为ref
。
返回值是一个类型板顶,这意味着isRef
可以被用作类型守卫。
toRef()
可以将值、refs
或getters
规范化为refs
(3.3+)。
也可以基于响应式对象上的一个属性,创建一个对应的ref
。这样创建的ref
与其源属性保持同步:改变源属性的值将更新ref
的值,反之亦然。