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
的值,反之亦然。
1 | const state = reactive({ |
toRef()
这个函数在你想要把一个props
的ref
传递给一个组合式函数时会很有用。
当toRef
与组件props
结合使用时,关于禁止对props
做出更改的限制依然有效。尝试将新的值传递给ref
等效于尝试直接更改props
,这是不允许的。在这种场景下,你可以考虑使用带有get
和set
的computed
替代。
当使用对象属性签名时,即使源属性当前不存在,toRef()
也会返回一个可用的ref
。这让它在处理可选props
的时候格外实用,相比之下toRefs()
就不会为可选props
创建对应的refs
。
toRefs()
将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的ref
。每个独立的ref
都是使用toRef()
创建的。
1 | const state = reactive({ |
当从组合式函数中返回响应式对象时,toRefs
相当有用。使用它,消费者组件可以解构/展开返回的对象而不会失去响应性。
1 | const useFeatureX = () => { |
toRefs
在调用时只会为源对象上可以枚举的属性创建ref
。如果要为可能还不存在的属性创建ref
,请改用toRef
。
isProxy()
检查一个对象是否是由reactive()
、readonly()
、shallowReactive()
或shallowReadonly()
创建的代理。
isReactive()
检查一个对象是否是由reactive()
或shallowReactive()
创建的代理。
isReadonly()
检查传入的值是否为只读对象。只读对象的属性可以更改,但它们不能通过传入的对象直接赋值。
通过readonly()
和shallowReadonly()
创建的代理都是只读的,类似于没有set
函数的computed()
的ref
。
响应式API:进阶
shallowRef()
ref()
的浅层作用形式。
和ref()
不同,浅层ref
的内部值将会原样存储和暴露,并且不会被深层递归地转为响应式。只有对.value
的访问是响应式的。
shallowRef()
常常用于对大型数据结构的性能优化或是与外部的状态管理系统集成。
1 | const state = shallowRef({ count: 1 }); |
customRef()
创建一个自定义的ref
,显式声明对其依赖追踪和更新触发的控制方式。
customRef()
预期接收一个工厂函数作为参数,这个工厂函数接受track
和trigger
两个函数作为参数,并返回一个带有get
和set
方法的对象。
一般来说,track()
应该在get()
方法中调用,而trigger()
应该在set()
中调用。然而事实上,你对何时调用、是否应该调用它们有完全的控制权。
1 | import { customRef } from 'vue'; |
toRaw()
根据一个Vue
创建的代理返回其原始对象。
toRaw()
可以返回由reactive()
、readonly()
、shallowReactive()
或者shallowReadonly()
创建的代理对应的原始对象。
这是一个可以用于临时读取而不引起代理访问/跟踪开销,或是写入而不触发更改的特殊方法。不建议保存对原始对象的持久引用,请谨慎使用。
markRaw()
将一个对象标记为不可被转为代理。返回该对象本身。
effectScope()
创建一个effect
作用域,可以捕获其中所创建的响应式副作用(即计算属性和侦听器),这样捕获到的副作用可以一起清理。
1 | const scope = effectScope(); |
onScopeDispose()
在当前活跃的effect
作用域上注册一个处理回调函数。当相关的effect
作用域停止时会调用这个回调函数。
这个方法可以作为可复用的组合式函数中onUnmounted
的替代品,它并不与组件耦合,因为每一个Vue
组件的setup()
函数也是在一个effect
作用域中调用的。
如果在没有活跃的effect
作用域的情况下调用此函数,将会抛出警告。在3.5+
版本中,可以通过将第二个参数设为true
来消除此警告。
组合式API:生命周期钩子
这些钩子在服务器端渲染期间不会被调用。
onMounted()
注册一个回调函数,在组件挂载完成后执行。
组件在以下情况下被视为已挂载:
- 其所有同步子组件都已经被挂载(不包含异步组件或
<Suspense>
树内的组件)。 - 其自身的
DOM
树已经创建完成并完成插入了父容器中。注意仅当根容器在文档中时,才可以保证组件DOM
树也在文档中。
这个钩子通常用于执行需要访问组件所渲染的DOM
树相关的副作用,或是在服务端渲染应用中用于确保DOM
相关代码仅在客户端执行。
onUpdated()
注册一个回调函数,在组件因为响应式状态变更而更新其DOM
树之后调用。
父组件的更新钩子将在其子组件的更新钩子之后调用。
这个钩子会在组件的任意DOM
更新后被调用,这些更新可能是由不同的状态变更导致的,因为多个状态变更可以在同一个渲染周期中批量执行。如果你需要在某个特定的状态更改后访问更新后的DOM
,请使用nextTick
作为替代。
onUnmounted()
注册一个回调函数,在组件实例被卸载之后调用。
一个组件在以下情况下被视为已卸载:
- 其所有子组件都已经被卸载。
- 所哟相关的响应式作用(渲染作用以及
setup()
时创建的计算属性和侦听器)都已经停止。
可以在这个钩子中手动清理一些副作用,例如计时器、DOM
事件监听器或者与服务器的连接。
onBeforeMount()
注册一个钩子,在组件被挂载之前被调用。
当这个钩子被调用时,组件已经完成了其响应式状态的设置,但还没有创建DOM
节点。它即将首次执行DOM
渲染过程。
onBeforeUpdate()
注册一个钩子,在组件即将因为响应式状态变更而更新其DOM
树之前调用。
这个钩子可以用来在Vue
更新DOM
之前访问DOM
状态。在这个钩子中更改状态也是安全的。
onBeforeUnmount()
注册一个钩子,在组件实例被卸载之前调用。
当这个钩子被调用时,组件实例依然还保有全部的功能。
onErrorCaptured()
注册一个钩子,在捕获了后代组件传递的错误时调用。
错误可以从以下几个来源中捕获:
- 组件渲染
- 事件处理器
- 生命周期钩子
setup()
函数- 侦听器
- 自定义指令钩子
- 过渡钩子
这个钩子带有三个实参:错误对象、触发该错误的组件实例,以及一个说明错误来源类型的信息字符串。
你可以在errorCaptured()
中更改组件状态来为用户显示一个错误状态。注意不要让错误状态再次渲染导致本次错误的内容,否则组件会陷入无线循环。
这个钩子可以通过返回false
来阻止错误继续向上传递。
错误传递规则
- 默认情况下,所有的错误都会被发送到应用级的
app.config.errorHandler
(前提是这个函数已经被定义),这些错误都能在一个同意的地方报告给分析服务。 - 如果组件的继承链或组件链上存在多个
errorCaptured
钩子,对于同一个错误,这些钩子会被按从底至上的顺序一一调用。这个过程被称为“向上传递”,类似于原生DOM
事件的冒泡机制。 - 如果
errorCaptured
钩子本身抛出了一个错误,那么这个错误和原来捕获到的错误都将被发送到app.config.errorHandler
。 errorCaptured
钩子可以通过返回false
来阻止错误继续向上传递。即表示“这个错误已经被处理了,应当被忽略”,它将阻止其他的errorCaptured
钩子或app.config.errorHandler
因这个错误而被调用。
onActivated()
注册一个对调函数,若组件实例是<keepAlive>
缓存树的一部分,当组件被插入到DOM
中时调用。
onDeactivated()
注册一个回调函数,若组件实例是<keepAlive>
缓存树的一部分,当组件从DOM
中被移除时调用。
onSrverPrefetchSSR only
注册一个异步函数,在组件实例在服务器上被渲染之前调用。
如果这个钩子返回了一个Promise
,服务端渲染会在渲染该组件前等待该Promise
完成。
这个钩子仅会在服务端渲染中执行,可以用于执行一些仅存在于服务端的数据抓取过程。
组合式API:依赖注入
provide()
提供一个值,可以被后代组件注入。
provide()
接受两个参数:第一个参数是要注入的key
,可以是一个字符串或者一个symbol
,第二个参数是要注入的值。
当使用Typescript
时,key
可以是一个被类型断言为InjectionKey
的symbol
。InjectionKey
是一个Vue
提供的工具类型,继承自Symbol
,可以用来同步provide()
和inject()
之间值的类型。
与注册生命周期钩子的API
类型,provide()
必须在组件的setup()
阶段同步调用。
inject()
注入一个由祖先组件或整个应用(通过app.provide()
)提供的值。
第一个参数是注入的Key
。Vue
会遍历父组件链,通过匹配Key
来确定所提供的值。如果父组件链上多个组件对同一个Key
提供了值,那么离得更近的组件将会“覆盖”链上更远的组件所提供的值。如果没有能通过Key
匹配的值,inject()
将返回undefined
,除非提供了一个默认值。
第二个参数也可以是一个工厂函数,用来返回某些创建起来比较复杂的值。在这种情况下,你必须将true
作为第三个参数传入,表面这个函数将作为工厂函数使用,而非值本身。
与注册生命周期钩子的API
类似,inject()
必须在组件的setup()
阶段同步调用。
当使用TypeScript
时,Key
可以是一个类型为InjectionKey
的Symbol
。InjectionKey
是一个Vue
提供的工具类型,继承自Symbol
,可以用来同步provide()
和inject()
之间值的类型。
组合式API:辅助
useAttrs()
从setup
上下文中返回attrs
对象,其中包含当前组件的透传attributes
。这是用于<script setup>
中的,因为在<script setup>
中无法获取setup
上下文对象。
useSlots()
从setup
上下文中返回slots
对象,其中包含父组件传递的插槽。这些插槽为可调用的函数,返回虚拟DOM
节点。这是用于<script setup>
中的,因为在<script setup>
中无法获取setup
上下文对象。
如果使用TypeScript
,建议优先使用defineSlots()
。
useModel()
这是驱动defineModel()
的底层辅助函数。如果使用<script setup>
,应当优先使用defineModel()
。
仅在3.4+
版本中可用。
useModel()
可以用于非单文件组件,例如在使用原始的setup()
函数时,它预期的第一个参数是props
对象,第二个参数是model
名称。可选的第三个参数可以用于为生成的model ref
声明自定义的getter
和setter
。请注意,与defineModel()
不同,你需要自己声明props
和emits
。
useTemplateRef()3.5+
返回一个浅层ref
,其值将与模板中的具有匹配ref attribute
的元素或组件同步。
1 | import { onMounted, useTemplateRef } from 'vue'; |
useId()
用于为无障碍属性或表单元素生成每个应用内位移的ID
。
1 | <template> |
useId()
生成的每个ID
在每个应用内都是唯一的。它可以用于为表单元素或无障碍属性生成ID
。在同一个组件中多次调用会生成不同的ID
;同一个组件的多个实例调用useId()
也会生成不同的ID
。
useId()
生成的ID
在服务器端和客户端渲染之间时稳定的,因此可以安全地在SSR
应用中使用,不会导致激活不匹配。
如果同一页面上有多个Vue
应用实例,可以通过app.config.idPrefix
为每个应用提供一个ID
前缀,以避免ID
冲突。
状态选项
data
用于声明组件初始响应式状态的函数。
该函数应当返回一个普通JavaScript
对象,Vue
会将它转换为响应式对象。实例创建后,可以通过this.$data
访问该响应式对象。组件实例也代理了该数据对象上所有的属性,因此this.a
等价于this.$data.a
。
所有会用到的顶层数据属性都应该提前在这个对象中声明。虽然理论上可以向this.$data
添加新属性,但并不推荐这么做。如果一个属性的值在一开始还获取不到,应当先用undefined
或是null
值来占位,让Vue
知道这个属性是存在的。
以_
或$
开头的属性将不会被组件实例代理,因为它们可能和Vue
的内置属性、API
方法冲突。你必须以this.$data._property
的方式访问它们。
不推荐返回一个可能改变自身状态的对象,如浏览器API
原生对象或是带原型的类实例等。理想情况下,返回的对象应是一个纯粹代表组件的普通对象。
注意,如果你为data
属性使用了一个箭头函数,则this
将不会指向该组件实例,不过你仍然可以通过该函数的第一个参数来访问实例。
props
用于声明一个组件的props
。
在Vue
中,所有的组件props
都需要被显式声明。组件props
可以通过两种方式声明:
- 使用字符串数组的简易形式。
- 使用对象的完整形式。该对象的每个属性键是对应
props
的名称,值则是该props
应具有的类型的构造函数,或是更高级的选项。
在基于对象的语法中,每个props
可以进一步定义如下选项:
type
:可以是下列原生构造函数之一:String
、Number
、Boolean
、Array
、Object
、Date
、Function
、Symbol
、任何自定义构造函数,或由上述内容组成的数组。在开发模式中,Vue
会检查一个props
的值是否匹配其声明的类型,如果不匹配则会抛出警告。
还要注意,一个Boolean
类型的props
会影响它在开发或生产模式下的值转换行为。
default
:为该props
指定一个当其没有被传入或值为undefined
时的默认值。对象或数组的默认值必须从一个工厂函数返回。工厂函数也接收原始props
对象作为参数。required
:定义该props
是否必须传入。在非生产环境中,如果required
值为真值且props
未被传入,一个控制台警告将会被抛出。validator
:将props
值作为唯一参数传入的自定义验证函数。在开发模式下,如果该函数返回一个假值(即验证失败),一个控制台警告将会被抛出。
1 | props: { |
computed
用于声明要在组件实例上暴露的计算属性。
该选项接收一个对象,其中键是计算属性的名称,值是一个计算属性getter
,或一个具有get
和set
方法的对象(用于声明可写的计算属性)。
所有的getters
和setters
会将它们的this
上下文自动绑定为组件实例。
注意,如果你为一个计算属性使用了箭头函数,则this
不会指向该组件实例,不过你仍然可以通过该函数的第一个参数来访问实例。
1 | computed(vm => vm.a * 2); |
methods
用于声明要混入到组件实例中的方法。
声明的方法可以直接通过组件实例访问,或者在模板语法表达式中使用。所有的方法都会将它们的this
上下文自动绑定为组件实例,即使在传递时也如此。
在声明方法时避免使用箭头函数,因为它们不能通过this
访问组件实例。
watch
用于声明在数据更改时调用的侦听回调。
watch
选项期望接受一个对象,其中键是需要侦听的响应式组件实例属性(例如,通过data
或computed
声明的属性)——值是相应的回调函数。该回调函数接受被侦听源的新和旧值。
除了一个根级属性,键名也可以是一个简单的由点分隔的路径。注意,这种用法不支持复杂表达式——仅支持由点分隔的路径。如果你需要侦听复杂的数据源,可以使用命令式$watch() API
。
值也可以是一个方法名称的字符串(通过methods
声明),或包含额外选项的对象。当使用对象语法时,回调函数应被声明在handler
中。额外的选项包含:
immediate
:在侦听器创建时立即触发回调。第一次调用时,旧值将为undefined
。deep
:如果源时对象或数组,则强制深度遍历源,以便在深度变更时触发回调。flush
:调整回调的刷新时机。onTrack/onTrigger
:调试侦听器的依赖关系。
声明侦听器回调时避免使用箭头函数,因为它们将无法通过this
访问组件实例。
emits
用于声明由组件触发的自定义事件。
可以以两种形式声明触发的事件:
- 使用字符串数组的简易形式。
- 使用对象的完整形式。该对象的每个属性键是事件的名称,值是
null
或一个验证函数。
验证函数会接收到传递给组件的$emit
调用的额外参数。
注意,emits
选项会影响一个监听器被解析为组件事件监听器,还是原生DOM
事件监听器。被声明为组件事件的监听器不会被透传到组件的根元素上,且将从组件的$attrs
对象中移除。
expose
用于声明当组件实例被父组件通过模板引用访问时暴露的公共属性。
默认情况下,当通过$parent
、$root
或模板引用访问时,组件实例将向父组件暴露所有的实例属性。这可能不是我们希望看到的,因为组件很可能拥有一些应保持私有的内部状态或方法,以避免紧耦合。
expose
选项值应当是一个包含要暴露的属性名称字符串的数组。当使用expose
时,只有显式列出的属性将在组件实例上暴露。
expose
仅影响用户定义的属性——它不会过滤掉内置的组件实例属性。
渲染选项
template
用于声明组件的字符串模板。
通过template
选项提供的模板将会在运行时即时编译。这仅在使用了包含模板编译器的Vue
构建版本的情况下支持。文件名中带有runtime
的Vue
构建版本未包含模板编译器,例如vue.runtime.esm-bundler.js
。
如果该字符以#
开头,它将被用作querySelector
的选择器,并使用所选中元素的innerHTML
作为模板字符串。这让我们能够使用原生<template>
元素来书写源模板。
如果render
选项也同时存在于该组件中,template
将被忽略。
如果应用的根组件不包含任何template
或render
选项,Vue
将会尝试使用所挂载元素的innerHTML
来作为模板。
render
用于编程式地创建组件虚拟DOM
树的函数。
render
是字符串模板的一种替代,可以使你利用JavaScript
的丰富表达力来完全编程式地声明组件最终的渲染输出。
预编译的模板,例如单文件组件中的模板,会在构建时被编译为render
选项。如果一个组件中同时存在render
和template
,则render
将具有更高的优先级。
compilerOptions
用于配置组件模板的运行时编译器选项。
这个配置选项仅在使用完整构建版本(即可以在浏览器中编译模板的vue.js
文件)时才有效。它支持与应用级的app.config.compilerOptions
想通的选项,并针对当前组件有更高的优先级。
slots
一个在渲染函数中以编程方式使用插槽时辅助类型推断的选项。
该选项的运行时值不会被使用。实际类型应通过SlotsType
类型辅助工具进行类型转换来声明。
其他杂项选项
name
用于显式声明组件展示时的名称。
组件的名字有以下用途:
- 在组件自己的模板中递归引用自己时
- 在
Vue
开发者工具中的组件树显示时 - 在组件抛出的警告追踪栈信息中显示时
当你在使用单文件组件时,组件已经会根据其文件名推导出其名称。举例来说,一个名为MyComponent.vue
的文件会推导出显示名称为MyComponent
。
另一种场景是当一个组件通过app.component
被全局注册时,这个全局ID
就自动被设为了其名称。
使用name
选项使你可以覆盖推导出的名称,或是在没有推导出名字时显式提供一个。(例如没有使用构建工具时,或是一个内联的非单文件组件)
有一种场景下name
必须是已显式声明的:即<KeepAlive>
通过其include/exclude``prop
来匹配其需要缓存的组件时。
在
3.2.34
或以上的版本中,使用<script setup>
的单文件组件会自动根据文件名生成对应的name
选项,即使是在配合<KeepAlive>
使用时也无需再手动声明。
inheritAttrs
用于控制是否启用默认的组件attribute
透传行为。
默认情况下,父组件传递的,但没有被子组件解析为props
的attributes
绑定会被透传
。这意味着当我们有一个单根节点的子组件时,这些绑定会被作为一个常规的HTML attribute
应用在子组件的根节点元素上。当你编写的组件想要在一个目标元素或其他组件外面包一层时,可能并不期望这样的行为。我们可以通过设置inheritAttrs
为false
来禁用这个默认行为。这些attributes
可以通过$attrs
这个实例属性来访问,并且可以通过v-bind
来显式绑定在一个非根节点的元素上。
1 | // child.vue |
在一个使用了<script setup>
的组件中声明这个选项时,可以使用defineOptions
宏。
components
一个对象,用于注册对当前组件实例可用的组件。
directives
一个对象,用于注册对当前组件实例可用的指令。
组件实例
本节文档描述了组件公共实例(即
this
)上暴露的内置属性和方法,本节罗列的所有属性,除了$data
下的嵌套属性之外,都是只读的。
$data
从data
选项函数中返回的对象,会被组件赋为响应式。组件实例将会代理对其数据对象的属性访问。
$props
表示组件当前已解析的props
对象。
这里只包含通过props
选项声明的props
。组件实例将会代理对其props
对象上属性的访问。
$el
该组件实例管理的DOM
根节点。
$el
直到组件挂载完成mounted
之前都会是undefined
。
- 对于单一根元素的组件,
$el
将会指向该跟元素。 - 对于以文本节点为根的组件,
$el
将会指向该文本节点。 - 对于以多个元素为根的组件,
$el
将是一个仅作占位符的DOM
节点,Vue
使用它来跟踪组件在DOM
中的位置(文本节点或SSR
激活模式下的注释节点)。
为保持一致性,我们推荐使用模板引用来直接访问元素而不是依赖
$el
。
$options
已解析的用于实例化当前组件的组件选项。
这个$options
对象暴露了当前组件的已解析选项,并且会是以下几种可能来源的合并结果:
- 全局
mixin
- 组件
extends
的基组件 - 组件级
mixin
它通常用于支持自定义组件选项。
$parent
当前组件可能存在的父组件实例,如果当前组件是顶层组件,则为null
。
$root
当前组件树的根组件实例。如果当前实例没有父组件,那么这个值就是它自己。
$slots
一个表示父组件所传入插槽的对象。
通常用于手写渲染函数,但也可以用于检测是否存在插槽。
每一个插槽都在this.$slots
上暴露为一个函数,返回一个vnode
数组,同时Key
名对应这插槽名。默认插槽暴露为this.$slots.default
。
如果插槽是一个作用域插槽,传递给该插槽函数的参数可以作为插槽的props
提供给插槽。
$refs
一个包含DOM
元素和组件实例的对象,通过模板引用注册。
$attrs
一个包含了组件所有透传attributes
的对象。
透传attributes
是指由父组件传入,且没有被子组件声明为props
或是组件自定义事件的attributes
和事件处理函数。
默认情况下,若是单一根节点组件,$attrs
中的所有属性都是直接自动继承自组件的根元素。而多根节点组件则不会如此,同时你也可以通过配置inheritAttrs
选项来显式地关闭该行为。
$watch
用于命令式地创建侦听器的API
。
第一个参数是侦听来源。可以是一个组件的属性名的字符串,一个简单的由点分隔的路径字符串,或是一个getter
函数。
第二个参数是回调函数。它接收的参数分别是侦听来源的新值、旧值。
immediate
:指定在侦听器创建时是否立即触发回调。在第一次调用时旧值为undefined
。deep
:指定在侦听来源时一个对象时,是否强制深度遍历,这样回调函数就会在深层级发生变更时被触发。flush
:指定回调函数的刷新时机。onTrack/onTrigger
:调试侦听器的依赖。
$emit()
在当前组件触发一个自定义事件。任何额外的参数都会传递给事件监听器的回调函数。
$forceUpdate()
强制该组件重新渲染。
鉴于Vue
的全自动响应性系统,这个功能应该很少会被用到。唯一可能需要它的情况是,你使用高阶响应式API
显式创建了一个非响应式的组件状态。
$nextTick()
绑定在实例上的nextTick()
函数。
和全局版本的nextTick()
的唯一区别就是组件传递给this.$nextTick()
的回调函数会带上this
上下文,其绑定了当前组件实例。
内置指令
v-text
更新元素的文本内容。
- 期望的绑定值类型:
string
v-text
通过设置元素的textContent
属性来工作,因此它将覆盖元素中所有现有的内容。如果你需要更新textContent
的部分,应该使用mustache interpolations
代替。
v-html
更新元素的innerHTML
。
v-html
的内容直接作为普通HTML
插入——Vue
模板语法时不会被解析的。如果你发现自己正打算用v-html
来编写模板,不如重新想想怎么使用组件来代替。
在单文件组件,scoped
样式将不会作用于v-html
里的内容,因为HTML
内容不会被Vue
的模板编译器解析。如果你想让v-html
的内容也支持scoped CSS
,你可以使用CSS modules
或使用一个额外的全局<style>
元素,手动设置类似BEM
的作用域策略。
v-show
基于表达式值的真假性,来改变元素的可见性。
期望的绑定值类型:key
v-show
通过设置内联样式的display``CSS
属性来工作,当元素可见时将使用初始display
值。当条件改变时,也会触发过渡效果。
v-if
基于表达式值的真假性,来条件性地渲染元素或模板片段。
当v-if
元素被触发,元素及其所包含的指令/组件都会销毁和重构。如果初始条件是假,那么其内部的内容根本都不会被渲染。
可用于<template>
表示仅包含文本或多个元素的条件块。
当条件改变时会触发过渡效果。
当同时使用,v-if
比v-for
优先级更高。并不推荐在一元素上同时使用这两个指令。
v-else
表示v-if
或v-if
/v-else-if
链式调用的“else
块”。
- 无需传入表达式
- 限定:上一个兄弟元素必须有
v-if
或v-else-if
。 - 可用于
<template>
表示仅包含文本或多个元素的条件块。
v-else-if
表示v-if
的“else if
块”。可以进行链式调用。
- 限定:上一个兄弟必须有
v-if
或v-else-if
。 - 可用于
<template>
表示仅包含文本或多个元素的条件块。
v-for
基于原始数据多次渲染元素或模板块。
- 期望的绑定值类型:
Array|Object|number|string|Iterable
指令值必须使用特殊语法为正在迭代的元素提供一个别名,或者,也可以为索引指定别名(如果用在对象,则时键值)
v-for
的默认方式是尝试就地更新元素而不一定它们。要强制其重新排序元素,你需要用特殊attribute key
来提供一个排序提示。
v-for
也可以用于Iterable Protocol
的实现,包括原生Map
和Set
。
v-on
给元素绑定事件监听器。
- 缩写:
@
- 期望的绑定值类型:
Function|Inline Statement|Object(不带参数)
- 参数:
event
(使用对象发则为可选项) - 修饰符
.stop
-调用event.stopPropagation()
.prevent
-调用event.preventDefault()
.capture
-在捕获模式添加事件监听器.self
-只有事件从元素本身发出才触发处理函数.{keyAliase}
-只在某些按键下触发处理函数.once
-最多触发一次处理函数.left
-只在鼠标左键事件触发处理函数.right
-只在鼠标右键事件触发处理函数.middle
-只在鼠标中键事件触发处理函数.passive
-通过{passive: true}
附加一个DOM
事件
事件类型由参数来指定。表达式可以是一个方法名,一个内联声明,如果由修饰符则可省略。
当用于普通元素,只监听原生DOM
事件。当用于自定义元素组件,则监听子组件触发的自定义事件。
当监听原生DOM
事件时,方法接收原生事件作为唯一参数。如果使用内联声明,声明可以访问一个特殊的$event
变量:v-on:click="handle('ok',$event)"
。
v-on
还支持绑定不带参数的事件/监听器对的对象。请注意,当使用对象语法时,不支持任何修饰符。
1 | <!-- 方法处理函数 --> |
监听子组件的自定义事件(当子组件的my-event
事件被触发,处理函数将被调用)。
1 | <MyComponent @my-event="handleThis" /> |
v-bind
动态的绑定一个或多个attribute
,也可以是组件的prop
。
缩写:
:
或者.
(当使用.prop
修饰符)- 值可以省略(当
attribute
和绑定的值同名时,需要3.4+
版本) - 期望:
any(带参数)|Object(不带参数)
- 参数:
attrOrProp(可选的)
- 修饰符
.camel
-将短横线命名的attribute
转变为驼峰式命名。.prop
-强制绑定为DOM property(3.2+)
。.attr
-强制绑定为DOM attribute(3.2+)
。
- 用途
当用于绑定class
或style``attribute
,v-bind
支持额外的值类型如数组或对象。
在处理绑定时,Vue
默认会利用in
操作符来检查该元素上是否定义了和绑定的key
同名的DOM property
。如果存在同名的property
,则Vue
会将它作为DOM property
赋值,而不是作为attribute
设置。这个行为大多数情况都符合期望的绑定值类型,但是你也可以显式用.prop
和.attr
修饰符来强制绑定方式。有时这是必要的,特别是在和自定义元素打交道时。
当用于组件props
绑定时,所绑定的props
必须在子组件中已被正确声明。
当不带参数使用时,可以用于绑定一个包含了多个attribute
名称-绑定值对的对象。
1 | <!-- 绑定attribute --> |
prop
修饰符也有专门的缩写,.
:
1 | <div :someProperty.prop="someObject"></div> |
当在DOM
内模板使用.camel
修饰符,可以驼峰化v-bind``attribute
的名称,例如viewBox``attribute
:
1 | <svg :view-box.camel="viewBox"></svg> |
如果使用字符串模板或使用构建步骤预编译模板,则不需要.camel
。
v-model
在表单输入元素或组件上创建双向绑定。
- 期望的绑定值类型:根据表单输入元素或组件输出的值而变化
- 仅限:
<input>
<select>
<textarea>
components
- 修饰符
.lazy
-监听change
事件而不是input
.number
-将输入的合法字符串转为数字.trim
-移除输入内容两端空格
v-slot
用于声明具名插槽或是期望接收props
的作用域插槽。
- 缩写:
#
- 期望的绑定值类型:能够合法在函数参数位置使用的
JavaScript
表达式。支持解构语法。绑定值是可选的——只有在给作用域插槽传递props
才需要。 - 参数:插槽名(可选,默认是
default
) - 仅限:
<template>
component
(用于带有prop
的单个默认插槽)
1 | <!-- 具名插槽 --> |
v-pre
跳过该元素及其所有子元素的编译。
- 无需传入
元素内具有v-pre
,所有Vue
模板语法都会被保留并按原样渲染。最常见的用例就是显式原始双大括号标签及内容。
1 | <span v-pre>{{ this will not be compiled }}</span> |
v-once
仅渲染元素和组件一次,并跳过之后的更新。
- 无需传入
在随后的重新渲染,元素/组件及其所有子项将被当做静态内容并跳过渲染。这可以用来优化更新时的性能。
从3.2
起,你也可以搭配v-memo
的无效条件来缓存部分模板。
v-memo
- 仅在
3.2+
中支持 - 期望的绑定值类型:
any[]
缓存一个模板的子树。在元素和组件上都可以使用。为了实现缓存,该指令需要传入一个固定长度的依赖值数组进行比较。如果数组里的每个值都与最后一次的渲染相同,那么整个子树的更新将被跳过。
1 | <div v-memo="[valueA, valueB]"></div> |
当组件重新渲染,如果valueA
和valueB
都保持不变,这个<div>
及其子项的所有更新都将被跳过。实际上,甚至虚拟DOM
的vnode
创建也将被跳过,因为缓存的子树副本可以被重新使用。
正确指定缓存数组很重要,否则应该生效的更新可能被跳过。v-memo
传入空数组(v-memo="[]"
)将与v-once
效果相同。
与v-for
一起使用
v-memo
仅用于性能至上场景中的微小优化,应该很少需要。最常见的情况可能是有助于渲染海量v-for
列表(长度超过1000
的情况)。
1 | <div v-for="item in list" :key="item.id" v-memo="[item.id === selected]"> |
当组件的selected
状态改变,默认会重新创建大量的vnode
,尽管绝大部分都跟之前是一模一样的。v-memo
用在这里本质上是在说“只有当该项的被选中状态改变时才需要更新”。这使得每个选中状态没有变的项能完全重用之前的vnode
并跳过差异比较。注意这里memo
依赖数组中并不需要包含item.id
,因为Vue
也会根据item
的:key
进行判断。
当搭配
v-for
使用v-memo
,确保两者都绑定在同一个元素上。**v-memo
不能用在v-for
内部**。
v-memo
也能被用于在一些默认优化失败的边际情况下,手动避免子组件出现不需要的更新。
但是一样的,开发者需要负责指定正确的依赖数组以免跳过必要的更新。
v-cloak
用于隐藏尚未完成编译的DOM
模板。
该指令只在没有构建步骤的环境下需要使用。
当使用直接在DOM
中书写的模板时,可能会出现一种叫做“为编译模板闪现”的情况:用户可能先看到的是还没编译完成的双大括号标签,直到挂载的组件将它们替换为时机渲染的内容。
v-cloak
会保留在所绑定的元素上,直到相关组件实例被挂载后才移除。配合像[v-cloak] {display: none}
这样的CSS
规则,它可以在组件编译完毕前隐藏原始模板。
1 | <div v-cloak>{{ message }}</div> |
内置组件
组件注册和使用
内置组件无需注册便可以直接在模板中使用。它们也支持tree-shake
:仅在使用时才会包含在构建中。
在渲染函数中使用它们时,需要显式导入。
<Transition>
为单个元素或组件提供动画过渡效果。
props
1 | interface TransitionProps { |
- 事件
@before-enter
@before-leave
@enter
@leave
@appear
@after-enter
@after-leave
@after-appear
@enter-cancelled
@leave-cancelled(v-show only)
@appear-cancelled
通过改变key
属性来强制过渡。