响应式: 进阶
customRef()
创建一个自定义的 ref,显式声明对其依赖追踪和更新触发的控制方式。
-
类型
function customRef<T>(factory: CustomRefFactory<T>): Ref<T>type CustomRefFactory<T> = (track: () => void,trigger: () => void ) => {get: () => Tset: (value: T) => void }
-
详细信息
customRef()
预期接收一个工厂函数作为参数,这个工厂函数接受track
和trigger
两个函数作为参数,并返回一个带有get
和set
方法的对象。一般来说,
track()
应该在get()
方法中调用,而trigger()
应该在set()
中调用。然而事实上,你对何时调用、是否应该调用他们有完全的控制权。 -
示例
创建一个防抖 ref,即只在最近一次 set 调用后的一段固定间隔后再调用:
import { customRef } from 'vue'export function useDebouncedRef(value, delay = 200) {let timeoutreturn customRef((track, trigger) => {return {get() {track() //告诉Vue数据msg很重要你要对msg进行持续关注,一旦msg变化就去更新return value},set(newValue) {clearTimeout(timeout)timeout = setTimeout(() => {value = newValuetrigger() //通知vue,数据变化了}, delay)}}}) }
在组件中使用:
<script setup> import { useDebouncedRef } from './debouncedRef' const text = useDebouncedRef('hello') </script><template><input v-model="text" /> </template>
内置组件
Teleport(瞬间移动组件)
Teleport
直译是 “传送”,作用是 将组件的部分内容 “传送” 到 DOM 树中的指定位置,而不受父组件的 DOM 层级限制。这在处理模态框、弹窗、通知等组件时非常有用,可避免父组件的样式(如 overflow: hidden
、z-index
)对其产生影响。
核心作用
打破组件的 DOM 层级限制,让子元素渲染到页面的任意位置(通常是 <body>
或专门的容器),解决样式隔离和层级覆盖问题。
使用语法
<teleport to="目标DOM选择器"><!-- 需要传送的内容 --><div class="modal">这是一个弹窗</div>
</teleport>
to
:必填属性,指定目标位置(如body
、#app
、.modal-container
等有效的 CSS 选择器)。
示例:弹窗组件
<!-- Modal.vue -->
<template><teleport to="body"><div class="modal-overlay"><div class="modal-content"><h3>弹窗标题</h3><p>弹窗内容</p><button @click="close">关闭</button></div></div></teleport>
</template><script setup>
const close = () => {// 关闭逻辑
};
</script><style>
.modal-overlay {position: fixed; /* 不受父组件定位影响 */top: 0;left: 0;right: 0;bottom: 0;background: rgba(0,0,0,0.5);z-index: 999; /* 确保在最上层 */
}
</style>
特点
- 内容仍属于原组件:虽然 DOM 位置变了,但组件的生命周期、事件处理、数据绑定仍受原组件控制(如
close
方法仍能访问原组件的状态)。 - 条件渲染:可配合
v-if
控制是否渲染(如<teleport to="body" v-if="showModal">
)。 - 多个 Teleport 到同一目标:目标位置会按顺序追加内容,不会覆盖。
Suspense(异步加载管理器)- 实验阶段
Suspense
用于 管理异步组件或带有异步操作的组件,提供 “加载中” 和 “加载完成 / 失败” 的状态处理,简化异步内容的渲染逻辑。
核心作用
等待异步操作(如异步组件加载、setup
中的异步请求)完成后再渲染组件,并在等待期间显示占位内容(如加载动画)。
使用前提
- 异步组件:通过
defineAsyncComponent
定义的组件。 - 组件的
setup
函数返回 Promise(或使用await
异步获取数据)。
使用语法
<suspense><!-- 异步内容(成功状态) --><template #default><AsyncComponent /> <!-- 异步组件或带异步操作的组件 --></template><!-- 加载中状态(等待时显示) --><template #fallback><div>加载中...</div></template>
</suspense>
#default
:异步操作完成后渲染的内容。#fallback
:异步操作等待期间显示的占位内容。
示例 1:异步组件
<!-- 父组件 -->
<template><suspense><template #default><AsyncUser /> <!-- 异步加载的组件 --></template><template #fallback><div>加载用户组件中...</div></template></suspense>
</template><script setup>
import { defineAsyncComponent } from 'vue';// 定义异步组件(动态导入)
const AsyncUser = defineAsyncComponent(() => import('./AsyncUser.vue'));
</script>
示例 2:组件内异步数据
<!-- AsyncData.vue(子组件) -->
<template><div>用户数据:{{ user.name }}</div>
</template><script setup>
// setup 中使用 await 异步获取数据
const fetchUser = async () => {const res = await fetch('/api/user');return res.json();
};const user = await fetchUser(); // 异步操作
</script><!-- 父组件中使用 -->
<template><suspense><template #default><AsyncData /></template><template #fallback><div>加载数据中...</div></template></suspense>
</template>
注意事项
- 错误处理:
Suspense
本身不处理错误,需配合onErrorCaptured
或错误边界组件(Error Boundary)捕获异步操作中的错误。 - 仅支持顶层异步:
Suspense
只能捕获其直接子组件的异步操作,深层嵌套组件的异步逻辑不会触发fallback
。 - 与路由结合:在 Vue Router 中,可将异步组件作为路由组件,配合
Suspense
实现路由切换时的加载状态。
总结
特性 | 核心功能 | 典型场景 |
---|---|---|
Teleport |
将内容渲染到指定 DOM 位置 | 模态框、弹窗、通知组件 |
Suspense |
管理异步操作的加载状态 | 异步组件、数据请求的加载提示 |
两者均为 Vue 3 新增特性,Teleport
解决了 DOM 层级限制问题,Suspense
简化了异步内容的状态管理,合理使用可显著提升组件的灵活性和用户体验。
全局API
app.component()
如果同时传递一个组件名字符串及其定义,则注册一个全局组件;如果只传递一个名字,则会返回用该名字注册的组件 (如果存在的话)。
-
类型
interface App {component(name: string): Component | undefinedcomponent(name: string, component: Component): this }
-
示例
import { createApp } from 'vue'const app = createApp({})// 注册一个选项对象 app.component('MyComponent', {/* ... */ })// 得到一个已注册的组件 const MyComponent = app.component('MyComponent')
app.config
每个应用实例都会暴露一个 config
对象,其中包含了对这个应用的配置设定。你可以在挂载应用前更改这些属性 (下面列举了每个属性的对应文档)。
import { createApp } from 'vue'const app = createApp(/* ... */)console.log(app.config)