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属性来强制过渡执行。
1 | <Transition> |
动态组件,初始渲染时带有过渡模式+动画出现:
1 | <Transition name="fade" mode="out-in" appear> |
监听过渡事件
1 | <Transition @after-enter="onTransitionComplete"> |
<TransitionGroup>
为列表中的多个元素或组件提供过渡效果
props
<TransitionGroup>拥有与<Transition>除了mode以外所有的props,并增加了两个额外的props
默认情况下,<TransitionGroup>不会渲染一个容器的DOM元素,但是可以通过tagprop启用。
注意,每个<transition-group>的子节点必须有**独立的key**,动画才能正常工作。
<TransitionGroup>支持通过CSS transform控制移动效果。当一个子节点在屏幕上的位置在更新之后发生变化时,它会被添加一个使其位移的CSS class(基于name attribute推导,或使用move-classprop显式配置)。如果使其位移的class被添加时CSS的transform属性是“可过渡的”,那么该元素会基于**FLIP技巧**平滑地达到动画终点。
1 | <TransitionGroup tag="ul" name="slide"> |
<KeepAlive>
缓存包裹在其中的动态切换组件。
<KeepAlive>包裹动态组件时,会缓存不活跃的组件实例,而不是销毁它们。
任何时候都只能有一个活跃组件实例作为<KeepAlive>的直接子节点。
当一个组件在<KeepAlive>中被切换时,它的activated和deactivated声明周期钩子将被调用,用来替代mounted和unmounted。这适用于<KeepAlive>的直接子节点及其所有子孙节点。
<Teleport>
将其插槽内容渲染到DOM中的另一个位置。
1 | <Teleport to="#some-id" /> |
有条件的禁用:
1 | <Teleport to="#popup" :disabled="displayVideoInline"> |
延迟目标解析3.5+
1 | <Teleport defer to="#late-div">...</Teleport> |
内置特殊元素
不是组件
component、slot和template具有类似组件的特性,也是模板语法的一部分。但它们并非真正的组件,同时在模板编译期间会被编译掉。因此,它们通常在模板中用小写字母书写。
component
一个用于渲染动态组件或元素的“元组件”。
要渲染的实际组件由isprop决定。
- 当
is是字符串,它既可以是HTML标签名也可以是组件的注册名 - 或者,
is也可以直接绑定到组件的定义
按注册名渲染组件:
1 | <template> |
按定义渲染组件:
1 | <template> |
内置组件都可以传递给is,但是如果想通过名称传递则必须先对其进行注册。
1 | <template> |
如果将组件本身传递给is而不是其名称,则不需要注册。
如果在<component>标签上使用v-model,模板编译器会将其扩展为modelValueprop和update:modelValue事件监听器,就像对任何其他组件一样。因此,在动态创建的原生元素上使用v-model将不起作用。
<slot>
表示模板中的插槽内容出口。
<slot>元素可以使用name``attribute来指定插槽名。当没有指定name时,将会渲染默认插槽。传递给插槽元素的附加attributes将作为插槽props,传递给父级中定义的作用域插槽。
元素本身将被其所匹配的插槽内容替换。
Vue模板里的<slot>元素会被编译到JavaScript,因此不要与原生<slot>元素进行混淆。
<template>
当我们想要使用内置指令而不在DOM中渲染元素时,<template>标签可以作为占位符使用。
对<template>的特殊处理只有在它与以下任一指令一起使用时才会被触发:
v-if、v-else-if或v-elsev-forv-slot
如果这些指令都不存在,那么它将被渲染成一个原生的<template>元素。
带有v-for的<template>也可以有一个key属性。所有其他的属性和指令都将被丢弃,因为没有相应的元素,它们就没有意义。
单文件组件使用顶层的<template>标签来包裹整个模板。该顶层标签不是模板本身的一部分,不支持指令等模板语法。
内置的特殊Attributes
key
key这个特殊的attribute主要作为Vue的虚拟DOM算法提示,在比较新旧节点列表时用于识别vnode。
在没有key的情况下,Vue将使用一种最小化元素移动的算法,并尽可能地就地更新/复用相同类型的元素。如果传了key,则将根据key的变化顺序来重新排列元素,并且将始终移除/销毁key已经不存在的元素。
同一个父元素下的子元素必须具有**唯一的key**。重复的key将会导致渲染异常。
也可以用于强制替换一个元素/组件而不是复用它。
- 在适当的时候触发组件的生命周期钩子
- 触发过渡
1 | <transition> |
当text变化时,<span>总是会被替换而不是更新,因此transition将会被触发。
ref
用于注册模板引用。
ref用于注册元素或子组件的引用。
使用选项式API,引用将被注册在组件的this.$refs对象里。
使用组合式API,引用将存储在与名字匹配的ref里。
如果用于普通DOM元素,引用将是元素本身;如果用于子组件,引用将是子组件的实力。
或者ref可以接收一个函数值,用于对存储引用位置的完全控制。
1 | <ChildComponent :ref="el => (child = el)" /> |
关于ref注册时机的重要说明:因为ref本身是作为渲染函数的结果来创建的,必须等待组件挂载后才能对它进行访问。
this.$refs也是非响应式的,因此你不应该尝试在模板中使用它来进行数据绑定。
is
用于绑定动态组件。







