VueApi
约 38480 字大约 128 分钟
2025-08-30
渐进式 JavaScript 框架。易学易用,性能出色,适用场景丰富的 Web 前端框架。
全局 API 🐳
全局 API照片
应用实例 API
应用实例 API 介绍
createApp()
创建一个应用实例。
类型
function createApp(rootComponent: Component, rootProps?: object): App详细信息
第一个参数是根组件。第二个参数可选,它是要传递给根组件的 props。
示例
可以直接内联根组件:
import { createApp } from 'vue' const app = createApp({ /* root component options */ })也可以使用从别处导入的组件:
// 实际使用占比高 import { createApp } from 'vue' import App from './App.vue' const app = createApp(App)
createSSRApp()
以 SSR 激活模式创建一个应用实例。用法与 createApp() 完全相同。
app.mount()
将应用实例挂载在一个容器元素中。
类型
interface App { mount(rootContainer: Element | string): ComponentPublicInstance }详细信息
参数可以是一个实际的 DOM 元素或一个 CSS 选择器 (使用第一个匹配到的元素)。返回根组件的实例。
如果该组件有模板或定义了渲染函数,它将替换容器内所有现存的 DOM 节点。否则在运行时编译器可用的情况下,容器元素的
innerHTML将被用作模板。在 SSR 激活模式下,它将激活容器内现有的 DOM 节点。如果出现了激活不匹配,那么现有的 DOM 节点将会被修改以匹配客户端的实际渲染结果。
对于每个应用实例,
mount()仅能调用一次。示例
import { createApp } from 'vue' // 创建一个应用实例 const app = createApp(/* ... */) // 将这个应用实例 挂载在id为app的容器中 app.mount('#app')也可以挂载到一个实际的 DOM 元素。
app.mount(document.body.firstChild)
app.unmount()
卸载一个已挂载的应用实例。卸载一个应用会触发该应用组件树内所有组件的卸载生命周期钩子。
类型
interface App { unmount(): void }
app.component()
如果同时传递一个组件名字符串及其定义,则注册一个全局组件;如果只传递一个名字,则会返回用该名字注册的组件 (如果存在的话)。
类型
interface App { component(name: string): Component | undefined component(name: string, component: Component): this }示例
import { createApp } from 'vue' const app = createApp({}) // 注册一个选项对象 app.component('my-component', { /* ... */ }) // 得到一个已注册的组件 const MyComponent = app.component('my-component')参考:组件注册
app.directive()
如果同时传递一个名字和一个指令定义,则注册一个全局指令;如果只传递一个名字,则会返回用该名字注册的指令 (如果存在的话)。
类型
interface App { directive(name: string): Directive | undefined directive(name: string, directive: Directive): this }示例
import { createApp } from 'vue' const app = createApp({ /* ... */ }) // 注册(对象形式的指令) app.directive('my-directive', { /* 自定义指令钩子 */ }) // 注册(函数形式的指令) app.directive('my-directive', () => { /* ... */ }) // 得到一个已注册的指令 const myDirective = app.directive('my-directive')参考:自定义指令
app.use()
安装一个插件。
类型
interface App { use(plugin: Plugin, ...options: any[]): this }详细信息
第一个参数应是插件本身,可选的第二个参数是要传递给插件的选项。
插件可以是一个带
install()方法的对象,亦或直接是一个将被用作install()方法的函数。插件选项 (app.use()的第二个参数) 将会传递给插件的install()方法。若
app.use()对同一个插件多次调用,该插件只会被安装一次。示例
import { createApp } from 'vue' import MyPlugin from './plugins/MyPlugin' const app = createApp({ /* ... */ }) app.use(MyPlugin)参考:插件
app.mixin()
应用一个全局 mixin (适用于该应用的范围)。一个全局的 mixin 会作用于应用中的每个组件实例。
不推荐
Mixins 在 Vue 3 支持主要是为了向后兼容,因为生态中有许多库使用到。在新的应用中应尽量避免使用 mixin,特别是全局 mixin。
若要进行逻辑复用,推荐用组合式函数来替代。
类型
interface App { mixin(mixin: ComponentOptions): this }
app.provide()
提供一个值,可以在应用中的所有后代组件中注入使用。
类型
interface App { provide<T>(key: InjectionKey<T> | symbol | string, value: T): this }详细信息
第一个参数应当是注入的 key,第二个参数则是提供的值。返回应用实例本身。
示例
import { createApp } from 'vue' const app = createApp(/* ... */) app.provide('message', 'hello')在应用的某个组件中:
import { inject } from 'vue' export default { setup() { console.log(inject('message')) // 'hello' } }参考:
app.runWithContext()3.3+
使用当前应用作为注入上下文执行回调函数。
类型
interface App { runWithContext<T>(fn: () => T): T }详情
需要一个回调函数并立即运行该回调。在回调同步调用期间,即使没有当前活动的组件实例,
inject()调用也可以从当前应用提供的值中查找注入。回调的返回值也将被返回。示例
import { inject } from 'vue' app.provide('id', 1) const injected = app.runWithContext(() => { return inject('id') }) console.log(injected) // 1
app.version
提供当前应用所使用的 Vue 版本号。这在插件中很有用,因为可能需要根据不同的 Vue 版本执行不同的逻辑。
类型
interface App { version: string }示例
在一个插件中对版本作判断:
export default { install(app) { const version = Number(app.version.split('.')[0]) if (version < 3) { console.warn('This plugin requires Vue 3') } } }
app.config
每个应用实例都会暴露一个 config 对象,其中包含了对这个应用的配置设定。你可以在挂载应用前更改这些属性 (下面列举了每个属性的对应文档)。
import { createApp } from 'vue'
const app = createApp(/* ... */)
console.log(app.config)app.config.errorHandler
用于为应用内抛出的未捕获错误指定一个全局处理函数。
类型
interface AppConfig { errorHandler?: ( err: unknown, instance: ComponentPublicInstance | null, // `info` 是一个 Vue 特定的错误信息 // 例如:错误是在哪个生命周期的钩子上抛出的 info: string ) => void }详细信息
错误处理器接收三个参数:错误对象、触发该错误的组件实例和一个指出错误来源类型信息的字符串。
它可以从下面这些来源中捕获错误:
- 组件渲染器
- 事件处理器
- 生命周期钩子
setup()函数- 侦听器
- 自定义指令钩子
- 过渡 (Transition) 钩子
示例
app.config.errorHandler = (err, instance, info) => { // 处理错误,例如:报告给一个服务 }
app.config.warnHandler
用于为 Vue 的运行时警告指定一个自定义处理函数。
类型
interface AppConfig { warnHandler?: ( msg: string, instance: ComponentPublicInstance | null, trace: string ) => void }详细信息
警告处理器将接受警告信息作为其第一个参数,来源组件实例为第二个参数,以及组件追踪字符串作为第三个参数。
这可以用户过滤筛选特定的警告信息,降低控制台输出的冗余。所有的 Vue 警告都需要在开发阶段得到解决,因此仅建议在调试期间选取部分特定警告,并且应该在调试完成之后立刻移除。
警告仅会在开发阶段显示,因此在生产环境中,这条配置将被忽略。
示例
app.config.warnHandler = (msg, instance, trace) => { // `trace` is the component hierarchy trace }
app.config.performance
设置此项为 true 可以在浏览器开发工具的“性能/时间线”页中启用对组件初始化、编译、渲染和修补的性能表现追踪。仅在开发模式和支持 performance.mark API 的浏览器中工作。
- 类型
boolean - 参考:指南 - 性能
app.config.compilerOptions
配置运行时编译器的选项。设置在此对象上的值将会在浏览器内进行模板编译时使用,并会影响到所配置应用的所有组件。另外你也可以通过 compilerOptions 选项在每个组件的基础上覆盖这些选项。
此配置项仅在完整构建版本,即可以在浏览器中编译模板的
vue.js文件中可用。如果你用的是带构建的项目配置,且使用的是仅含运行时的 Vue 文件版本,那么编译器选项必须通过构建工具的相关配置传递给@vue/compiler-dom。
vue-loader:通过compilerOptionsloader 的选项传递。并请阅读如何在vue-cli中配置它。
app.config.compilerOptions.isCustomElement
用于指定一个检查方法来识别原生自定义元素。
类型
(tag: string) => boolean详细信息
如果该标签需要当作原生自定义元素则应返回
true。对匹配到的标签,Vue 会将其渲染为原生元素而非将其视为一个 Vue 组件来解析。原生 HTML 和 SVG 标签不需要在此函数中进行匹配,Vue 的解析器会自动识别它们。
示例
// 将所有标签前缀为 `ion-` 的标签视为自定义元素 app.config.compilerOptions.isCustomElement = (tag) => { return tag.startsWith('ion-') }
app.config.compilerOptions.whitespace
用于调整模板中空格的处理行为。
类型
'condense' | 'preserve'默认
'condense'详细信息
Vue 移除/缩短了模板中的空格以求更高效的模板输出。默认的策略是“缩短”,表现行为如下:
- 元素中开头和结尾的空格字符将被缩短为一个空格。
- 包含换行的元素之间的空白字符会被删除。
- 文本节点中连续的空白字符被缩短成一个空格。
设置该选项为
'preserve'则会禁用 (2) 和 (3) 两项。示例
app.config.compilerOptions.whitespace = 'preserve'
app.config.compilerOptions.delimiters
用于调整模板内文本插值的分隔符。
类型
[string, string]默认
['{{', '}}']详细信息
此项通常是为了避免与同样使用 mustache 语法的服务器端框架发生冲突。
示例
// 分隔符改为ES6模板字符串样式 app.config.compilerOptions.delimiters = ['${', '}']
app.config.compilerOptions.comments
用于调整是否移除模板中的 HTML 注释。
类型
boolean默认
false详细信息
默认情况下,Vue 会在生产环境移除所有注释,设置该项为
true会强制 Vue 在生产环境也保留注释。在开发过程中,注释是始终被保留的。这个选项通常在 Vue 与其他依赖 HTML 注释的库一起使用时使用。示例
app.config.compilerOptions.comments = true
app.config.globalProperties
一个用于注册能够被应用内所有组件实例访问到的全局属性的对象。
类型
interface AppConfig { globalProperties: Record<string, any> }详细信息
这是对 Vue 2 中
Vue.prototype使用方式的一种替代,此写法在 Vue 3 已经不存在了。与任何全局的东西一样,应该谨慎使用。如果全局属性与组件自己的属性冲突,组件自己的属性将具有更高的优先级。
用法
app.config.globalProperties.msg = 'hello'这使得
msg在应用的任意组件模板上都可用,并且也可以通过任意组件实例的this访问到:export default { mounted() { console.log(this.msg) // 'hello' } }
app.config.optionMergeStrategies
一个用于定义自定义组件选项的合并策略的对象。
类型
interface AppConfig { optionMergeStrategies: Record<string, OptionMergeFunction> } type OptionMergeFunction = (to: unknown, from: unknown) => any详细信息
一些插件或库对自定义组件选项添加了支持 (通过注入全局 mixin)。这些选项在有多个不同来源时可能需要特殊的合并策略 (例如 mixin 或组件继承)。
可以在
app.config.optionMergeStrategies对象上以选项的名称作为 key,可以为一个自定义选项注册分配一个合并策略函数。合并策略函数分别接受在父实例和子实例上定义的该选项的值作为第一和第二个参数。
示例
const app = createApp({ // option from self msg: 'Vue', // option from a mixin mixins: [ { msg: 'Hello ' } ], mounted() { // 在 this.$options 上暴露被合并的选项 console.log(this.$options.msg) } }) // 为 `msg` 定义一个合并策略函数 app.config.optionMergeStrategies.msg = (parent, child) => { return (parent || '') + (child || '') } app.mount('#app') // 打印 'Hello Vue'
通用
通用 介绍
version
暴露当前所使用的 Vue 版本。
类型
string示例
import { version } from 'vue' console.log(version)
nextTick()
等待下一次 DOM 更新刷新的工具方法。
类型
function nextTick(callback?: () => void): Promise<void>详细信息
当你在 Vue 中更改响应式状态时,最终的 DOM 更新并不是同步生效的,而是由 Vue 将它们缓存在一个队列中,直到下一个“tick”才一起执行。这样是为了确保每个组件无论发生多少状态改变,都仅执行一次更新。
nextTick()可以在状态改变后立即使用,以等待 DOM 更新完成。你可以传递一个回调函数作为参数,或者 await 返回的 Promise。示例
<script setup> import { ref, nextTick } from 'vue' const count = ref(0) async function increment() { count.value++ // DOM 还未更新 console.log(document.getElementById('counter').textContent) // 0 await nextTick() // DOM 此时已经更新 console.log(document.getElementById('counter').textContent) // 1 } </script> <template> <button id="counter" @click="increment">{{ count }}</button> </template>
defineComponent()
在定义 Vue 组件时提供类型推导的辅助函数。
类型
// 选项语法 function defineComponent( component: ComponentOptions ): ComponentConstructor // 函数语法 (需要 3.3+) function defineComponent( setup: ComponentOptions['setup'], extraOptions?: ComponentOptions ): () => any为了便于阅读,对类型进行了简化。
详细信息
第一个参数是一个组件选项对象。返回值将是该选项对象本身,因为该函数实际上在运行时没有任何操作,仅用于提供类型推导。
注意返回值的类型有一点特别:它会是一个构造函数类型,它的实例类型是根据选项推断出的组件实例类型。这是为了能让该返回值在 TSX 中用作标签时提供类型推导支持。
你可以像这样从
defineComponent()的返回类型中提取出一个组件的实例类型 (与其选项中的this的类型等价):const Foo = defineComponent(/* ... */) type FooInstance = InstanceType<typeof Foo>函数签名3.3+
defineComponent()还有一种备用签名,旨在与组合式 API 和 渲染函数或 JSX 一起使用。与传递选项对象不同的是,它需要传入一个函数。这个函数的工作方式与组合式 API 的
setup()函数相同:它接收 props 和 setup 上下文。返回值应该是一个渲染函数——支持h()和 JSX:import { ref, h } from 'vue' const Comp = defineComponent( (props) => { // 就像在 <script setup> 中一样使用组合式 API const count = ref(0) return () => { // 渲染函数或 JSX return h('div', count.value) } }, // 其他选项,例如声明 props 和 emits。 { props: { /* ... */ } } )此签名的主要用例是使用 TypeScript (特别是使用 TSX ),因为它支持泛型:
const Comp = defineComponent( <T extends string | number>(props: { msg: T; list: T[] }) => { // 就像在 <script setup> 中一样使用组合式 API const count = ref(0) return () => { // 渲染函数或 JSX return <div>{count.value}</div> } }, // 目前仍然需要手动声明运行时的 props { props: ['msg', 'list'] } )在将来,我们计划提供一个 Babel 插件,自动推断并注入运行时 props (就像在 SFC 中的
defineProps一样),以便省略运行时 props 的声明。webpack Treeshaking 的注意事项
因为
defineComponent()是一个函数调用,所以它可能被某些构建工具认为会产生副作用,如 webpack。即使一个组件从未被使用,也有可能不被 tree-shake。为了告诉 webpack 这个函数调用可以被安全地 tree-shake,我们可以在函数调用之前添加一个
/*#__PURE__*/形式的注释:export default /*#__PURE__*/ defineComponent(/* ... */)请注意,如果你的项目中使用的是 Vite,就不需要这么做,因为 Rollup (Vite 底层使用的生产环境打包工具) 可以智能地确定
defineComponent()实际上并没有副作用,所以无需手动注释。
defineAsyncComponent()
定义一个异步组件,它在运行时是懒加载的。参数可以是一个异步加载函数,或是对加载行为进行更具体定制的一个选项对象。
类型
function defineAsyncComponent( source: AsyncComponentLoader | AsyncComponentOptions ): Component type AsyncComponentLoader = () => Promise<Component> interface AsyncComponentOptions { loader: AsyncComponentLoader loadingComponent?: Component errorComponent?: Component delay?: number timeout?: number suspensible?: boolean onError?: ( error: Error, retry: () => void, fail: () => void, attempts: number ) => any }参考:指南 - 异步组件
defineCustomElement()
这个方法和 defineComponent 接受的参数相同,不同的是会返回一个原生自定义元素类的构造器。
类型
function defineCustomElement( component: | (ComponentOptions & { styles?: string[] }) | ComponentOptions['setup'] ): { new (props?: object): HTMLElement }为了便于阅读,对类型进行了简化。
详细信息
除了常规的组件选项,
defineCustomElement()还支持一个特别的选项styles,它应该是一个内联 CSS 字符串的数组,所提供的 CSS 会被注入到该元素的 shadow root 上。返回值是一个可以通过
customElements.define()注册的自定义元素构造器。示例
import { defineCustomElement } from 'vue' const MyVueElement = defineCustomElement({ /* 组件选项 */ }) // 注册自定义元素 customElements.define('my-vue-element', MyVueElement)参考:
- 指南 - 使用 Vue 构建自定义元素
- 另外请注意在使用单文件组件时
defineCustomElement()需要特殊的配置。
组合式 API 🐳
组合式 API照片
setup()
setup() 介绍
基本使用
setup() 钩子是在组件中使用组合式 API 的入口,通常只在以下情况下使用:
- 需要在非单文件组件中使用组合式 API 时。
- 需要在基于选项式 API 的组件中集成基于组合式 API 的代码时。
注意: 对于结合单文件组件使用的组合式 API,推荐通过<script setup>以获得更加简洁及符合人体工程学的语法。
我们可以使用响应式 API 来声明响应式的状态,在 setup() 函数中返回的对象会暴露给模板和组件实例。其他的选项也可以通过组件实例来获取 setup() 暴露的属性:
<template>
<button @click="count++">{{ count }}</button>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
// 返回值会暴露给模板和其他的选项式 API 钩子
return {
count
}
},
mounted() {
console.log(this.count) // 0
}
}
</script>在模板中访问从 setup 返回的 ref 时,它会自动浅层解包,因此你无须再在模板中为它写 .value。当通过 this 访问时也会同样如此解包。
setup() 自身并不含对组件实例的访问权,即在 setup() 中访问 this 会是 undefined。你可以在选项式 API 中访问组合式 API 暴露的值,但反过来则不行。
setup() 应该同步地返回一个对象。唯一可以使用 async setup() 的情况是,该组件是 Suspense 组件的后裔。
访问Props
setup 函数的第一个参数是组件的 props。和标准的组件一致,一个 setup 函数的 props 是响应式的,并且会在传入新的 props 时同步更新。
export default {
props: {
title: String
},
setup(props) {
console.log(props.title)
}
}请注意如果你解构了 props 对象,解构出的变量将会丢失响应性。因此我们推荐通过 props.xxx 的形式来使用其中的 props。
如果你确实需要解构 props 对象,或者需要将某个 prop 传到一个外部函数中并保持响应性,那么你可以使用 toRefs() 和 toRef() 这两个工具函数:
import { toRefs, toRef } from 'vue'
export default {
setup(props) {
// 将 `props` 转为一个其中全是 ref 的对象,然后解构
const { title } = toRefs(props)
// `title` 是一个追踪着 `props.title` 的 ref
console.log(title.value)
// 或者,将 `props` 的单个属性转为一个 ref
const title = toRef(props, 'title')
}
}Setup 上下文
传入 setup 函数的第二个参数是一个 Setup 上下文对象。上下文对象暴露了其他一些在 setup 中可能会用到的值:
export default {
setup(props, context) {
// 透传 Attributes(非响应式的对象,等价于 $attrs)
console.log(context.attrs)
// 插槽(非响应式的对象,等价于 $slots)
console.log(context.slots)
// 触发事件(函数,等价于 $emit)
console.log(context.emit)
// 暴露公共属性(函数)
console.log(context.expose)
}
}该上下文对象是非响应式的,可以安全地解构:
export default {
setup(props, { attrs, slots, emit, expose }) {
...
}
}attrs 和 slots 都是有状态的对象,它们总是会随着组件自身的更新而更新。这意味着你应当避免解构它们,并始终通过 attrs.x 或 slots.x 的形式使用其中的属性。此外还需注意,和 props 不同,attrs 和 slots 的属性都不是响应式的。如果你想要基于 attrs 或 slots 的改变来执行副作用,那么你应该在 onBeforeUpdate 生命周期钩子中编写相关逻辑。
暴露公共属性
expose 函数用于显式地限制该组件暴露出的属性,当父组件通过模板引用访问该组件的实例时,将仅能访问 expose 函数暴露出的内容:
export default {
setup(props, { expose }) {
// 让组件实例处于 “关闭状态”
// 即不向父组件暴露任何东西
expose()
const publicCount = ref(0)
const privateCount = ref(0)
// 有选择地暴露局部状态
expose({ count: publicCount })
}
}与渲染函数一起使用
setup 也可以返回一个渲染函数,此时在渲染函数中可以直接使用在同一作用域下声明的响应式状态:
import { h, ref } from 'vue'
export default {
setup() {
const count = ref(0)
return () => h('div', count.value)
}
}返回一个渲染函数将会阻止我们返回其他东西。对于组件内部来说,这样没有问题,但如果我们想通过模板引用将这个组件的方法暴露给父组件,那就有问题了。
我们可以通过调用 expose() 解决这个问题:
import { h, ref } from 'vue'
export default {
setup(props, { expose }) {
const count = ref(0)
const increment = () => ++count.value
expose({
increment
})
return () => h('div', count.value)
}
}此时父组件可以通过模板引用来访问这个 increment 方法。
响应式: 核心
响应式: 核心 介绍
要更好地了解响应式 API,推荐阅读下面几个指南中的章节:
ref()
接受一个内部值,返回一个响应式的、可更改的 ref 对象,此对象只有一个指向其内部值的属性 .value。
类型
function ref<T>(value: T): Ref<UnwrapRef<T>> interface Ref<T> { value: T }详细信息
ref 对象是可更改的,也就是说你可以为
.value赋予新的值。它也是响应式的,即所有对.value的操作都将被追踪,并且写操作会触发与之相关的副作用。如果将一个对象赋值给 ref,那么这个对象将通过 reactive() 转为具有深层次响应式的对象。这也意味着如果对象中包含了嵌套的 ref,它们将被深层地解包。
若要避免这种深层次的转换,请使用
shallowRef()来替代。示例
const count = ref(0) console.log(count.value) // 0 count.value++ console.log(count.value) // 1参考:
computed()
接受一个 getter 函数,返回一个只读的响应式 ref 对象。该 ref 通过 .value 暴露 getter 函数的返回值。它也可以接受一个带有 get 和 set 函数的对象来创建一个可写的 ref 对象。
类型
// 只读 function computed<T>( getter: () => T, // 查看下方的 "计算属性调试" 链接 debuggerOptions?: DebuggerOptions ): Readonly<Ref<Readonly<T>>> // 可写的 function computed<T>( options: { get: () => T set: (value: T) => void }, debuggerOptions?: DebuggerOptions ): Ref<T>示例
创建一个只读的计算属性 ref:
const count = ref(1) const plusOne = computed(() => count.value + 1) console.log(plusOne.value) // 2 plusOne.value++ // 错误创建一个可写的计算属性 ref:
const count = ref(1) const plusOne = computed({ get: () => count.value + 1, set: (val) => { count.value = val - 1 } }) plusOne.value = 1 console.log(count.value) // 0调试:
const plusOne = computed(() => count.value + 1, { onTrack(e) { debugger }, onTrigger(e) { debugger } })参考:
reactive()
返回一个对象的响应式代理。
类型
function reactive<T extends object>(target: T): UnwrapNestedRefs<T>详细信息
响应式转换是“深层”的:它会影响到所有嵌套的属性。一个响应式对象也将深层地解包任何 ref 属性,同时保持响应性。
值得注意的是,当访问到某个响应式数组或
Map这样的原生集合类型中的 ref 元素时,不会执行 ref 的解包。若要避免深层响应式转换,只想保留对这个对象顶层次访问的响应性,请使用 shallowReactive() 作替代。
返回的对象以及其中嵌套的对象都会通过 ES Proxy 包裹,因此不等于源对象,建议只使用响应式代理,避免使用原始对象。
示例
创建一个响应式对象:
const obj = reactive({ count: 0 }) obj.count++ref 的解包:
const count = ref(1) const obj = reactive({ count }) // ref 会被解包 console.log(obj.count === count.value) // true // 会更新 `obj.count` count.value++ console.log(count.value) // 2 console.log(obj.count) // 2 // 也会更新 `count` ref obj.count++ console.log(obj.count) // 3 console.log(count.value) // 3注意当访问到某个响应式数组或
Map这样的原生集合类型中的 ref 元素时,不会执行 ref 的解包:const books = reactive([ref('Vue 3 Guide')]) // 这里需要 .value console.log(books[0].value) const map = reactive(new Map([['count', ref(0)]])) // 这里需要 .value console.log(map.get('count').value)将一个 ref 赋值给一个
reactive属性时,该 ref 会被自动解包:const count = ref(1) const obj = reactive({}) obj.count = count console.log(obj.count) // 1 console.log(obj.count === count.value) // true参考:
readonly()
接受一个对象 (不论是响应式还是普通的) 或是一个 ref,返回一个原值的只读代理。
类型
function readonly<T extends object>( target: T ): DeepReadonly<UnwrapNestedRefs<T>>详细信息
只读代理是深层的:对任何嵌套属性的访问都将是只读的。它的 ref 解包行为与
reactive()相同,但解包得到的值是只读的。要避免深层级的转换行为,请使用 shallowReadonly() 作替代。
示例
const original = reactive({ count: 0 }) const copy = readonly(original) watchEffect(() => { // 用来做响应性追踪 console.log(copy.count) }) // 更改源属性会触发其依赖的侦听器 original.count++ // 更改该只读副本将会失败,并会得到一个警告 copy.count++ // warning!
watchEffect()
立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。
类型
function ref<T>(value: T): Ref<UnwrapRef<T>> interface Ref<T> { value: T }详细信息
第一个参数就是要运行的副作用函数。这个副作用函数的参数也是一个函数,用来注册清理回调。清理回调会在该副作用下一次执行前被调用,可以用来清理无效的副作用,例如等待中的异步请求 (参见下面的示例)。
第二个参数是一个可选的选项,可以用来调整副作用的刷新时机或调试副作用的依赖。
默认情况下,侦听器将在组件渲染之前执行。设置
flush: 'post'将会使侦听器延迟到组件渲染之后再执行。详见回调的触发时机。在某些特殊情况下 (例如要使缓存失效),可能有必要在响应式依赖发生改变时立即触发侦听器。这可以通过设置flush: 'sync'来实现。然而,该设置应谨慎使用,因为如果有多个属性同时更新,这将导致一些性能和数据一致性的问题。返回值是一个用来停止该副作用的函数。
示例
const count = ref(0) watchEffect(() => console.log(count.value)) // -> 输出 0 count.value++ // -> 输出 1副作用清除:
watchEffect(async (onCleanup) => { const { response, cancel } = doAsyncWork(id.value) // `cancel` 会在 `id` 更改时调用 // 以便取消之前 // 未完成的请求 onCleanup(cancel) data.value = await response })停止侦听器:
const stop = watchEffect(() => {}) // 当不再需要此侦听器时: stop()选项:
watchEffect(() => {}, { flush: 'post', onTrack(e) { debugger }, onTrigger(e) { debugger } })参考:
watchPostEffect()
watchEffect() 使用 flush: 'post' 选项时的别名。
watchSyncEffect()
watchEffect() 使用 flush: 'sync' 选项时的别名。
watch()
侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。
类型
// 侦听单个来源 function watch<T>( source: WatchSource<T>, callback: WatchCallback<T>, options?: WatchOptions ): StopHandle // 侦听多个来源 function watch<T>( sources: WatchSource<T>[], callback: WatchCallback<T[]>, options?: WatchOptions ): StopHandle type WatchCallback<T> = ( value: T, oldValue: T, onCleanup: (cleanupFn: () => void) => void ) => void type WatchSource<T> = | Ref<T> // ref | (() => T) // getter | T extends object ? T : never // 响应式对象 interface WatchOptions extends WatchEffectOptions { immediate?: boolean // 默认:false deep?: boolean // 默认:false flush?: 'pre' | 'post' | 'sync' // 默认:'pre' onTrack?: (event: DebuggerEvent) => void onTrigger?: (event: DebuggerEvent) => void }为了便于阅读,对类型进行了简化。
详细信息
watch()默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数。第一个参数是侦听器的源。这个来源可以是以下几种:
- 一个函数,返回一个值
- 一个 ref
- 一个响应式对象
- ...或是由以上类型的值组成的数组
第二个参数是在发生变化时要调用的回调函数。这个回调函数接受三个参数:新值、旧值,以及一个用于注册副作用清理的回调函数。该回调函数会在副作用下一次重新执行前调用,可以用来清除无效的副作用,例如等待中的异步请求。
当侦听多个来源时,回调函数接受两个数组,分别对应来源数组中的新值和旧值。
第三个可选的参数是一个对象,支持以下这些选项:
- immediate:在侦听器创建时立即触发回调。第一次调用时旧值是
undefined。 - deep:如果源是对象,强制深度遍历,以便在深层级变更时触发回调。参考深层侦听器。
- flush:调整回调函数的刷新时机。参考回调的刷新时机及
watchEffect()。 - onTrack / onTrigger:调试侦听器的依赖。参考调试侦听器。
与
watchEffect()相比,watch()使我们可以:- 懒执行副作用;
- 更加明确是应该由哪个状态触发侦听器重新执行;
- 可以访问所侦听状态的前一个值和当前值。
示例
侦听一个 getter 函数:
const state = reactive({ count: 0 }) watch( () => state.count, (count, prevCount) => { /* ... */ } )侦听一个 ref:
const count = ref(0) watch(count, (count, prevCount) => { /* ... */ })当侦听多个来源时,回调函数接受两个数组,分别对应来源数组中的新值和旧值:
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => { /* ... */ })当使用 getter 函数作为源时,回调只在此函数的返回值变化时才会触发。如果你想让回调在深层级变更时也能触发,你需要使用
{ deep: true }强制侦听器进入深层级模式。在深层级模式时,如果回调函数由于深层级的变更而被触发,那么新值和旧值将是同一个对象。const state = reactive({ count: 0 }) watch( () => state, (newValue, oldValue) => { // newValue === oldValue }, { deep: true } )当直接侦听一个响应式对象时,侦听器会自动启用深层模式:
const state = reactive({ count: 0 }) watch(state, () => { /* 深层级变更状态所触发的回调 */ })watch()和watchEffect()享有相同的刷新时机和调试选项:watch(source, callback, { flush: 'post', onTrack(e) { debugger }, onTrigger(e) { debugger } })停止侦听器:
const stop = watch(source, callback) // 当已不再需要该侦听器时: stop()副作用清理:
watch(id, async (newId, oldId, onCleanup) => { const { response, cancel } = doAsyncWork(newId) // 当 `id` 变化时,`cancel` 将被调用, // 取消之前的未完成的请求 onCleanup(cancel) data.value = await response })参考:
响应式: 工具
响应式: 工具 介绍
isRef()
检查某个值是否为 ref。
类型
function isRef<T>(r: Ref<T> | unknown): r is Ref<T>请注意,返回值是一个类型判定 (type predicate),这意味着
isRef可以被用作类型守卫:let foo: unknown if (isRef(foo)) { // foo 的类型被收窄为了 Ref<unknown> foo.value }
unref()
如果参数是 ref,则返回内部值,否则返回参数本身。这是 val = isRef(val) ? val.value : val 计算的一个语法糖。
类型
function unref<T>(ref: T | Ref<T>): T示例
function useFoo(x: number | Ref<number>) { const unwrapped = unref(x) // unwrapped 现在保证为 number 类型 }
toRef()
可以将值、refs 或 getters 规范化为 refs (3.3+)。
也可以基于响应式对象上的一个属性,创建一个对应的 ref。这样创建的 ref 与其源属性保持同步:改变源属性的值将更新 ref 的值,反之亦然。
类型
// 规范化签名 (3.3+) function toRef<T>( value: T ): T extends () => infer R ? Readonly<Ref<R>> : T extends Ref ? T : Ref<UnwrapRef<T>> // 对象属性签名 function toRef<T extends object, K extends keyof T>( object: T, key: K, defaultValue?: T[K] ): ToRef<T[K]> type ToRef<T> = T extends Ref ? T : Ref<T>示例
规范化签名 (3.3+):
// 按原样返回现有的 ref toRef(existingRef) // 创建一个只读的 ref,当访问 .value 时会调用此 getter 函数 toRef(() => props.foo) // 从非函数的值中创建普通的 ref // 等同于 ref(1) toRef(1)对象属性签名:
const state = reactive({ foo: 1, bar: 2 }) // 双向 ref,会与源属性同步 const fooRef = toRef(state, 'foo') // 更改该 ref 会更新源属性 fooRef.value++ console.log(state.foo) // 2 // 更改源属性也会更新该 ref state.foo++ console.log(fooRef.value) // 3请注意,这不同于:
const fooRef = ref(state.foo)上面这个 ref 不会和
state.foo保持同步,因为这个ref()接收到的是一个纯数值。toRef()这个函数在你想把一个 prop 的 ref 传递给一个组合式函数时会很有用:<script setup> import { toRef } from 'vue' const props = defineProps(/* ... */) // 将 `props.foo` 转换为 ref,然后传入 // 一个组合式函数 useSomeFeature(toRef(props, 'foo')) // getter 语法——推荐在 3.3+ 版本使用 useSomeFeature(toRef(() => props.foo)) </script>当
toRef与组件 props 结合使用时,关于禁止对 props 做出更改的限制依然有效。尝试将新的值传递给 ref 等效于尝试直接更改 props,这是不允许的。在这种场景下,你可能可以考虑使用带有get和set的computed替代。详情请见在组件上使用v-model指南。当使用对象属性签名时,即使源属性当前不存在,
toRef()也会返回一个可用的 ref。这让它在处理可选 props 的时候格外实用,相比之下toRefs就不会为可选 props 创建对应的 refs。
toValue() 3.3+
将值、refs 或 getters 规范化为值。这与 unref() 类似,不同的是此函数也会规范化 getter 函数。如果参数是一个 getter,它将会被调用并且返回它的返回值。
这可以在组合式函数中使用,用来规范化一个可以是值、ref 或 getter 的参数。
类型
function toValue<T>(source: T | Ref<T> | (() => T)): T示例
toValue(1) // --> 1 toValue(ref(1)) // --> 1 toValue(() => 1) // --> 1在组合式函数中规范化参数:
import type { MaybeRefOrGetter } from 'vue' function useFeature(id: MaybeRefOrGetter<number>) { watch(() => toValue(id), id => { // 处理 id 变更 }) } // 这个组合式函数支持以下的任意形式: useFeature(1) useFeature(ref(1)) useFeature(() => 1)
toRefs()
将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的 ref。每个单独的 ref 都是使用 toRef() 创建的。
类型
function toRefs<T extends object>( object: T ): { [K in keyof T]: ToRef<T[K]> } type ToRef = T extends Ref ? T : Ref<T>示例
const state = reactive({ foo: 1, bar: 2 }) const stateAsRefs = toRefs(state) /* stateAsRefs 的类型:{ foo: Ref<number>, bar: Ref<number> } */ // 这个 ref 和源属性已经“链接上了” state.foo++ console.log(stateAsRefs.foo.value) // 2 stateAsRefs.foo.value++ console.log(state.foo) // 3当从组合式函数中返回响应式对象时,
toRefs相当有用。使用它,消费者组件可以解构/展开返回的对象而不会失去响应性:function useFeatureX() { const state = reactive({ foo: 1, bar: 2 }) // ...基于状态的操作逻辑 // 在返回时都转为 ref return toRefs(state) } // 可以解构而不会失去响应性 const { foo, bar } = useFeatureX()toRefs在调用时只会为源对象上可以枚举的属性创建 ref。如果要为可能还不存在的属性创建 ref,请改用toRef。
isProxy()
检查一个对象是否是由 reactive()、readonly()、shallowReactive() 或 shallowReadonly() 创建的代理。
类型
function isProxy(value: unknown): boolean
isReactive()
检查一个对象是否是由 reactive() 或 shallowReactive() 创建的代理。
类型
function isReactive(value: unknown): boolean
isReadonly()
检查传入的值是否为只读对象。只读对象的属性可以更改,但他们不能通过传入的对象直接赋值。
通过 readonly() 和 shallowReadonly() 创建的代理都是只读的,因为他们是没有 set 函数的 computed() ref。
类型
function isReadonly(value: unknown): boolean
响应式: 进阶
响应式: 进阶 介绍
shallowRef()
ref() 的浅层作用形式。
类型
function shallowRef<T>(value: T): ShallowRef<T> interface ShallowRef<T> { value: T }详细信息
和
ref()不同,浅层 ref 的内部值将会原样存储和暴露,并且不会被深层递归地转为响应式。只有对.value的访问是响应式的。shallowRef()常常用于对大型数据结构的性能优化或是与外部的状态管理系统集成。示例
const state = shallowRef({ count: 1 }) // 不会触发更改 state.value.count = 2 // 会触发更改 state.value = { count: 2 }参考:
triggerRef()
强制触发依赖于一个浅层 ref 的副作用,这通常在对浅引用的内部值进行深度变更后使用。
类型
function triggerRef(ref: ShallowRef): void示例
const shallow = shallowRef({ greet: 'Hello, world' }) // 触发该副作用第一次应该会打印 "Hello, world" watchEffect(() => { console.log(shallow.value.greet) }) // 这次变更不应触发副作用,因为这个 ref 是浅层的 shallow.value.greet = 'Hello, universe' // 打印 "Hello, universe" triggerRef(shallow)
customRef()
创建一个自定义的 ref,显式声明对其依赖追踪和更新触发的控制方式。
类型
function customRef<T>(factory: CustomRefFactory<T>): Ref<T> type CustomRefFactory<T> = ( track: () => void, trigger: () => void ) => { get: () => T set: (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 timeout return customRef((track, trigger) => { return { get() { track() return value }, set(newValue) { clearTimeout(timeout) timeout = setTimeout(() => { value = newValue trigger() }, delay) } } }) }在组件中使用:
<script setup> import { useDebouncedRef } from './debouncedRef' const text = useDebouncedRef('hello') </script> <template> <input v-model="text" /> </template>
shallowReactive()
reactive() 的浅层作用形式。
类型
function shallowReactive<T extends object>(target: T): T详细信息
和
reactive()不同,这里没有深层级的转换:一个浅层响应式对象里只有根级别的属性是响应式的。属性的值会被原样存储和暴露,这也意味着值为 ref 的属性不会被自动解包了。
谨慎使用
浅层数据结构应该只用于组件中的根级状态。请避免将其嵌套在深层次的响应式对象中,因为它创建的树具有不一致的响应行为,这可能很难理解和调试。
示例
const state = shallowReactive({ foo: 1, nested: { bar: 2 } }) // 更改状态自身的属性是响应式的 state.foo++ // ...但下层嵌套对象不会被转为响应式 isReactive(state.nested) // false // 不是响应式的 state.nested.bar++
shallowReadonly()
readonly() 的浅层作用形式
类型
function shallowReadonly<T extends object>(target: T): Readonly<T>详细信息
和
readonly()不同,这里没有深层级的转换:只有根层级的属性变为了只读。属性的值都会被原样存储和暴露,这也意味着值为 ref 的属性不会被自动解包了。
谨慎使用
浅层数据结构应该只用于组件中的根级状态。请避免将其嵌套在深层次的响应式对象中,因为它创建的树具有不一致的响应行为,这可能很难理解和调试。
示例
const state = shallowReadonly({ foo: 1, nested: { bar: 2 } }) // 更改状态自身的属性会失败 state.foo++ // ...但可以更改下层嵌套对象 isReadonly(state.nested) // false // 这是可以通过的 state.nested.bar++
toRaw()
根据一个 Vue 创建的代理返回其原始对象。
类型
function toRaw<T>(proxy: T): T详细信息
toRaw()可以返回由reactive()、readonly()、shallowReactive()或者shallowReadonly()创建的代理对应的原始对象。这是一个可以用于临时读取而不引起代理访问/跟踪开销,或是写入而不触发更改的特殊方法。不建议保存对原始对象的持久引用,请谨慎使用。
示例
const foo = {} const reactiveFoo = reactive(foo) console.log(toRaw(reactiveFoo) === foo) // true
markRaw()
将一个对象标记为不可被转为代理。返回该对象本身。
类型
function markRaw<T extends object>(value: T): T示例
const foo = markRaw({}) console.log(isReactive(reactive(foo))) // false // 也适用于嵌套在其他响应性对象 const bar = reactive({ foo }) console.log(isReactive(bar.foo)) // false
谨慎使用
markRaw()和类似shallowReactive()这样的浅层式 API 使你可以有选择地避开默认的深度响应/只读转换,并在状态关系谱中嵌入原始的、非代理的对象。它们可能出于各种各样的原因被使用:
- 有些值不应该是响应式的,例如复杂的第三方类实例或 Vue 组件对象。
- 当呈现带有不可变数据源的大型列表时,跳过代理转换可以提高性能。
这应该是一种进阶需求,因为只在根层访问能到原始值,所以如果把一个嵌套的、没有标记的原始对象设置成一个响应式对象,然后再次访问它,你获取到的是代理的版本。这可能会导致对象身份风险,即执行一个依赖于对象身份的操作,但却同时使用了同一对象的原始版本和代理版本:
const foo = markRaw({ nested: {} }) const bar = reactive({ // 尽管 `foo` 被标记为了原始对象,但 foo.nested 却没有 nested: foo.nested }) console.log(foo.nested === bar.nested) // false识别风险一般是很罕见的。然而,要正确使用这些 API,同时安全地避免这样的风险,需要你对响应性系统的工作方式有充分的了解。
effectScope()
创建一个 effect 作用域,可以捕获其中所创建的响应式副作用 (即计算属性和侦听器),这样捕获到的副作用可以一起处理。对于该 API 的使用细节,请查阅对应的 RFC。
类型
function effectScope(detached?: boolean): EffectScope interface EffectScope { run<T>(fn: () => T): T | undefined // 如果作用域不活跃就为 undefined stop(): void }示例
const scope = effectScope() scope.run(() => { const doubled = computed(() => counter.value * 2) watch(doubled, () => console.log(doubled.value)) watchEffect(() => console.log('Count: ', doubled.value)) }) // 处理掉当前作用域内的所有 effect scope.stop()
getCurrentScope()
如果有的话,返回当前活跃的 effect 作用域。
类型
function getCurrentScope(): EffectScope | undefined
onScopeDispose()
在当前活跃的 effect 作用域上注册一个处理回调函数。当相关的 effect 作用域停止时会调用这个回调函数。
这个方法可以作为可复用的组合式函数中 onUnmounted 的替代品,它并不与组件耦合,因为每一个 Vue 组件的 setup() 函数也是在一个 effect 作用域中调用的。
类型
function onScopeDispose(fn: () => void): void
生命周期钩子
生命周期钩子 介绍
使用方式注意
所有罗列在本页的 API 都应该在组件的
setup()阶段被同步调用。相关细节请看指南 - 生命周期钩子。
onMounted()
注册一个回调函数,在组件挂载完成后执行。
类型
function onMounted(callback: () => void): void详细信息
组件在以下情况下被视为已挂载:
- 其所有同步子组件都已经被挂载 (不包含异步组件或
<Suspense>树内的组件)。 - 其自身的 DOM 树已经创建完成并插入了父容器中。注意仅当根容器在文档中时,才可以保证组件 DOM 树也在文档中。
这个钩子通常用于执行需要访问组件所渲染的 DOM 树相关的副作用,或是在服务端渲染应用中用于确保 DOM 相关代码仅在客户端执行。
这个钩子在服务器端渲染期间不会被调用。
- 其所有同步子组件都已经被挂载 (不包含异步组件或
示例
通过模板引用访问一个元素:
<template> <div ref="el"></div> </template> <script setup> import { ref, onMounted } from 'vue' const el = ref() onMounted(() => { el.value // <div> }) </script>
onUpdated()
注册一个回调函数,在组件因为响应式状态变更而更新其 DOM 树之后调用。
类型
function onUpdated(callback: () => void): void详细信息
父组件的更新钩子将在其子组件的更新钩子之后调用。
这个钩子会在组件的任意 DOM 更新后被调用,这些更新可能是由不同的状态变更导致的。如果你需要在某个特定的状态更改后访问更新后的 DOM,请使用 nextTick() 作为替代。
这个钩子在服务器端渲染期间不会被调用。
WARNING
不要在 updated 钩子中更改组件的状态,这可能会导致无限的更新循环!
示例
访问更新后的 DOM
<template> <button id="count" @click="count++">{{ count }}</button> </template> <script setup> import { ref, onUpdated } from 'vue' const count = ref(0) onUpdated(() => { // 文本内容应该与当前的 `count.value` 一致 console.log(document.getElementById('count').textContent) }) </script>
onUnmounted()
注册一个回调函数,在组件实例被卸载之后调用。
类型
function onUnmounted(callback: () => void): void详细信息
一个组件在以下情况下被视为已卸载:
- 其所有子组件都已经被卸载。
- 所有相关的响应式作用 (渲染作用以及
setup()时创建的计算属性和侦听器) 都已经停止。
可以在这个钩子中手动清理一些副作用,例如计时器、DOM 事件监听器或者与服务器的连接。
这个钩子在服务器端渲染期间不会被调用。
示例
<script setup> import { onMounted, onUnmounted } from 'vue' let intervalId onMounted(() => { // setInterval 定时器 // 使用变量存储 便于清理定时器 intervalId = setInterval(() => { // ... }) }) // 组件已卸载时 可以使用onUnmounted 清除副作用 onUnmounted(() => clearInterval(intervalId)) </script>
onBeforeMount()
注册一个钩子,在组件被挂载之前被调用。
类型
function onBeforeMount(callback: () => void): void详细信息
当这个钩子被调用时,组件已经完成了其响应式状态的设置,但还没有创建 DOM 节点。它即将首次执行 DOM 渲染过程。
这个钩子在服务器端渲染期间不会被调用。
onBeforeUpdate()
注册一个钩子,在组件即将因为响应式状态变更而更新其 DOM 树之前调用。
类型
function onBeforeUpdate(callback: () => void): void详细信息
这个钩子可以用来在 Vue 更新 DOM 之前访问 DOM 状态。在这个钩子中更改状态也是安全的。
这个钩子在服务器端渲染期间不会被调用。
onBeforeUnmount()
注册一个钩子,在组件实例被卸载之前调用。
类型
function onBeforeUnmount(callback: () => void): void详细信息
当这个钩子被调用时,组件实例依然还保有全部的功能。
这个钩子在服务器端渲染期间不会被调用。
onErrorCaptured()
注册一个钩子,在捕获了后代组件传递的错误时调用。
类型
function onErrorCaptured(callback: ErrorCapturedHook): void type ErrorCapturedHook = ( err: unknown, instance: ComponentPublicInstance | null, info: string ) => boolean | void详细信息
错误可以从以下几个来源中捕获:
- 组件渲染
- 事件处理器
- 生命周期钩子
setup()函数- 侦听器
- 自定义指令钩子
- 过渡钩子
这个钩子带有三个实参:错误对象、触发该错误的组件实例,以及一个说明错误来源类型的信息字符串。
你可以在
errorCaptured()中更改组件状态来为用户显示一个错误状态。注意不要让错误状态再次渲染导致本次错误的内容,否则组件会陷入无限循环。这个钩子可以通过返回
false来阻止错误继续向上传递。请看下方的传递细节介绍。错误传递规则
- 默认情况下,所有的错误都会被发送到应用级的
app.config.errorHandler(前提是这个函数已经定义),这样这些错误都能在一个统一的地方报告给分析服务。 - 如果组件的继承链或组件链上存在多个
errorCaptured钩子,对于同一个错误,这些钩子会被按从底至上的顺序一一调用。这个过程被称为“向上传递”,类似于原生 DOM 事件的冒泡机制。 - 如果
errorCaptured钩子本身抛出了一个错误,那么这个错误和原来捕获到的错误都将被发送到app.config.errorHandler。 errorCaptured钩子可以通过返回false来阻止错误继续向上传递。即表示“这个错误已经被处理了,应当被忽略”,它将阻止其他的errorCaptured钩子或app.config.errorHandler因这个错误而被调用。
onRenderTracked()Dev only
注册一个调试钩子,当组件渲染过程中追踪到响应式依赖时调用。
这个钩子仅在开发模式下可用,且在服务器端渲染期间不会被调用。
类型
function onRenderTracked(callback: DebuggerHook): void type DebuggerHook = (e: DebuggerEvent) => void type DebuggerEvent = { effect: ReactiveEffect target: object type: TrackOpTypes /* 'get' | 'has' | 'iterate' */ key: any }
onRenderTriggered()Dev only
注册一个调试钩子,当响应式依赖的变更触发了组件渲染时调用。
这个钩子仅在开发模式下可用,且在服务器端渲染期间不会被调用。
类型
function onRenderTriggered(callback: DebuggerHook): void type DebuggerHook = (e: DebuggerEvent) => void type DebuggerEvent = { effect: ReactiveEffect target: object type: TriggerOpTypes /* 'set' | 'add' | 'delete' | 'clear' */ key: any newValue?: any oldValue?: any oldTarget?: Map<any, any> | Set<any> }
onActivated()
注册一个回调函数,若组件实例是<keepAlive>缓存树的一部分,当组件被插入到 DOM 中时调用。
这个钩子在服务器端渲染期间不会被调用。
类型
function onActivated(callback: () => void): void
onDeactivated()
注册一个回调函数,若组件实例是<keepAlive>缓存树的一部分,当组件从 DOM 中被移除时调用。
这个钩子在服务器端渲染期间不会被调用。
类型
function onDeactivated(callback: () => void): void
onServerPrefetch()SSR only
注册一个异步函数,在组件实例在服务器上被渲染之前调用。
类型
function onServerPrefetch(callback: () => Promise<any>): void详细信息
如果这个钩子返回了一个 Promise,服务端渲染会在渲染该组件前等待该 Promise 完成。
这个钩子仅会在服务端渲染中执行,可以用于执行一些仅存在于服务端的数据抓取过程。
示例
<script setup> import { ref, onServerPrefetch, onMounted } from 'vue' const data = ref(null) onServerPrefetch(async () => { // 组件作为初始请求的一部分被渲染 // 在服务器上预抓取数据,因为它比在客户端上更快。 data.value = await fetchOnServer(/* ... */) }) onMounted(async () => { if (!data.value) { // 如果数据在挂载时为空值,这意味着该组件 // 是在客户端动态渲染的。将转而执行 // 另一个客户端侧的抓取请求 data.value = await fetchOnClient(/* ... */) } }) </script>参考:服务端渲染
依赖注入
依赖注入 介绍
provide()
提供一个值,可以被后代组件注入。
类型
function provide<T>(key: InjectionKey<T> | string, value: T): void详细信息
provide()接受两个参数:第一个参数是要注入的 key,可以是一个字符串或者一个 symbol,第二个参数是要注入的值。当使用 TypeScript 时,key 可以是一个被类型断言为
InjectionKey的 symbol。InjectionKey是一个 Vue 提供的工具类型,继承自Symbol,可以用来同步provide()和inject()之间值的类型。与注册生命周期钩子的 API 类似,
provide()必须在组件的setup()阶段同步调用。示例
<script setup> import { ref, provide } from 'vue' import { fooSymbol } from './injectionSymbols' // 提供静态值 provide('foo', 'bar') // 提供响应式的值 const count = ref(0) provide('count', count) // 提供时将 Symbol 作为 key provide(fooSymbol, count) </script>参考:
inject()
注入一个由祖先组件或整个应用 (通过 app.provide()) 提供的值。
类型
// 没有默认值 function inject<T>(key: InjectionKey<T> | string): T | undefined // 带有默认值 function inject<T>(key: InjectionKey<T> | string, defaultValue: T): T // 使用工厂函数 function inject<T>( key: InjectionKey<T> | string, defaultValue: () => T, treatDefaultAsFactory: true ): T详细信息
第一个参数是注入的 key。Vue 会遍历父组件链,通过匹配 key 来确定所提供的值。如果父组件链上多个组件对同一个 key 提供了值,那么离得更近的组件将会“覆盖”链上更远的组件所提供的值。如果没有能通过 key 匹配到值,
inject()将返回undefined,除非提供了一个默认值。第二个参数是可选的,即在没有匹配到 key 时使用的默认值。它也可以是一个工厂函数,用来返回某些创建起来比较复杂的值。如果默认值本身就是一个函数,那么你必须将
false作为第三个参数传入,表明这个函数就是默认值,而不是一个工厂函数。与注册生命周期钩子的 API 类似,
inject()必须在组件的setup()阶段同步调用。当使用 TypeScript 时,key 可以是一个类型为
InjectionKey的 symbol。InjectionKey是一个 Vue 提供的工具类型,继承自Symbol,可以用来同步provide()和inject()之间值的类型。示例
假设有一个父组件已经提供了一些值,如前面
provide()的例子中所示:<script setup> import { inject } from 'vue' import { fooSymbol } from './injectionSymbols' // 注入值的默认方式 const foo = inject('foo') // 注入响应式的值 const count = inject('count') // 通过 Symbol 类型的 key 注入 const foo2 = inject(fooSymbol) // 注入一个值,若为空则使用提供的默认值 const bar = inject('foo', 'default value') // 注入一个值,若为空则使用提供的工厂函数 const baz = inject('foo', () => new Map()) // 注入时为了表明提供的默认值是个函数,需要传入第三个参数 const fn = inject('function', () => {}, false) </script>参考:
选项式 API 🐳
选项式 API照片
状态选项
状态选项 介绍
data
用于声明组件初始响应式状态的函数。
类型
interface ComponentOptions { data?( this: ComponentPublicInstance, vm: ComponentPublicInstance ): object }详细信息
该函数应当返回一个普通 JavaScript 对象,Vue 会将它转换为响应式对象。实例创建后,可以通过
this.$data访问该响应式对象。组件实例也代理了该数据对象上所有的属性,因此this.a等价于this.$data.a。所有会用到的顶层数据属性都应该提前在这个对象中声明。虽然理论上可以向
this.$data添加新属性,但并不推荐这么做。如果一个属性的值在一开始还获取不到,应当先用undefined或是null值来占位,让 Vue 知道这个属性是存在的。以
_或$开头的属性将不会被组件实例代理,因为它们可能和 Vue 的内置属性、API 方法冲突。你必须以this.$data._property的方式访问它们。不推荐返回一个可能改变自身状态的对象,如浏览器 API 原生对象或是带原型的类实例等。理想情况下,返回的对象应是一个纯粹代表组件状态的普通对象。
示例
export default { data() { return { a: 1 } }, created() { console.log(this.a) // 1 console.log(this.$data) // { a: 1 } } }注意,如果你为
data属性使用了一个箭头函数,则this将不会指向该组件实例,不过你仍然可以通过该函数的第一个参数来访问实例:data: (vm) => ({ a: vm.myProp })参考深入响应式系统
props
用于声明一个组件的 props。
类型
interface ComponentOptions { props?: ArrayPropsOptions | ObjectPropsOptions } type ArrayPropsOptions = string[] type ObjectPropsOptions = { [key: string]: Prop } type Prop<T = any> = PropOptions<T> | PropType<T> | null interface PropOptions<T> { type?: PropType<T> required?: boolean default?: T | ((rawProps: object) => T) validator?: (value: unknown) => boolean } type PropType<T> = { new (): T } | { new (): T }[]为了便于阅读,对类型进行了简化。
详细信息
在 Vue 中,所有的组件 props 都需要被显式声明。组件 props 可以通过两种方式声明:
- 使用字符串数组的简易形式。
- 使用对象的完整形式。该对象的每个属性键是对应 prop 的名称,值则是该 prop 应具有的类型的构造函数,或是更高级的选项。
在基于对象的语法中,每个 prop 可以进一步定义如下选项:
type:可以是下列原生构造函数之一:String、Number、Boolean、Array、Object、Date、Function、Symbol、任何自定义构造函数,或由上述内容组成的数组。在开发模式中,Vue 会检查一个 prop 的值是否匹配其声明的类型,如果不匹配则会抛出警告。详见 Prop 校验。还要注意,一个
Boolean类型的 prop 会影响它在开发或生产模式下的值转换行为。详见 Boolean 类型转换。default:为该 prop 指定一个当其没有被传入或值为
undefined时的默认值。对象或数组的默认值必须从一个工厂函数返回。工厂函数也接收原始 prop 对象作为参数。required:定义该 prop 是否必需传入。在非生产环境中,如果 required 值为真值且 prop 未被传入,一个控制台警告将会被抛出。
validator:将 prop 值作为唯一参数传入的自定义验证函数。在开发模式下,如果该函数返回一个假值 (即验证失败),一个控制台警告将会被抛出。
示例
简易声明:
export default { props: ['size', 'myMessage'] }对象声明,带有验证:
export default { props: { // 类型检查 height: Number, // 类型检查 + 其他验证 age: { type: Number, default: 0, required: true, validator: (value) => { return value >= 0 } } } }参考
computed
用于声明要在组件实例上暴露的计算属性。
类型
interface ComponentOptions { computed?: { [key: string]: ComputedGetter<any> | WritableComputedOptions<any> } } type ComputedGetter<T> = ( this: ComponentPublicInstance, vm: ComponentPublicInstance ) => T type ComputedSetter<T> = ( this: ComponentPublicInstance, value: T ) => void type WritableComputedOptions<T> = { get: ComputedGetter<T> set: ComputedSetter<T> }详细信息
该选项接收一个对象,其中键是计算属性的名称,值是一个计算属性 getter,或一个具有 get 和 set 方法的对象 (用于声明可写的计算属性)。
所有的
getters和setters会将它们的this上下文自动绑定为组件实例。注意,如果你为一个计算属性使用了箭头函数,则
this不会指向该组件实例,不过你仍然可以通过该函数的第一个参数来访问实例:export default { computed: { aDouble: (vm) => vm.a * 2 } }示例
export default { data() { return { a: 1 } }, computed: { // 只读 aDouble() { return this.a * 2 }, // 可写 aPlus: { get() { return this.a + 1 }, set(v) { this.a = v - 1 } } }, created() { console.log(this.aDouble) // => 2 console.log(this.aPlus) // => 2 this.aPlus = 3 console.log(this.a) // => 2 console.log(this.aDouble) // => 4 } }参考
methods
用于声明要混入到组件实例中的方法。
类型
interface ComponentOptions { methods?: { [key: string]: (this: ComponentPublicInstance, ...args: any[]) => any } }详细信息
声明的方法可以直接通过组件实例访问,或者在模板语法表达式中使用。所有的方法都会将它们的 this 上下文自动绑定为组件实例,即使在传递时也如此。
在声明方法时避免使用箭头函数,因为它们不能通过
this访问组件实例。示例
export default { data() { return { a: 1 } }, methods: { plus() { this.a++ } }, created() { this.plus() console.log(this.a) // => 2 } }参考事件处理
watch
用于声明在数据更改时调用的侦听回调。
类型
interface ComponentOptions { watch?: { [key: string]: WatchOptionItem | WatchOptionItem[] } } type WatchOptionItem = string | WatchCallback | ObjectWatchOptionItem type WatchCallback<T> = ( value: T, oldValue: T, onCleanup: (cleanupFn: () => void) => void ) => void type ObjectWatchOptionItem = { handler: WatchCallback | string immediate?: boolean // default: false deep?: boolean // default: false flush?: 'pre' | 'post' | 'sync' // default: 'pre' onTrack?: (event: DebuggerEvent) => void onTrigger?: (event: DebuggerEvent) => void }为了便于阅读,对类型进行了简化。
详细信息
watch选项期望接受一个对象,其中键是需要侦听的响应式组件实例属性 (例如,通过data或computed声明的属性)——值是相应的回调函数。该回调函数接受被侦听源的新值和旧值。除了一个根级属性,键名也可以是一个简单的由点分隔的路径,例如
a.b.c。注意,这种用法不支持复杂表达式——仅支持由点分隔的路径。如果你需要侦听复杂的数据源,可以使用命令式的$watch() API。值也可以是一个方法名称的字符串 (通过
methods声明),或包含额外选项的对象。当使用对象语法时,回调函数应被声明在handler中。额外的选项包含:immediate:在侦听器创建时立即触发回调。第一次调用时,旧值将为undefined。deep:如果源是对象或数组,则强制深度遍历源,以便在深度变更时触发回调。详见深层侦听器。flush:调整回调的刷新时机。详见回调的触发时机及watchEffect()。onTrack / onTrigger:调试侦听器的依赖关系。详见侦听器调试。
声明侦听器回调时避免使用箭头函数,因为它们将无法通过 this 访问组件实例。
示例
export default { data() { return { a: 1, b: 2, c: { d: 4 }, e: 5, f: 6 } }, watch: { // 侦听根级属性 a(val, oldVal) { console.log(`new: ${val}, old: ${oldVal}`) }, // 字符串方法名称 b: 'someMethod', // 该回调将会在被侦听的对象的属性改变时调动,无论其被嵌套多深 c: { handler(val, oldVal) { console.log('c changed') }, deep: true }, // 侦听单个嵌套属性: 'c.d': function (val, oldVal) { // do something }, // 该回调将会在侦听开始之后立即调用 e: { handler(val, oldVal) { console.log('e changed') }, immediate: true }, // 你可以传入回调数组,它们将会被逐一调用 f: [ 'handle1', function handle2(val, oldVal) { console.log('handle2 triggered') }, { handler: function handle3(val, oldVal) { console.log('handle3 triggered') } /* ... */ } ] }, methods: { someMethod() { console.log('b changed') }, handle1() { console.log('handle 1 triggered') } }, created() { this.a = 3 // => new: 3, old: 1 } }参考侦听器
emits
用于声明由组件触发的自定义事件。
类型
interface ComponentOptions { emits?: ArrayEmitsOptions | ObjectEmitsOptions } type ArrayEmitsOptions = string[] type ObjectEmitsOptions = { [key: string]: EmitValidator | null } type EmitValidator = (...args: unknown[]) => boolean详细信息
可以以两种形式声明触发的事件:
- 使用字符串数组的简易形式。
- 使用对象的完整形式。该对象的每个属性键是事件的名称,值是
null或一个验证函数。
验证函数会接收到传递给组件的
$emit调用的额外参数。例如,如果this.$emit('foo', 1)被调用,foo相应的验证函数将接受参数1。验证函数应返回布尔值,以表明事件参数是否通过了验证。注意,
emits选项会影响一个监听器被解析为组件事件监听器,还是原生 DOM 事件监听器。被声明为组件事件的监听器不会被透传到组件的根元素上,且将从组件的$attrs对象中移除。详见透传 Attributes。示例
数组语法:
export default { emits: ['check'], created() { this.$emit('check') } }对象语法:
export default { emits: { // 没有验证函数 click: null, // 具有验证函数 submit: (payload) => { if (payload.email && payload.password) { return true } else { console.warn(`Invalid submit event payload!`) return false } } } }参考
expose
用于声明当组件实例被父组件通过模板引用访问时暴露的公共属性。
类型
interface ComponentOptions { expose?: string[] }详细信息
默认情况下,当通过
$parent、$root或模板引用访问时,组件实例将向父组件暴露所有的实例属性。这可能不是我们希望看到的,因为组件很可能拥有一些应保持私有的内部状态或方法,以避免紧耦合。expose选项值应当是一个包含要暴露的属性名称字符串的数组。当使用 expose 时,只有显式列出的属性将在组件实例上暴露。expose仅影响用户定义的属性——它不会过滤掉内置的组件实例属性。示例
export default { // 只有 `publicMethod` 在公共实例上可用 expose: ['publicMethod'], methods: { publicMethod() { // ... }, privateMethod() { // ... } } }
渲染选项
渲染选项 介绍
template
用于声明组件的字符串模板。
类型
interface ComponentOptions { template?: string }详细信息
通过
template选项提供的模板将会在运行时即时编译。这仅在使用了包含模板编译器的 Vue 构建版本的情况下支持。文件名中带有runtime的 Vue 构建版本未包含模板编译器,例如vue.runtime.esm-bundler.js。请查阅构建文件指南了解不同构建版本之间的详细区别。如果该字符串以
#开头,它将被用作querySelector的选择器,并使用所选中元素的innerHTML作为模板字符串。这让我们能够使用原生<template>元素来书写源模板。如果
render选项也同时存在于该组件中,template将被忽略。如果应用的根组件不含任何
template或render选项,Vue 将会尝试使用所挂载元素的innerHTML来作为模板。安全性注意
务必只使用可以信任的模板来源。不要直接将用户提供的内容用作模板。查看安全指南了解更多细节
render
用于编程式地创建组件虚拟 DOM 树的函数。
类型
interface ComponentOptions { render?(this: ComponentPublicInstance) => VNodeChild } type VNodeChild = VNodeChildAtom | VNodeArrayChildren type VNodeChildAtom = | VNode | string | number | boolean | null | undefined | void type VNodeArrayChildren = (VNodeArrayChildren | VNodeChildAtom)[]详细信息
render是字符串模板的一种替代,可以使你利用 JavaScript 的丰富表达力来完全编程式地声明组件最终的渲染输出。预编译的模板,例如单文件组件中的模板,会在构建时被编译为
render选项。如果一个组件中同时存在render和template,则render将具有更高的优先级。参考
compilerOptions
用于配置组件模板的运行时编译器选项。
类型
interface ComponentOptions { compilerOptions?: { isCustomElement?: (tag: string) => boolean whitespace?: 'condense' | 'preserve' // 默认:'condense' delimiters?: [string, string] // 默认:['{{', '}}'] comments?: boolean // 默认:false } }详细信息
这个配置选项仅在使用完整构建版本 (即可以在浏览器中编译模板的
vue.js文件) 时才有效。它支持与应用级的 app.config.compilerOptions 相同的选项,并针对当前组件有更高的优先级。
slotsTS
一个在渲染函数中以编程方式使用插槽时辅助类型推断的选项。仅在 Vue 3.3+ 中支持。
详情
该选项的运行时值不会被使用。实际类型应通过
SlotsType类型辅助工具进行类型转换来声明:import { SlotsType } from 'vue' defineComponent({ slots: Object as SlotsType<{ default: { foo: string; bar: number } item: { data: number } }>, setup(props, { slots }) { expectType< undefined | ((scope: { foo: string; bar: number }) => any) >(slots.default) expectType<undefined | ((scope: { data: number }) => any)>( slots.item ) } })
生命周期选项
生命周期选项 介绍
对于生命周期钩子的通用使用方法,请看指南 - 生命周期钩子
beforeCreate
在组件实例初始化完成之后立即调用。
类型
interface ComponentOptions { beforeCreate?(this: ComponentPublicInstance): void }详细信息
会在实例初始化完成、props 解析之后、
data()和computed等选项处理之前立即调用。注意,组合式 API 中的
setup()钩子会在所有选项式 API 钩子之前调用,beforeCreate()也不例外。
created
在组件实例处理完所有与状态相关的选项后调用。
类型
interface ComponentOptions { created?(this: ComponentPublicInstance): void }详细信息
当这个钩子被调用时,以下内容已经设置完成:响应式数据、计算属性、方法和侦听器。然而,此时挂载阶段还未开始,因此
$el属性仍不可用。
beforeMount
在组件被挂载之前调用。
类型
interface ComponentOptions { beforeMount?(this: ComponentPublicInstance): void }详细信息
当这个钩子被调用时,组件已经完成了其响应式状态的设置,但还没有创建 DOM 节点。它即将首次执行 DOM 渲染过程。
这个钩子在服务端渲染时不会被调用。
mounted
在组件被挂载之后调用。
类型
interface ComponentOptions { mounted?(this: ComponentPublicInstance): void }详细信息
组件在以下情况下被视为已挂载:
所有同步子组件都已经被挂载。(不包含异步组件或
<Suspense>树内的组件)其自身的 DOM 树已经创建完成并插入了父容器中。注意仅当根容器在文档中时,才可以保证组件 DOM 树也在文档中。
这个钩子通常用于执行需要访问组件所渲染的 DOM 树相关的副作用,或是在服务端渲染应用中用于确保 DOM 相关代码仅在客户端被调用。
这个钩子在服务端渲染时不会被调用。
beforeUpdate
在组件即将因为一个响应式状态变更而更新其 DOM 树之前调用。
类型
interface ComponentOptions { beforeUpdate?(this: ComponentPublicInstance): void }详细信息
这个钩子可以用来在 Vue 更新 DOM 之前访问 DOM 状态。在这个钩子中更改状态也是安全的。
这个钩子在服务端渲染时不会被调用。
updated
在组件因为一个响应式状态变更而更新其 DOM 树之后调用。
类型
interface ComponentOptions { updated?(this: ComponentPublicInstance): void }详细信息
父组件的更新钩子将在其子组件的更新钩子之后调用。
这个钩子会在组件的任意 DOM 更新后被调用,这些更新可能是由不同的状态变更导致的。如果你需要在某个特定的状态更改后访问更新后的 DOM,请使用 nextTick() 作为替代。
这个钩子在服务端渲染时不会被调用。
WARNING
不要在 updated 钩子中更改组件的状态,这可能会导致无限的更新循环!
beforeUnmount
在一个组件实例被卸载之前调用。
类型
interface ComponentOptions { beforeUnmount?(this: ComponentPublicInstance): void }详细信息
当这个钩子被调用时,组件实例依然还保有全部的功能。
这个钩子在服务端渲染时不会被调用。
unmounted
在一个组件实例被卸载之后调用。
类型
interface ComponentOptions { unmounted?(this: ComponentPublicInstance): void }详细信息
一个组件在以下情况下被视为已卸载:
其所有子组件都已经被卸载。
所有相关的响应式作用 (渲染作用以及
setup()时创建的计算属性和侦听器) 都已经停止。
可以在这个钩子中手动清理一些副作用,例如计时器、DOM 事件监听器或者与服务器的连接。
这个钩子在服务端渲染时不会被调用。
errorCaptured
在捕获了后代组件传递的错误时调用。
类型
interface ComponentOptions { errorCaptured?( this: ComponentPublicInstance, err: unknown, instance: ComponentPublicInstance | null, info: string ): boolean | void }详细信息
错误可以从以下几个来源中捕获:
- 组件渲染
- 事件处理器
- 生命周期钩子
setup()函数- 侦听器
- 自定义指令钩子
- 过渡钩子
这个钩子带有三个实参:错误对象、触发该错误的组件实例,以及一个说明错误来源类型的信息字符串。
你可以在
errorCaptured()中更改组件状态来为用户显示一个错误状态。然而重要的是,不要让错误状态渲染为导致本次错误的内容,否则组件就会进入无限的渲染循环中。这个钩子可以通过返回
false来阻止错误继续向上传递。请看下方的传递细节介绍。错误传递规则
默认情况下,所有的错误都会被发送到应用级的
app.config.errorHandler(前提是这个函数已经定义),这样这些错误都能在一个统一的地方报告给分析服务。如果组件的继承链或组件链上存在多个
errorCaptured钩子,对于同一个错误,这些钩子会被按从底至上的顺序一一调用。这个过程被称为“向上传递”,类似于原生 DOM 事件的冒泡机制。如果
errorCaptured钩子本身抛出了一个错误,那么这个错误和原来捕获到的错误都将被发送到app.config.errorHandler。errorCaptured钩子可以通过返回false来阻止错误继续向上传递。即表示“这个错误已经被处理了,应当被忽略”,它将阻止其他的errorCaptured钩子或app.config.errorHandler因这个错误而被调用。
renderTrackedDev only
在一个响应式依赖被组件的渲染作用追踪后调用。
这个钩子仅在开发模式下可用,且在服务器端渲染期间不会被调用。
类型
interface ComponentOptions { renderTracked?(this: ComponentPublicInstance, e: DebuggerEvent): void } type DebuggerEvent = { effect: ReactiveEffect target: object type: TrackOpTypes /* 'get' | 'has' | 'iterate' */ key: any }参考深入响应式系统
renderTriggeredDev only
在一个响应式依赖被组件触发了重新渲染之后调用。
这个钩子仅在开发模式下可用,且在服务器端渲染期间不会被调用。
类型
interface ComponentOptions { renderTriggered?(this: ComponentPublicInstance, e: DebuggerEvent): void } type DebuggerEvent = { effect: ReactiveEffect target: object type: TriggerOpTypes /* 'set' | 'add' | 'delete' | 'clear' */ key: any newValue?: any oldValue?: any oldTarget?: Map<any, any> | Set<any> }参考深入响应式系统
activated
若组件实例是 <KeepAlive> 缓存树的一部分,当组件被插入到 DOM 中时调用。
这个钩子在服务端渲染时不会被调用。
类型
interface ComponentOptions { activated?(this: ComponentPublicInstance): void }
deactivated
若组件实例是 <KeepAlive> 缓存树的一部分,当组件从 DOM 中被移除时调用。
这个钩子在服务端渲染时不会被调用。
类型
interface ComponentOptions { deactivated?(this: ComponentPublicInstance): void }
serverPrefetchSSR only
当组件实例在服务器上被渲染之前要完成的异步函数。
类型
interface ComponentOptions { serverPrefetch?(this: ComponentPublicInstance): Promise<any> }详细信息
如果这个钩子返回了一个 Promise,服务端渲染会在渲染该组件前等待该 Promise 完成。
这个钩子仅会在服务端渲染中执行,可以用于执行一些仅在服务端才有的数据抓取过程。
示例
export default { data() { return { data: null } }, async serverPrefetch() { // 组件会作为初次请求的一部分被渲染 // 会在服务端预抓取数据,因为这比客户端更快 this.data = await fetchOnServer(/* ... */) }, async mounted() { if (!this.data) { // 如果数据在挂载时是 null,这意味着这个组件 // 是在客户端动态渲染的,请另外执行一个 // 客户端请求作为替代 this.data = await fetchOnClient(/* ... */) } } }参考服务端渲染
组合选项
组合选项 介绍
provide
用于提供可以被后代组件注入的值。
类型
interface ComponentOptions { provide?: object | ((this: ComponentPublicInstance) => object) }详细信息
provide和inject通常成对一起使用,使一个祖先组件作为其后代组件的依赖注入方,无论这个组件的层级有多深都可以注入成功,只要他们处于同一条组件链上。这个
provide选项应当是一个对象或是返回一个对象的函数。这个对象包含了可注入其后代组件的属性。你可以在这个对象中使用 Symbol 类型的值作为 key。示例
基本使用方式:
const s = Symbol() export default { provide: { foo: 'foo', [s]: 'bar' } }使用函数可以提供其组件中的状态:
export default { data() { return { msg: 'foo' } } provide() { return { msg: this.msg } } }请注意,针对上面这个例子,所供给的
msg将不会是响应式的。请查看和响应式数据配合使用一节获取更多细节。参考依赖注入
inject
用于声明要通过从上层提供方匹配并注入进当前组件的属性。
类型
interface ComponentOptions { inject?: ArrayInjectOptions | ObjectInjectOptions } type ArrayInjectOptions = string[] type ObjectInjectOptions = { [key: string | symbol]: | string | symbol | { from?: string | symbol; default?: any } }详细信息
该
inject选项应该是以下两种之一:- 一个字符串数组
- 一个对象,其 key 名就是在当前组件中的本地绑定名称,而它的值应该是以下两种之一:
- 匹配可用注入的 key (string 或者 Symbol)
- 一个对象
- 它的
from属性是一个 key (string 或者 Symbol),用于匹配可用的注入 - 它的
default属性用作候补值。和 props 的默认值类似,如果它是一个对象,那么应该使用一个工厂函数来创建,以避免多个组件共享同一个对象。
- 它的
如果没有供给相匹配的属性、也没有提供默认值,那么注入的属性将为
undefined。请注意,注入绑定并非响应式的。这是有意为之的一个设计。如果要注入的值是一个响应式对象,那么这个对象上的属性将会保留响应性。请看配合响应性一节获取更多细节。
示例
基本使用方式:
export default { inject: ['foo'], created() { console.log(this.foo) } }使用注入的值作为 props 的默认值:
const Child = { inject: ['foo'], props: { bar: { default() { return this.foo } } } }使用注入的值作为 data:
const Child = { inject: ['foo'], data() { return { bar: this.foo } } }注入项可以选择是否带有默认值:
const Child = { inject: { foo: { default: 'foo' } } }如果需要从不同名字的属性中注入,请使用 from 指明来源属性。
const Child = { inject: { foo: { from: 'bar', default: 'foo' } } }和 props 默认值类似,对于非原始数据类型的值,你需要使用工厂函数:
const Child = { inject: { foo: { from: 'bar', default: () => [1, 2, 3] } } }参考依赖注入
mixins
一个包含组件选项对象的数组,这些选项都将被混入到当前组件的实例中。
类型
interface ComponentOptions { mixins?: ComponentOptions[] }详细信息
mixins选项接受一个 mixin 对象数组。这些 mixin 对象可以像普通的实例对象一样包含实例选项,它们将使用一定的选项合并逻辑与最终的选项进行合并。举例来说,如果你的 mixin 包含了一个 created 钩子,而组件自身也有一个,那么这两个函数都会被调用。Mixin 钩子的调用顺序与提供它们的选项顺序相同,且会在组件自身的钩子前被调用。
不再推荐
在 Vue 2 中,mixins 是创建可重用组件逻辑的主要方式。尽管在 Vue 3 中保留了 mixins 支持,但对于组件间的逻辑复用,使用组合式 API 的组合式函数是现在更推荐的方式。
示例
const mixin = { created() { console.log(1) } } createApp({ created() { console.log(2) }, mixins: [mixin] }) // => 1 // => 2
extends
要继承的“基类”组件。
类型
interface ComponentOptions { extends?: ComponentOptions }详细信息
使一个组件可以继承另一个组件的组件选项。
从实现角度来看,
extends几乎和mixins相同。通过extends指定的组件将会当作第一个 mixin 来处理。然而,
extends和mixins表达的是不同的目标。mixins选项基本用于组合功能,而extends则一般更关注继承关系。同
mixins一样,所有选项 (setup()除外) 都将使用相关的策略进行合并。示例
const CompA = { ... } const CompB = { extends: CompA, ... }不建议用于组合式 API
extends 是为选项式 API 设计的,不会处理
setup()钩子的合并。在组合式 API 中,逻辑复用的首选模式是“组合”而不是“继承”。如果一个组件中的逻辑需要复用,考虑将相关逻辑提取到组合式函数中。
如果你仍然想要通过组合式 API 来“继承”一个组件,可以在继承组件的
setup()中调用基类组件的setup():import Base from './Base.js' export default { extends: Base, setup(props, ctx) { return { ...Base.setup(props, ctx), // 本地绑定 } } }
其他杂项
其他杂项 介绍
name
用于显式声明组件展示时的名称。
类型
interface ComponentOptions { name?: string }详细信息
组件的名字有以下用途:
- 在组件自己的模板中递归引用自己时
- 在 Vue 开发者工具中的组件树显示时
- 在组件抛出的警告追踪栈信息中显示时
当你在使用单文件组件时,组件已经会根据其文件名推导出其名称。举例来说,一个名为
MyComponent.vue的文件会推导出显示名称为“MyComponent”。另一种场景是当一个组件通过
app.component被全局注册时,这个全局 ID 就自动被设为了其名称。使用
name选项使你可以覆盖推导出的名称,或是在没有推导出名字时显式提供一个。(例如没有使用构建工具时,或是一个内联的非 SFC 式的组件)有一种场景下
name必须是已显式声明的:即<KeepAlive>通过其include / excludeprop 来匹配其需要缓存的组件时。TIP
在 3.2.34 或以上的版本中,使用
<script setup>的单文件组件会自动根据文件名生成对应的name选项,即使是在配合<KeepAlive>使用时也无需再手动声明。
inheritAttrs
用于控制是否启用默认的组件 attribute 透传行为。
类型
interface ComponentOptions { inheritAttrs?: boolean // 默认值:true }详细信息
默认情况下,父组件传递的,但没有被子组件解析为 props 的
attributes绑定会被“透传”。这意味着当我们有一个单根节点的子组件时,这些绑定会被作为一个常规的 HTML attribute 应用在子组件的根节点元素上。当你编写的组件想要在一个目标元素或其他组件外面包一层时,可能并不期望这样的行为。我们可以通过设置inheritAttrs为false来禁用这个默认行为。这些attributes可以通过$attrs这个实例属性来访问,并且可以通过v-bind来显式绑定在一个非根节点的元素上。示例
在一个使用了
<script setup>的组件中声明这个选项时,可以使用defineOptions宏:<template> <label> {{ label }} <input v-bind="$attrs" v-bind:value="value" v-on:input="$emit('input', $event.target.value)" /> </label> </template> <script setup> defineProps(['label', 'value']) defineEmits(['input']) defineOptions({ inheritAttrs: false }) </script>从 3.3 开始你也可以直接在
<script setup>中使用defineOptions:<template> <label> {{ label }} <input v-bind="$attrs" v-bind:value="value" v-on:input="$emit('input', $event.target.value)" /> </label> </template> <script setup> defineProps(['label', 'value']) defineEmits(['input']) defineOptions({ inheritAttrs: false }) </script>
components
一个对象,用于注册对当前组件实例可用的组件。
类型
interface ComponentOptions { components?: { [key: string]: Component } }示例
import Foo from './Foo.vue' import Bar from './Bar.vue' export default { components: { // 简写 Foo, // 注册为一个不同的名称 RenamedBar: Bar } }参考组件注册
directives
一个对象,用于注册对当前组件实例可用的指令。
类型
interface ComponentOptions { directives?: { [key: string]: Directive } }示例
export default { directives: { // 在模板中启用 v-focus focus: { mounted(el) { el.focus() } } } }<input v-focus>这个列表中的指令都在当前组件实例中可用。
参考自定义指令
组件实例
组件实例 介绍
INFO
本页文档描述了组件公共实例 (即
this) 上暴露的内置属性和方法,本页罗列的所有属性,除了
$data下的嵌套属性之外,都是只读的。
$data
从 data 选项函数中返回的对象,会被组件赋为响应式。组件实例将会代理对其数据对象的属性访问。
类型
interface ComponentPublicInstance { $data: object }
$props
表示组件当前已解析的 props 对象。
类型
interface ComponentPublicInstance { $props: object }详细信息
这里只包含通过
props选项声明的 props。组件实例将会代理对其 props 对象上属性的访问。
$el
该组件实例管理的 DOM 根节点。
类型
interface ComponentPublicInstance { $el: Node | undefined }详细信息
$el直到组件挂载完成 (mounted)之前都会是undefined。对于单一根元素的组件,
$el将会指向该根元素。 对于以文本节点为根的组件,$el将会指向该文本节点。 对于以多个元素为根的组件,$el将是一个仅作占位符的 DOM 节点,Vue 使用它来跟踪组件在 DOM 中的位置 (文本节点或 SSR 激活模式下的注释节点)。TIP
为保持一致性,我们推荐使用模板引用来直接访问元素而不是依赖
$el。
$options
已解析的用于实例化当前组件的组件选项。
类型
interface ComponentPublicInstance { $options: ComponentOptions }详细信息
这个
$options对象暴露了当前组件的已解析选项,并且会是以下几种可能来源的合并结果:- 全局 mixin
- 组件
extends的基组件 - 组件级 mixin
它通常用于支持自定义组件选项:
const app = createApp({ customOption: 'foo', created() { console.log(this.$options.customOption) // => 'foo' } })
$parent
当前组件可能存在的父组件实例,如果当前组件是顶层组件,则为 null。
类型
interface ComponentPublicInstance { $parent: ComponentPublicInstance | null }
$root
当前组件树的根组件实例。如果当前实例没有父组件,那么这个值就是它自己。
类型
interface ComponentPublicInstance { $root: ComponentPublicInstance }
$slots
一个表示父组件所传入插槽的对象。
类型
interface ComponentPublicInstance { $slots: { [name: string]: Slot } } type Slot = (...args: any[]) => VNode[]详细信息
通常用于手写渲染函数,但也可用于检测是否存在插槽。
每一个插槽都在
this.$slots上暴露为一个函数,返回一个vnode数组,同时key名对应着插槽名。默认插槽暴露为this.$slots.default。如果插槽是一个作用域插槽,传递给该插槽函数的参数可以作为插槽的 prop 提供给插槽。
$refs
一个包含 DOM 元素和组件实例的对象,通过模板引用注册。
类型
interface ComponentPublicInstance { $refs: { [name: string]: Element | ComponentPublicInstance | null } }参考
$attrs
一个包含了组件所有透传 attributes 的对象。
类型
interface ComponentPublicInstance { $attrs: object }详细信息
透传 Attributes 是指由父组件传入,且没有被子组件声明为 props 或是组件自定义事件的 attributes 和事件处理函数。
默认情况下,若是单一根节点组件,
$attrs中的所有属性都是直接自动继承自组件的根元素。而多根节点组件则不会如此,同时你也可以通过配置inheritAttrs选项来显式地关闭该行为。参考
$watch()
用于命令式地创建侦听器的 API。
类型
interface ComponentPublicInstance { $watch( source: string | (() => any), callback: WatchCallback, options?: WatchOptions ): StopHandle } type WatchCallback<T> = ( value: T, oldValue: T, onCleanup: (cleanupFn: () => void) => void ) => void interface WatchOptions { immediate?: boolean // default: false deep?: boolean // default: false flush?: 'pre' | 'post' | 'sync' // default: 'pre' onTrack?: (event: DebuggerEvent) => void onTrigger?: (event: DebuggerEvent) => void } type StopHandle = () => void详细信息
第一个参数是侦听来源。可以是一个组件的属性名的字符串,一个简单的由点分隔的路径字符串,或是一个 getter 函数。
第二个参数是回调函数。它接收的参数分别是侦听来源的新值、旧值。
- immediate:指定在侦听器创建时是否立即触发回调。在第一次调用时旧值为 undefined。
- deep:指定在侦听来源是一个对象时,是否强制深度遍历,这样回调函数就会在深层级发生变更时被触发。详见深层侦听器。
- flush:指定回调函数的刷新时机。详见回调刷新时机及
watchEffect()。 - onTrack / onTrigger:调试侦听器的依赖,详见侦听器调试。
示例
侦听一个属性名:
this.$watch('a', (newVal, oldVal) => {})侦听一个由
.分隔的路径:this.$watch('a.b', (newVal, oldVal) => {})对更复杂表达式使用 getter 函数:
this.$watch( // 每一次这个 `this.a + this.b` 表达式生成一个 // 不同的结果,处理函数都会被调用 // 这就好像我们在侦听一个计算属性 // 而不定义计算属性本身。 () => this.a + this.b, (newVal, oldVal) => {} )停止该侦听器:
const unwatch = this.$watch('a', cb) // 之后…… unwatch()参考
$emit()
在当前组件触发一个自定义事件。任何额外的参数都会传递给事件监听器的回调函数。
类型
interface ComponentPublicInstance { $emit(event: string, ...args: any[]): void }示例
export default { created() { // 仅触发事件 this.$emit('foo') // 带有额外的参数 this.$emit('bar', 1, 2, 3) } }参考
$forceUpdate()
强制该组件重新渲染。
类型
interface ComponentPublicInstance { $forceUpdate(): void }详细信息
鉴于 Vue 的全自动响应性系统,这个功能应该很少会被用到。唯一可能需要它的情况是,你使用高阶响应式 API 显式创建了一个非响应式的组件状态。
$nextTick()
绑定在实例上的 nextTick() 函数。
类型
interface ComponentPublicInstance { $nextTick(callback?: (this: ComponentPublicInstance) => void): Promise<void> }详细信息
和全局版本的
nextTick()的唯一区别就是组件传递给 this.$nextTick()的回调函数会带上this上下文,其绑定了当前组件实例。参考
nextTick()
内置内容 🐳
内置内容照片
指令
指令 介绍
v-text
更新元素的文本内容。
期望的绑定值类型:
string详细信息
v-text通过设置元素的 textContent 属性来工作,因此它将覆盖元素中所有现有的内容。如果你需要更新textContent的部分,应该使用 mustache interpolations 代替。示例
<span v-text="msg"></span> <!-- 等同于 --> <span>{{msg}}</span>参考:模板语法 - 文本插值
v-html
更新元素的 innerHTML。
- 期望的绑定值类型:
string - 详细信息
v-html 的内容直接作为普通 HTML 插入—— Vue 模板语法是不会被解析的。如果你发现自己正打算用 v-html 来编写模板,不如重新想想怎么使用组件来代替。
安全说明:
在你的站点上动态渲染任意的 HTML 是非常危险的,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要将用户提供的内容作为插值
在单文件组件,scoped 样式将不会作用于 v-html 里的内容,因为 HTML 内容不会被 Vue 的模板编译器解析。如果你想让 v-html 的内容也支持 scoped CSS,你可以使用 CSS modules 或使用一个额外的全局 <style> 元素,手动设置类似 BEM 的作用域策略。
示例:
<div v-html="html"></div>
v-show
基于表达式值的真假性,来改变元素的可见性。
期望的绑定值类型:
any详细信息
v-show通过设置内联样式的displayCSS 属性来工作,当元素可见时将使用初始display值。当条件改变时,也会触发过渡效果。**使用的场景: **多次切换组件, 需要使用
v-show,性能最佳
v-if
基于表达式值的真假性,来条件性地渲染元素或者模板片段。
期望的绑定值类型:
any详细信息
当
v-if元素被触发,元素及其所包含的指令/组件都会销毁和重构。如果初始条件是假,那么其内部的内容根本都不会被渲染。可用于
<template>表示仅包含文本或多个元素的条件块。当条件改变时会触发过渡效果。
当同时使用时,
v-if比v-for优先级更高。我们并不推荐在一元素上同时使用这两个指令 — 查看列表渲染指南详情。参考:条件渲染 - v-if
v-else
表示 v-if 或 v-if / v-else-if 链式调用的“else 块”。
无需传入表达式
详细信息
- 限定:上一个兄弟元素必须有
v-if或v-else-if。 - 可用于
<template>表示仅包含文本或多个元素的条件块。
- 限定:上一个兄弟元素必须有
示例
<!-- 随机数 Math.random() --> <div v-if="MatMath.random() h.random() > 0.5"> Now you see me </div> <div v-else> Now you don't </div>
v-else-if
表示 v-if 的“else if 块”。可以进行链式调用。
期望的绑定值类型:
any详细信息
- 限定:上一个兄弟元素必须有
v-if或v-else-if。 - 可用于
<template>表示仅包含文本或多个元素的条件块。
- 限定:上一个兄弟元素必须有
示例
<div v-if="type === 'A'"> A </div> <div v-else-if="type === 'B'"> B </div> <div v-else-if="type === 'C'"> C </div> <div v-else> Not A/B/C </div>
v-for
基于原始数据多次渲染元素或模板块。
期望的绑定值类型:
Array | Object | number | string | Iterable详细信息
指令值必须使用特殊语法
alias in expression为正在迭代的元素提供一个别名:<div v-for="item in items"> {{ item.text }} </div>或者,你也可以为索引指定别名 (如果用在对象,则是键值):
<div v-for="(item, index) in items"></div> <div v-for="(value, key) in object"></div> <div v-for="(value, name, index) in object"></div>v-for的默认方式是尝试就地更新元素而不移动它们。要强制其重新排序元素,你需要用特殊 attributekey来提供一个排序提示:<div v-for="item in items" :key="item.id"> {{ item.text }} </div>v-for也可以用于 Iterable Protocol 的实现,包括原生Map和Set。参考:
v-on
给元素绑定事件监听器。
缩写:
@期望的绑定值类型:
Function | Inline Statement | Object (不带参数)参数:
event(使用对象语法则为可选项)修饰符:
.stop- 调用event.stopPropagation()。.prevent- 调用event.preventDefault()。.capture- 在捕获模式添加事件监听器。.self- 只有事件从元素本身发出才触发处理函数。.{keyAlias}- 只在某些按键下触发处理函数。.once- 最多触发一次处理函数。.left- 只在鼠标左键事件触发处理函数。.right- 只在鼠标右键事件触发处理函数。.middle- 只在鼠标中键事件触发处理函数。.passive- 通过{ passive: true }附加一个 DOM 事件。
详细信息
事件类型由参数来指定。表达式可以是一个方法名,一个内联声明,如果有修饰符则可省略。
当用于普通元素,只监听原生 DOM 事件。当用于自定义元素组件,则监听子组件触发的自定义事件。
当监听原生 DOM 事件时,方法接收原生事件作为唯一参数。如果使用内联声明,声明可以访问一个特殊的
$event变量:v-on:click="handle('ok', $event)"。v-on还支持绑定不带参数的事件/监听器对的对象。请注意,当使用对象语法时,不支持任何修饰符。示例:
<!-- 方法处理函数 --> <button v-on:click="doThis"></button> <!-- 动态事件 --> <button v-on:[event]="doThis"></button> <!-- 内联声明 --> <button v-on:click="doThat('hello', $event)"></button> <!-- 缩写 --> <button @click="doThis"></button> <!-- 使用缩写的动态事件 --> <button @[event]="doThis"></button> <!-- 停止传播 --> <button @click.stop="doThis"></button> <!-- 阻止默认事件 --> <button @click.prevent="doThis"></button> <!-- 不带表达式地阻止默认事件 --> <form @submit.prevent></form> <!-- 链式调用修饰符 --> <button @click.stop.prevent="doThis"></button> <!-- 按键用于 keyAlias 修饰符--> <input @keyup.enter="onEnter" /> <!-- 点击事件将最多触发一次 --> <button v-on:click.once="doThis"></button> <!-- 对象语法 --> <button v-on="{ mousedown: doThis, mouseup: doThat }"></button>监听子组件的自定义事件 (当子组件的“my-event”事件被触发,处理函数将被调用):
<MyComponent @my-event="handleThis" /> <!-- 内联声明 --> <MyComponent @my-event="handleThis(123, $event)" />参考:
v-bind
动态的绑定一个或多个 attribute,也可以是组件的 prop。
缩写:
:或者.(当使用.prop修饰符)期望:
any (带参数) | Object (不带参数)参数:
attrOrProp (可选的)修饰符:
.camel- 将短横线命名的 attribute 转变为驼峰式命名。.prop- 强制绑定为 DOM property。3.2+.attr- 强制绑定为 DOM attribute。3.2+
用途:
当用于绑定
class或styleattribute,v-bind支持额外的值类型如数组或对象。详见下方的指南链接。在处理绑定时,Vue 默认会利用
in操作符来检查该元素上是否定义了和绑定的 key 同名的 DOM property。如果存在同名的 property,则 Vue 会把作为 DOM property 赋值,而不是作为 attribute 设置。这个行为在大多数情况都符合期望的绑定值类型,但是你也可以显式用.prop和.attr修饰符来强制绑定方式。有时这是必要的,特别是在和自定义元素打交道时。当用于组件 props 绑定时,所绑定的 props 必须在子组件中已被正确声明。
当不带参数使用时,可以用于绑定一个包含了多个 attribute 名称-绑定值对的对象。
示例:
<!-- 绑定 attribute --> <img v-bind:src="imageSrc" /> <!-- 动态 attribute 名 --> <button v-bind:[key]="value"></button> <!-- 缩写 --> <img :src="imageSrc" /> <!-- 缩写形式的动态 attribute 名 --> <button :[key]="value"></button> <!-- 内联字符串拼接 --> <img :src="'/path/to/images/' + fileName" /> <!-- class 绑定 --> <div :class="{ red: isRed }"></div> <div :class="[classA, classB]"></div> <div :class="[classA, { classB: isB, classC: isC }]"></div> <!-- style 绑定 --> <div :style="{ fontSize: size + 'px' }"></div> <div :style="[styleObjectA, styleObjectB]"></div> <!-- 绑定对象形式的 attribute --> <div v-bind="{ id: someProp, 'other-attr': otherProp }"></div> <!-- prop 绑定。“prop” 必须在子组件中已声明。 --> <MyComponent :prop="someThing" /> <!-- 传递子父组件共有的 prop --> <MyComponent v-bind="$props" /> <!-- XLink --> <svg><a :xlink:special="foo"></a></svg>.prop修饰符也有专门的缩写,.:<div :someProperty.prop="someObject"></div> <!-- 等同于 --> <div .someProperty="someObject"></div>当在 DOM 内模板使用
.camel修饰符,可以驼峰化v-bindattribute 的名称,例如 SVGviewBoxattribute:<svg :view-box.camel="viewBox"></svg>如果使用字符串模板或使用构建步骤预编译模板,则不需要
.camel。参考:
v-model
在表单输入元素或组件上创建双向绑定。
- 期望的绑定值类型:根据表单输入元素或组件输出的值而变化
- 仅限:
<input><select><textarea>- components
- 修饰符:
- 参考:
v-slot
用于声明具名插槽或是期望接收 props 的作用域插槽。
缩写:
#期望的绑定值类型:能够合法在函数参数位置使用的 JavaScript 表达式。支持解构语法。绑定值是可选的——只有在给作用域插槽传递 props 才需要。
参数:插槽名 (可选,默认是
default)仅限:
<template>- components (用于带有 prop 的单个默认插槽)
示例:
<!-- 具名插槽 --> <BaseLayout> <template v-slot:header> Header content </template> <template v-slot:default> Default slot content </template> <template v-slot:footer> Footer content </template> </BaseLayout> <!-- 接收 prop 的具名插槽 --> <InfiniteScroll> <template v-slot:item="slotProps"> <div class="item"> {{ slotProps.item.text }} </div> </template> </InfiniteScroll> <!-- 接收 prop 的默认插槽,并解构 --> <Mouse v-slot="{ x, y }"> Mouse position: {{ x }}, {{ y }} </Mouse>参考:
v-pre
跳过该元素及其所有子元素的编译。
无需传入
详细信息
元素内具有
v-pre,所有 Vue 模板语法都会被保留并按原样渲染。最常见的用例就是显示原始双大括号标签及内容。示例:
<span v-pre>{{ this will not be compiled }}</span>
v-once
仅渲染元素和组件一次,并跳过之后的更新。
无需传入
详细信息
在随后的重新渲染,元素/组件及其所有子项将被当作静态内容并跳过渲染。这可以用来优化更新时的性能。
<!-- 单个元素 --> <span v-once>This will never change: {{msg}}</span> <!-- 带有子元素的元素 --> <div v-once> <h1>comment</h1> <p>{{msg}}</p> </div> <!-- 组件 --> <MyComponent v-once :comment="msg" /> <!-- `v-for` 指令 --> <ul> <li v-for="i in list" v-once>{{i}}</li> </ul>从 3.2 起,你也可以搭配
v-memo的无效条件来缓存部分模板。参考:
v-memo3.2+
期望的绑定值类型:
any[]详细信息
缓存一个模板的子树。在元素和组件上都可以使用。为了实现缓存,该指令需要传入一个固定长度的依赖值数组进行比较。如果数组里的每个值都与最后一次的渲染相同,那么整个子树的更新将被跳过。举例来说:
<div v-memo="[valueA, valueB]"> ... </div>当组件重新渲染,如果
valueA和valueB都保持不变,这个<div>及其子项的所有更新都将被跳过。实际上,甚至虚拟 DOM 的 vnode 创建也将被跳过,因为缓存的子树副本可以被重新使用。正确指定缓存数组很重要,否则应该生效的更新可能被跳过。
v-memo传入空依赖数组 (v-memo="[]") 将与v-once效果相同。与 v-for 一起使用
v-memo仅用于性能至上场景中的微小优化,应该很少需要。最常见的情况可能是有助于渲染海量v-for列表 (长度超过 1000 的情况):<div v-for="item in list" :key="item.id" v-memo="[item.id === selected]"> <p>ID: {{ item.id }} - selected: {{ item.id === selected }}</p> <p>...more child nodes</p> </div>当组件的
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 规则,它可以在组件编译完毕前隐藏原始模板。示例:
[v-cloak] { display: none; }<div v-cloak> {{ message }} </div>直到编译完成前,
<div>将不可见。
组件
组件 介绍
组件注册和使用
内置组件无需注册便可以直接在模板中使用。它们也支持 tree-shake:仅在使用时才会包含在构建中。
在渲染函数中使用它们时,需要显式导入。例如:
import { h, Transition } from 'vue' h(Transition, { /* props */ })
<Transition>
为单个元素或组件提供动画过渡效果。
Props
interface TransitionProps { /** * 用于自动生成过渡 CSS class 名。 * 例如 `name: 'fade'` 将自动扩展为 `.fade-enter`、 * `.fade-enter-active` 等。 */ name?: string /** * 是否应用 CSS 过渡 class。 * 默认:true */ css?: boolean /** * 指定要等待的过渡事件类型 * 来确定过渡结束的时间。 * 默认情况下会自动检测 * 持续时间较长的类型。 */ type?: 'transition' | 'animation' /** * 显式指定过渡的持续时间。 * 默认情况下是等待过渡效果的根元素的第一个 `transitionend` * 或`animationend`事件。 */ duration?: number | { enter: number; leave: number } /** * 控制离开/进入过渡的时序。 * 默认情况下是同时的。 */ mode?: 'in-out' | 'out-in' | 'default' /** * 是否对初始渲染使用过渡。 * 默认:false */ appear?: boolean /** * 用于自定义过渡 class 的 prop。 * 在模板中使用短横线命名,例如:enter-from-class="xxx" */ enterFromClass?: string enterActiveClass?: string enterToClass?: string appearFromClass?: string appearActiveClass?: string appearToClass?: string leaveFromClass?: string leaveActiveClass?: string leaveToClass?: string }事件
@before-enter@before-leave@enter@leave@appear@after-enter@after-leave@after-appear@enter-cancelled@leave-cancelled(v-showonly)@appear-cancelled
示例
简单元素:
<Transition> <div v-if="ok">toggled content</div> </Transition>通过改变
key属性来强制过度执行:<Transition> <div :key="text">{{ text }}</div> </Transition>动态组件,初始渲染时带有过渡模式 + 动画出现:
<Transition name="fade" mode="out-in" appear> <component :is="view"></component> </Transition>监听过渡事件:
<Transition @after-enter="onTransitionComplete"> <div v-show="ok">toggled content</div> </Transition>参考:
<Transition>指南
<TransitionGroup>
为列表中的多个元素或组件提供过渡效果。
Props
<TransitionGroup>拥有与<Transition>除了mode以外所有的 props,并增加了两个额外的 props:interface TransitionGroupProps extends Omit<TransitionProps, 'mode'> { /** * 如果未定义,则渲染为片段 (fragment)。 */ tag?: string /** * 用于自定义过渡期间被应用的 CSS class。 * 在模板中使用 kebab-case,例如 move-class="xxx" */ moveClass?: string }事件
<TransitionGroup>抛出与<Transition>相同的事件。详细信息
默认情况下,
<TransitionGroup>不会渲染一个容器 DOM 元素,但是可以通过tagprop 启用。注意,每个
<transition-group>的子节点必须有独立的 key,动画才能正常工作。<TransitionGroup>支持通过 CSS transform 控制移动效果。当一个子节点在屏幕上的位置在更新之后发生变化时,它会被添加一个使其位移的 CSS class (基于nameattribute 推导,或使用move-classprop 显式配置)。如果使其位移的 class 被添加时 CSS 的transform属性是“可过渡的”,那么该元素会基于 FLIP 技巧平滑地到达动画终点。示例
<TransitionGroup tag="ul" name="slide"> <li v-for="item in items" :key="item.id"> {{ item.text }} </li> </TransitionGroup>
<KeepAlive>
缓存包裹在其中的动态切换组件。
Props
interface KeepAliveProps { /** * 如果指定,则只有与 `include` 名称 * 匹配的组件才会被缓存。 */ include?: MatchPattern /** * 任何名称与 `exclude` * 匹配的组件都不会被缓存。 */ exclude?: MatchPattern /** * 最多可以缓存多少组件实例。 */ max?: number | string } type MatchPattern = string | RegExp | (string | RegExp)[]详细信息
<KeepAlive>包裹动态组件时,会缓存不活跃的组件实例,而不是销毁它们。任何时候都只能有一个活跃组件实例作为
<KeepAlive>的直接子节点。当一个组件在
<KeepAlive>中被切换时,它的activated和deactivated生命周期钩子将被调用,用来替代mounted和unmounted。这适用于<KeepAlive>的直接子节点及其所有子孙节点。示例
基本用法:
<KeepAlive> <component :is="view"></component> </KeepAlive>与
v-if/v-else分支一起使用时,同一时间只能有一个组件被渲染:<KeepAlive> <comp-a v-if="a > 1"></comp-a> <comp-b v-else></comp-b> </KeepAlive>与
<Transition>一起使用:<Transition> <KeepAlive> <component :is="view"></component> </KeepAlive> </Transition>使用
include/exclude:<!-- 用逗号分隔的字符串 --> <KeepAlive include="a,b"> <component :is="view"></component> </KeepAlive> <!-- 正则表达式 (使用 `v-bind`) --> <KeepAlive :include="/a|b/"> <component :is="view"></component> </KeepAlive> <!-- 数组 (使用 `v-bind`) --> <KeepAlive :include="['a', 'b']"> <component :is="view"></component> </KeepAlive>使用
max:<KeepAlive :max="10"> <component :is="view"></component> </KeepAlive>
<Teleport>
将其插槽内容渲染到 DOM 中的另一个位置。
Props
interface TeleportProps { /** * 必填项。指定目标容器。 * 可以是选择器或实际元素。 */ to: string | HTMLElement /** * 当值为 `true` 时,内容将保留在其原始位置 * 而不是移动到目标容器中。 * 可以动态更改。 */ disabled?: boolean }示例
指定目标容器:
<teleport to="#some-id" /> <teleport to=".some-class" /> <teleport to="[data-teleport]" />有条件地禁用:
<teleport to="#popup" :disabled="displayVideoInline"> <video src="./my-movie.mp4"> </teleport>
<Suspense>Experimental
用于协调对组件树中嵌套的异步依赖的处理。
Props
interface SuspenseProps { timeout?: string | number }事件
@resolve@pending@fallback
详细信息
<Suspense>接受两个插槽:#default和#fallback。它将在内存中渲染默认插槽的同时展示后备插槽内容。如果在渲染时遇到异步依赖项 (异步组件和具有
async setup()的组件),它将等到所有异步依赖项解析完成时再显示默认插槽。
特殊元素
特殊元素 介绍
不是组件
<component>、<slot>和<template>具有类似组件的特性,也是模板语法的一部分。但它们并非真正的组件,同时在模板编译期间会被编译掉。因此,它们通常在模板中用小写字母书写。
<component>
一个用于渲染动态组件或元素的“元组件”。
Props
interface DynamicComponentProps { is: string | Component }详细信息
要渲染的实际组件由
isprop 决定。- 当
is是字符串,它既可以是 HTML 标签名也可以是组件的注册名。 - 或者,
is也可以直接绑定到组件的定义。
- 当
示例
按注册名渲染组件 (选项式 API):
<script> import Foo from './Foo.vue' import Bar from './Bar.vue' export default { components: { Foo, Bar }, data() { return { view: 'Foo' } } } </script> <template> <component :is="view" /> </template>按定义渲染组件 (
<script setup>组合式 API):<script setup> import Foo from './Foo.vue' import Bar from './Bar.vue' </script> <template> <component :is="Math.random() > 0.5 ? Foo : Bar" /> </template>渲染 HTML 元素:
template
内置组件都可以传递给
is,但是如果想通过名称传递则必须先对其进行注册。举例来说:<component :is="href ? 'a' : 'span'"></component>如果将组件本身传递给
is而不是其名称,则不需要注册,例如在<script setup>中。如果在
<component>标签上使用v-model,模板编译器会将其扩展为modelValueprop 和update:modelValue事件监听器,就像对任何其他组件一样。但是,这与原生 HTML 元素不兼容,例如<input>或<select>。因此,在动态创建的原生元素上使用v-model将不起作用:<script> import { Transition, TransitionGroup } from 'vue' export default { components: { Transition, TransitionGroup } } </script> <template> <component :is="isGroup ? 'TransitionGroup' : 'Transition'"> ... </component> </template>在实践中,这种极端情况并不常见,因为原生表单字段通常包裹在实际应用的组件中。如果确实需要直接使用原生元素,那么你可以手动将
v-model拆分为 attribute 和事件。参考:动态组件
<slot>
表示模板中的插槽内容出口。
Props
interface SlotProps { /** * 任何传递给 <slot> 的 prop 都可以作为作用域插槽 * 的参数传递 */ [key: string]: any /** * 保留,用于指定插槽名。 */ name?: string }详细信息
<slot>元素可以使用nameattribute 来指定插槽名。当没有指定name时,将会渲染默认插槽。传递给插槽元素的附加 attributes 将作为插槽 props,传递给父级中定义的作用域插槽。元素本身将被其所匹配的插槽内容替换。
Vue 模板里的
<slot>元素会被编译到 JavaScript,因此不要与原生 `` 元素进行混淆。参考:组件 - 插槽
<template>
当我们想要使用内置指令而不在 DOM 中渲染元素时,<template> 标签可以作为占位符使用。
详细信息:
对
<template>的特殊处理只有在它与以下任一指令一起使用时才会被触发:v-if、v-else-if或v-elsev-forv-slot
如果这些指令都不存在,那么它将被渲染成一个[原生的 `。
带有
v-for的<template>也可以有一个key属性。所有其他的属性和指令都将被丢弃,因为没有相应的元素,它们就没有意义。单文件组件使用顶层的
<template>标签来包裹整个模板。这种用法与上面描述的<template>使用方式是有区别的。该顶层标签不是模板本身的一部分,不支持指令等模板语法。参考:
- 指南 -
<template>上的v-if - 指南 -
<template>上的v-for - 指南 - 具名插槽
- 指南 -
特殊 Attributes
特殊 Attributes 介绍
key
key 这个特殊的 attribute 主要作为 Vue 的虚拟 DOM 算法提示,在比较新旧节点列表时用于识别 vnode。
预期:
number | string | symbol详细信息
在没有 key 的情况下,Vue 将使用一种最小化元素移动的算法,并尽可能地就地更新/复用相同类型的元素。如果传了 key,则将根据 key 的变化顺序来重新排列元素,并且将始终移除/销毁 key 已经不存在的元素。
同一个父元素下的子元素必须具有唯一的 key。重复的 key 将会导致渲染异常。
最常见的用例是与
v-for结合:<ul> <li v-for="item in items" :key="item.id">...</li> </ul>也可以用于强制替换一个元素/组件而不是复用它。当你想这么做时它可能会很有用:
- 在适当的时候触发组件的生命周期钩子
- 触发过渡
举例来说:
<transition> <span :key="text">{{ text }}</span> </transition>当
text变化时,<span>总是会被替换而不是更新,因此 transition 将会被触发。
ref
用于注册模板引用。
预期:
string | Function详细信息
ref用于注册元素或子组件的引用。使用选项式 API,引用将被注册在组件的
this.$refs对象里:<!-- 存储为 this.$refs.p 组合式的API --> <p ref="p">hello</p>使用组合式 API,引用将存储在与名字匹配的 ref 里:
<template> <p ref="p">hello</p> </template> <script setup> // 组合式的API import { ref } from 'vue' const p = ref() // </script>如果用于普通 DOM 元素,引用将是元素本身;如果用于子组件,引用将是子组件的实例。
或者
ref可以接收一个函数值,用于对存储引用位置的完全控制:<ChildComponent :ref="(el) => child = el" />关于 ref 注册时机的重要说明:因为 ref 本身是作为渲染函数的结果来创建的,必须等待组件挂载后才能对它进行访问。
this.$refs也是非响应式的,因此你不应该尝试在模板中使用它来进行数据绑定。参考:
is
用于绑定动态组件。
预期:
string | Component用于原生元素3.1+
当
isattribute 用于原生 HTML 元素时,它将被当作 Customized built-in element,其为原生 web 平台的特性。但是,在这种用例中,你可能需要 Vue 用其组件来替换原生元素,如 DOM 模板解析注意事项所述。你可以在
isattribute 的值中加上vue:前缀,这样 Vue 就会把该元素渲染为 Vue 组件:<table> <tr is="vue:my-row-component"></tr> </table>参考:
单文件组件 🐳
单文件组件照片
语法定义
语法定义 介绍
SFC语法定义
总览
一个 Vue 单文件组件 (SFC),通常使用 *.vue 作为文件扩展名,它是一种使用了类似 HTML 语法的自定义文件格式,用于定义 Vue 组件。一个 Vue 单文件组件在语法上是兼容 HTML 的。
每一个 *.vue 文件都由三种顶层语言块构成:<template>、<script> 和 <style>,以及一些其他的自定义块:
<template>
<div class="example">{{ msg }}</div>
</template>
<script>
export default {
data() {
return {
msg: 'Hello world!'
}
}
}
</script>
<style>
.example {
color: red;
}
</style>
<custom1>
This could be e.g. documentation for the component.
</custom1>相应语言块
<template>
- 每个
*.vue文件最多可以包含一个顶层<template>块。 - 语块包裹的内容将会被提取、传递给
@vue/compiler-dom,预编译为 JavaScript 渲染函数,并附在导出的组件上作为其render选项。
<script>
- 每个
*.vue文件最多可以包含一个<script>块。(使用 `` 的情况除外) - 这个脚本代码块将作为 ES 模块执行。
- 默认导出应该是 Vue 的组件选项对象,可以是一个对象字面量或是 defineComponent 函数的返回值。
<script setup>
- 每个
*.vue文件最多可以包含一个<script setup>。(不包括一般的<script>) - 这个脚本块将被预处理为组件的
setup()函数,这意味着它将为每一个组件实例都执行。<script setup>中的顶层绑定都将自动暴露给模板。要了解更多细节,请看 `` 的专门文档。
<style>
- 每个
*.vue文件可以包含多个<style>标签。 - 一个
<style>标签可以使用scoped或moduleattribute (查看 SFC 样式功能了解更多细节) 来帮助封装当前组件的样式。使用了不同封装模式的多个<style>标签可以被混合入同一个组件。
自定义块
在一个 *.vue 文件中可以为任何项目特定需求使用额外的自定义块。举例来说,一个用作写文档的 <docs> 块。这里是一些自定义块的真实用例:
自定义块的处理需要依赖工具链。如果你想要在构建中集成你的自定义语块,请参见 SFC 自定义块集成工具链指南获取更多细节。
自动名称推导
SFC 在以下场景中会根据文件名自动推导其组件名:
- 开发警告信息中需要格式化组件名时;
- DevTools 中观察组件时;
- 递归组件自引用时。例如一个名为
FooBar.vue的组件可以在模板中通过<FooBar/>引用自己。(同名情况下) 这比明确注册/导入的组件优先级低。
预处理器
代码块可以使用 lang 这个 attribute 来声明预处理器语言,最常见的用例就是在 <script> 中使用 TypeScript:
<script lang="ts">
// use TypeScript
</script>lang 在任意块上都能使用,比如我们可以在 <style> 标签中使用 Sass 或是 <template> 中使用 Pug:
<template lang="pug">
p {{ msg }}
</template>
<style lang="scss">
$primary-color: #333;
body {
color: $primary-color;
}
</style>注意对不同预处理器的集成会根据你所使用的工具链而有所不同,具体细节请查看相应的工具链文档来确认:
src 导入
如果你更喜欢将 *.vue 组件分散到多个文件中,可以为一个语块使用 src 这个 attribute 来导入一个外部文件:
<template src="./template.html"></template>
<style src="./style.css"></style>
<script src="./script.js"></script>请注意 src 导入和 JS 模块导入遵循相同的路径解析规则,这意味着:
- 相对路径需要以
./开头 - 你也可以从 npm 依赖中导入资源
<!-- 从所安装的 "todomvc-app-css" npm 包中导入一个文件 -->
<style src="todomvc-app-css/index.css" />src 导入对自定义语块也同样适用:
<unit-test src="./unit-test.js">
</unit-test>注释
在每一个语块中你都可以按照相应语言 (HTML、CSS、JavaScript 和 Pug 等等) 的语法书写注释。对于顶层注释,请使用 HTML 的注释语法 <!-- comment contents here -->
<script setup>
setup 语法糖 介绍
<script setup>是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。当同时使用 SFC 与组合式 API 时该语法是默认推荐。相比于普通的 <script> 语法,它具有更多优势:
- 更少的样板内容,更简洁的代码。
- 能够使用纯 TypeScript 声明 props 和自定义事件。
- 更好的运行时性能 (其模板会被编译成同一作用域内的渲染函数,避免了渲染上下文代理对象)。
- 更好的 IDE 类型推导性能 (减少了语言服务器从代码中抽取类型的工作)。
基本语法
要启用该语法,需要在 <script> 代码块上添加 setup attribute:
<script setup>
console.log('hello script setup')
</script>里面的代码会被编译成组件 setup() 函数的内容。这意味着与普通的 <script> 只在组件被首次引入的时候执行一次不同,<script setup> 中的代码会在每次组件实例被创建的时候执行。
顶层的绑定会被暴露给模板
当使用 <script setup> 的时候,任何在 <script setup> 声明的顶层的绑定 (包括变量,函数声明,以及 import 导入的内容) 都能在模板中直接使用:
<script setup>
// 变量
const msg = 'Hello!'
// 函数
function log() {
console.log(msg)
}
</script>
<template>
<button @click="log">{{ msg }}</button>
</template>import 导入的内容也会以同样的方式暴露。这意味着我们可以在模板表达式中直接使用导入的 helper 函数,而不需要通过 methods 选项来暴露它:
<script setup>
import { capitalize } from './helpers'
</script>
<template>
<div>{{ capitalize('hello') }}</div>
</template>响应式
响应式状态需要明确使用响应式 API 来创建。和 setup() 函数的返回值一样,ref 在模板中使用的时候会自动解包:
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count++">{{ count }}</button>
</template>使用组件
<script setup>范围里的值也能被直接作为自定义组件的标签名使用:
<script setup>
import MyComponent from './MyComponent.vue'
</script>
<template>
<MyComponent />
</template>这里 MyComponent 应当被理解为像是在引用一个变量。如果你使用过 JSX,此处的心智模型是类似的。其 kebab-case 格式的 <my-component> 同样能在模板中使用——不过,我们强烈建议使用 PascalCase 格式以保持一致性。同时这也有助于区分原生的自定义元素。
动态组件
由于组件是通过变量引用而不是基于字符串组件名注册的,在 <script setup> 中要使用动态组件的时候,应该使用动态的 :is 来绑定:
<script setup>
import Foo from './Foo.vue'
import Bar from './Bar.vue'
</script>
<template>
<component :is="Foo" />
<component :is="someCondition ? Foo : Bar" />
</template>请注意组件是如何在三元表达式中被当做变量使用的。
递归组件
一个单文件组件可以通过它的文件名被其自己所引用。例如:名为 FooBar.vue 的组件可以在其模板中用 <FooBar/> 引用它自己。
请注意这种方式相比于导入的组件优先级更低。如果有具名的导入和组件自身推导的名字冲突了,可以为导入的组件添加别名:
import { FooBar as FooBarChild } from './components'命名空间组件
可以使用带 . 的组件标签,例如 <Foo.Bar> 来引用嵌套在对象属性中的组件。这在需要从单个文件中导入多个组件的时候非常有用:
<script setup>
import * as Form from './form-components'
</script>
<template>
<Form.Input>
<Form.Label>label</Form.Label>
</Form.Input>
</template>使用自定义指令
全局注册的自定义指令将正常工作。本地的自定义指令在 <script setup> 中不需要显式注册,但他们必须遵循 vNameOfDirective 这样的命名规范:
<script setup>
const vMyDirective = {
beforeMount: (el) => {
// 在元素上做些操作
}
}
</script>
<template>
<h1 v-my-directive>This is a Heading</h1>
</template>如果指令是从别处导入的,可以通过重命名来使其符合命名规范:
<script setup>
import { myDirective as vMyDirective } from './MyDirective.js'
</script>defineProps() 和 defineEmits()
为了在声明 props 和 emits 选项时获得完整的类型推导支持,我们可以使用 defineProps 和 defineEmits API,它们将自动地在 <script setup> 中可用:
<script setup>
const props = defineProps({
foo: String
})
const emit = defineEmits(['change', 'delete'])
// setup 代码
</script>defineProps和defineEmits都是只能在<script setup>中使用的编译器宏。他们不需要导入,且会随着<script setup>的处理过程一同被编译掉。defineProps接收与props选项相同的值,defineEmits接收与emits选项相同的值。defineProps和defineEmits在选项传入后,会提供恰当的类型推导。- 传入到
defineProps和defineEmits的选项会从 setup 中提升到模块的作用域。因此,传入的选项不能引用在 setup 作用域中声明的局部变量。这样做会引起编译错误。但是,它可以引用导入的绑定,因为它们也在模块作用域内。
针对类型的 props/emit 声明TS
props 和 emit 也可以通过给 defineProps 和 defineEmits 传递纯类型参数的方式来声明:
const props = defineProps<{
foo: string
bar?: number
}>()
const emit = defineEmits<{
(e: 'change', id: number): void
(e: 'update', value: string): void
}>()
// 3.3+:另一种更简洁的语法
const emit = defineEmits<{
change: [id: number] // 具名元组语法
update: [value: string]
}>()defineProps或defineEmits要么使用运行时声明,要么使用类型声明。同时使用两种声明方式会导致编译报错。使用类型声明的时候,静态分析会自动生成等效的运行时声明,从而在避免双重声明的前提下确保正确的运行时行为。
- 在开发模式下,编译器会试着从类型来推导对应的运行时验证。例如这里从
foo: string类型中推断出foo: String。如果类型是对导入类型的引用,这里的推导结果会是foo: null(与any类型相等),因为编译器没有外部文件的信息。 - 在生产模式下,编译器会生成数组格式的声明来减少打包体积 (这里的 props 会被编译成
['foo', 'bar'])。
- 在开发模式下,编译器会试着从类型来推导对应的运行时验证。例如这里从
在 3.2 及以下版本中,
defineProps()的泛型类型参数只能使用类型字面量或者本地接口的引用。这个限制已经在 3.3 版本中解决。最新版本的 Vue 支持在类型参数的位置引用导入的和有限的复杂类型。然而,由于类型到运行时的转换仍然基于 AST,因此并不支持使用需要实际类型分析的复杂类型,例如条件类型等。你可以在单个 prop 的类型上使用条件类型,但不能对整个 props 对象使用。
使用类型声明时的默认 props 值
针对类型的 defineProps 声明的不足之处在于,它没有可以给 props 提供默认值的方式。为了解决这个问题,我们还提供了 withDefaults 编译器宏:
export interface Props {
msg?: string
labels?: string[]
}
const props = withDefaults(defineProps<Props>(), {
msg: 'hello',
labels: () => ['one', 'two']
})上面代码会被编译为等价的运行时 props 的 default 选项。此外,withDefaults 辅助函数提供了对默认值的类型检查,并确保返回的 props 的类型删除了已声明默认值的属性的可选标志。
defineExpose()
使用 <script setup> 的组件是默认关闭的——即通过模板引用或者 $parent 链获取到的组件的公开实例,不会暴露任何在 <script setup> 中声明的绑定。
可以通过 defineExpose 编译器宏来显式指定在 <script setup> 组件中要暴露出去的属性:
<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
defineExpose({
a,
b
})
</script>当父组件通过模板引用的方式获取到当前组件的实例,获取到的实例会像这样 { a: number, b: number } (ref 会和在普通实例中一样被自动解包)
defineOptions()
这个宏可以用来直接在 <script setup> 中声明组件选项,而不必使用单独的 <script> 块:
<script setup>
defineOptions({
inheritAttrs: false,
customOptions: {
/* ... */
}
})
</script>- 仅支持 Vue 3.3+。
- 这是一个宏定义,选项将会被提升到模块作用域中,无法访问
<script setup>中不是字面常数的局部变量。
defineSlots()TS
这个宏可以用于为 IDE 提供插槽名称和 props 类型检查的类型提示。
defineSlots() 只接受类型参数,没有运行时参数。类型参数应该是一个类型字面量,其中属性键是插槽名称,值类型是插槽函数。函数的第一个参数是插槽期望接收的 props,其类型将用于模板中的插槽 props。返回类型目前被忽略,可以是 any,但我们将来可能会利用它来检查插槽内容。
它还返回 slots 对象,该对象等同于在 setup 上下文中暴露或由 useSlots() 返回的 slots 对象。
<script setup lang="ts">
const slots = defineSlots<{
default(props: { msg: string }): any
}>()
</script>- 仅支持 Vue 3.3+。
useSlots() 和 useAttrs()
在 <script setup> 使用 slots 和 attrs 的情况应该是相对来说较为罕见的,因为可以在模板中直接通过 $slots 和 $attrs 来访问它们。在你的确需要使用它们的罕见场景中,可以分别用 useSlots 和 useAttrs 两个辅助函数:
<script setup>
import { useSlots, useAttrs } from 'vue'
const slots = useSlots()
const attrs = useAttrs()
</script>useSlots 和 useAttrs 是真实的运行时函数,它的返回与 setupContext.slots 和 setupContext.attrs 等价。它们同样也能在普通的组合式 API 中使用。
与普通的 <script> 一起使用
<script setup>可以和普通的 <script> 一起使用。普通的 <script> 在有这些需要的情况下或许会被使用到:
- 声明无法在
<script setup>中声明的选项,例如inheritAttrs或插件的自定义选项。 - 声明模块的具名导出 (named exports)。
- 运行只需要在模块作用域执行一次的副作用,或是创建单例对象。
<script>
// 普通 <script>, 在模块作用域下执行 (仅一次)
runSideEffectOnce()
// 声明额外的选项
export default {
inheritAttrs: false,
customOptions: {}
}
</script>
<script setup>
// 在 setup() 作用域中执行 (对每个实例皆如此)
</script>在同一组件中将 <script setup> 与 <script> 结合使用的支持仅限于上述情况。具体来说:
- 不要为已经可以用
<script setup>定义的选项使用单独的<script>部分,如props和emits。 - 在
<script setup>中创建的变量不会作为属性添加到组件实例中,这使得它们无法从选项式 API 中访问。我们强烈反对以这种方式混合 API。
如果你发现自己处于以上任一不被支持的场景中,那么你应该考虑切换到一个显式的 setup() 函数,而不是使用 <script setup>。
顶层 await
<script setup> 中可以使用顶层 await。结果代码会被编译成 async setup():
<script setup>
const post = await fetch(`/api/post/1`).then((r) => r.json())
</script>另外,await 的表达式会自动编译成在 await 之后保留当前组件实例上下文的格式。
注意:
async setup()必须与Suspense内置组件组合使用,Suspense目前还是处于实验阶段的特性,会在将来的版本中稳定。
泛型TS
可以使用 <script> 标签上的 generic 属性声明泛型类型参数:
<script setup lang="ts" generic="T">
defineProps<{
items: T[]
selected: T
}>()
</script>generic 的值与 TypeScript 中位于 <...> 之间的参数列表完全相同。例如,您可以使用多个参数,extends 约束,默认类型和引用导入的类型:
<script
setup
lang="ts"
generic="T extends string | number, U extends Item"
>
import type { Item } from './types'
defineProps<{
id: T
list: U[]
}>()
</script>限制
由于模块执行语义的差异,<script setup> 中的代码依赖单文件组件的上下文。当将其移动到外部的 .js 或者 .ts 文件中的时候,对于开发者和工具来说都会感到混乱。因此,<script setup> 不能和 src attribute 一起使用。
CSS 功能
CSS 功能 介绍
组件作用域 CSS
当 <style> 标签带有 scoped attribute 的时候,它的 CSS 只会影响当前组件的元素,和 Shadow DOM 中的样式封装类似。使用时有一些注意事项,不过好处是不需要任何的 polyfill。它的实现方式是通过 PostCSS 将以下内容:
<style scoped>
.example {
color: red;
}
</style>
<template>
<div class="example">hi</div>
</template>转换为:
<style>
.example[data-v-f3f3eg9] {
color: red;
}
</style>
<template>
<div class="example" data-v-f3f3eg9>hi</div>
</template>子组件的根元素
使用 scoped 后,父组件的样式将不会渗透到子组件中。不过,子组件的根节点会同时被父组件的作用域样式和子组件的作用域样式影响。这样设计是为了让父组件可以从布局的角度出发,调整其子组件根元素的样式。
深度选择器
处于 scoped 样式中的选择器如果想要做更“深度”的选择,也即:影响到子组件,可以使用 :deep() 这个伪类:
<style scoped>
.a :deep(.b) {
/* ... */
}
</style>上面的代码会被编译成:
.a[data-v-f3f3eg9] .b {
/* ... */
}TIP
通过
v-html创建的 DOM 内容不会被作用域样式影响,但你仍然可以使用深度选择器来设置其样式。
插槽选择器
默认情况下,作用域样式不会影响到 <slot/> 渲染出来的内容,因为它们被认为是父组件所持有并传递进来的。使用 :slotted 伪类以明确地将插槽内容作为选择器的目标:
<style scoped>
:slotted(div) {
color: red;
}
</style>全局选择器
如果想让其中一个样式规则应用到全局,比起另外创建一个 <style>,可以使用 :global 伪类来实现 (看下面的代码):
<style scoped>
:global(.red) {
color: red;
}
</style>混合使用局部与全局样式
你也可以在同一个组件中同时包含作用域样式和非作用域样式:
<style>
/* 全局样式 */
</style>
<style scoped>
/* 局部样式 */
</style>作用域样式须知
- 作用域样式并没有消除对 class 的需求。由于浏览器渲染各种各样 CSS 选择器的方式,
p { color: red }结合作用域样式使用时 (即当与 attribute 选择器组合的时候) 会慢很多倍。如果你使用 class 或者 id 来替代,例如.example { color: red },那你几乎就可以避免性能的损失。 - 小心递归组件中的后代选择器!对于一个使用了
.a .b选择器的样式规则来说,如果匹配到.a的元素包含了一个递归的子组件,那么所有的在那个子组件中的.b都会匹配到这条样式规则。
CSS Modules
一个 <style module> 标签会被编译为 CSS Modules 并且将生成的 CSS class 作为 $style 对象暴露给组件:
<template>
<p :class="$style.red">This should be red</p>
</template>
<style module>
.red {
color: red;
}
</style>得出的 class 将被哈希化以避免冲突,实现了同样的将 CSS 仅作用于当前组件的效果。
参考 CSS Modules spec 以查看更多详情,例如 global exceptions 和 composition。
自定义注入名称
你可以通过给 module attribute 一个值来自定义注入 class 对象的属性名:
<template>
<p :class="classes.red">red</p>
</template>
<style module="classes">
.red {
color: red;
}
</style>与组合式 API 一同使用
可以通过 useCssModule API 在 setup() 和 <script setup> 中访问注入的 class。对于使用了自定义注入名称的 <style module> 块,useCssModule 接收一个匹配的 module attribute 值作为第一个参数:
import { useCssModule } from 'vue'
// 在 setup() 作用域中...
// 默认情况下, 返回 <style module> 的 class
useCssModule()
// 具名情况下, 返回 <style module="classes"> 的 class
useCssModule('classes')CSS 中的 v-bind()
单文件组件的 <style> 标签支持使用 v-bind CSS 函数将 CSS 的值链接到动态的组件状态:
<template>
<div class="text">hello</div>
</template>
<script>
export default {
data() {
return {
color: 'red'
}
}
}
</script>
<style>
.text {
color: v-bind(color);
}
</style>这个语法同样也适用于 ``,且支持 JavaScript 表达式 (需要用引号包裹起来):
<script setup>
const theme = {
color: 'red'
}
</script>
<template>
<p>hello</p>
</template>
<style scoped>
p {
color: v-bind('theme.color');
}
</style>实际的值会被编译成哈希化的 CSS 自定义属性,因此 CSS 本身仍然是静态的。自定义属性会通过内联样式的方式应用到组件的根元素上,并且在源值变更的时候响应式地更新。
进阶 API 🐳
进阶 API照片
渲染函数
渲染函数 介绍
h()
创建虚拟 DOM 节点 (vnode)。
类型
// 完整参数签名 function h( type: string | Component, props?: object | null, children?: Children | Slot | Slots ): VNode // 省略 props function h(type: string | Component, children?: Children | Slot): VNode type Children = string | number | boolean | VNode | null | Children[] type Slot = () => Children type Slots = { [name: string]: Slot }为了便于阅读,对类型进行了简化。
详细信息
第一个参数既可以是一个字符串 (用于原生元素) 也可以是一个 Vue 组件定义。第二个参数是要传递的 prop,第三个参数是子节点。
当创建一个组件的 vnode 时,子节点必须以插槽函数进行传递。如果组件只有默认槽,可以使用单个插槽函数进行传递。否则,必须以插槽函数的对象形式来传递。
为了方便阅读,当子节点不是插槽对象时,可以省略 prop 参数。
示例
创建原生元素:
import { h } from 'vue' // 除了 type 外,其他参数都是可选的 h('div') h('div', { id: 'foo' }) // attribute 和 property 都可以用于 prop // Vue 会自动选择正确的方式来分配它 h('div', { class: 'bar', innerHTML: 'hello' }) // class 与 style 可以像在模板中一样 // 用数组或对象的形式书写 h('div', { class: [foo, { bar }], style: { color: 'red' } }) // 事件监听器应以 onXxx 的形式书写 h('div', { onClick: () => {} }) // children 可以是一个字符串 h('div', { id: 'foo' }, 'hello') // 没有 prop 时可以省略不写 h('div', 'hello') h('div', [h('span', 'hello')]) // children 数组可以同时包含 vnode 和字符串 h('div', ['hello', h('span', 'hello')])创建组件:
import Foo from './Foo.vue' // 传递 prop h(Foo, { // 等价于 some-prop="hello" someProp: 'hello', // 等价于 @update="() => {}" onUpdate: () => {} }) // 传递单个默认插槽 h(Foo, () => 'default slot') // 传递具名插槽 // 注意,需要使用 `null` 来避免 // 插槽对象被当作是 prop h(MyComponent, null, { default: () => 'default slot', foo: () => h('div', 'foo'), bar: () => [h('span', 'one'), h('span', 'two')] })
mergeProps()
合并多个 props 对象,用于处理含有特定的 props 参数的情况。
类型
function mergeProps(...args: object[]): object详细信息
mergeProps()支持以下特定 props 参数的处理,将它们合并成一个对象。classstyleonXxx事件监听器——多个同名的事件监听器将被合并到一个数组。
如果你不需要合并行为而是简单覆盖,可以使用原生 object spread 语法来代替。
示例
import { mergeProps } from 'vue' const one = { class: 'foo', onClick: handlerA } const two = { class: { bar: true }, onClick: handlerB } const merged = mergeProps(one, two) /** { class: 'foo bar', onClick: [handlerA, handlerB] } */
cloneVNode()
克隆一个 vnode。
类型
function cloneVNode(vnode: VNode, extraProps?: object): VNode详细信息
返回一个克隆的 vnode,可在原有基础上添加一些额外的 prop。
Vnode 被认为是一旦创建就不能修改的,你不应该修改已创建的 vnode 的 prop,而应该附带不同的/额外的 prop 来克隆它。
Vnode 具有特殊的内部属性,因此克隆它并不像 object spread 一样简单。
cloneVNode()处理了大部分这样的内部逻辑。示例
import { h, cloneVNode } from 'vue' const original = h('div') const cloned = cloneVNode(original, { id: 'foo' })
isVNode()
判断一个值是否为 vnode 类型。
类型
function isVNode(value: unknown): boolean
resolveComponent()
按名称手动解析已注册的组件。
类型
function resolveComponent(name: string): Component | string详细信息
备注:如果你可以直接引入组件就不需使用此方法。
为了能从正确的组件上下文进行解析,
resolveComponent()必须在setup()或渲染函数内调用。如果组件未找到,会抛出一个运行时警告,并返回组件名字符串。
示例
const { h, resolveComponent } = Vue export default { setup() { const ButtonCounter = resolveComponent('ButtonCounter') return () => { return h(ButtonCounter) } } }
resolveDirective()
按名称手动解析已注册的指令。
类型
function resolveDirective(name: string): Directive | undefined详细信息
备注:如果你可以直接引入组件就不需使用此方法。
为了能从正确的组件上下文进行解析,
resolveDirective()必须在setup()或渲染函数内调用。如果指令没有找到,会抛出一个运行时警告,并返回
undefined。
withDirectives()
用于给 vnode 增加自定义指令。
类型
function withDirectives( vnode: VNode, directives: DirectiveArguments ): VNode // [Directive, value, argument, modifiers] type DirectiveArguments = Array< | [Directive] | [Directive, any] | [Directive, any, string] | [Directive, any, string, DirectiveModifiers] >详细信息
用自定义指令包装一个现有的 vnode。第二个参数是自定义指令数组。每个自定义指令也可以表示为
[Directive, value, argument, modifiers]形式的数组。如果不需要,可以省略数组的尾元素。示例
import { h, withDirectives } from 'vue' // 一个自定义指令 const pin = { mounted() { /* ... */ }, updated() { /* ... */ } } // <div v-pin:top.animate="200"></div> const vnode = withDirectives(h('div'), [ [pin, 200, 'top', { animate: true }] ])
withModifiers()
用于向事件处理函数添加内置 v-on 修饰符。
类型
function withModifiers(fn: Function, modifiers: string[]): Function示例
import { h, withModifiers } from 'vue' const vnode = h('button', { // 等价于 v-on:click.stop.prevent onClick: withModifiers(() => { // ... }, ['stop', 'prevent']) })
服务端渲染
服务端渲染 介绍
renderToString()
导出自
vue/server-renderer类型
function renderToString( input: App | VNode, context?: SSRContext ): Promise<string>示例
import { createSSRApp } from 'vue' import { renderToString } from 'vue/server-renderer' const app = createSSRApp({ data: () => ({ msg: 'hello' }), template: `<div>{{ msg }}</div>` }) ;(async () => { const html = await renderToString(app) console.log(html) })()SSR 上下文
你可以传入一个可选的上下文对象用来在渲染过程中记录额外的数据,例如访问 Teleport 的内容:
const ctx = {} const html = await renderToString(app, ctx) console.log(ctx.teleports) // { '#teleported': 'teleported content' }这个页面中的其他大多数 SSR API 也可以接受一个上下文对象。该上下文对象可以在组件代码里通过 useSSRContext 辅助函数进行访问。
renderToNodeStream()
将输入渲染为一个 Node.js Readable stream 实例。
导出自 vue/server-renderer
类型
function renderToNodeStream( input: App | VNode, context?: SSRContext ): Readable示例
// 在一个 Node.js http 处理函数内 renderToNodeStream(app).pipe(res)
备注
vue/server-renderer的 ESM 构建不支持此方法,因为它是与 Node.js 环境分离的。请换为使用pipeToNodeWritable。
pipeToNodeWritable()
将输入渲染并 pipe 到一个 Node.js Writable stream 实例。
导出自 vue/server-renderer
类型
function pipeToNodeWritable( input: App | VNode, context: SSRContext = {}, writable: Writable ): voidid示例
// 在一个 Node.js http 处理函数内 pipeToNodeWritable(app, {}, res)
renderToWebStream()
将输入渲染并 pipe 到一个 Web WritableStream 实例。
导出自 vue/server-renderer
类型
function renderToWebStream( input: App | VNode, context?: SSRContext ): ReadableStream示例
// 在一个支持 ReadableStream 的环境下 return new Response(renderToWebStream(app))
备注
在不能全局暴露 ReadableStream 构造函数的环境下,请换为使用
pipeToWebWritable()。
pipeToWebWritable()
将输入渲染并 pipe 到一个 Web WritableStream 实例。
导出自 vue/server-renderer
类型
function pipeToWebWritable( input: App | VNode, context: SSRContext = {}, writable: WritableStream ): void示例
通常与
TransformStream结合使用:// 诸如 CloudFlare worker 这样的环境中,TransformStream 是可用的。 // 在 Node.js 中,TransformStream 需要从 'stream/web' 显示导入。 const { readable, writable } = new TransformStream() pipeToWebWritable(app, {}, writable) return new Response(readable)
renderToSimpleStream()
通过一个简单的接口,将输入以 stream 模式进行渲染。
导出自 vue/server-renderer
类型
function renderToSimpleStream( input: App | VNode, context: SSRContext, options: SimpleReadable ): SimpleReadable interface SimpleReadable { push(content: string | null): void destroy(err: any): void }示例
let res = '' renderToSimpleStream( app, {}, { push(chunk) { if (chunk === null) { // done console(`render complete: ${res}`) } else { res += chunk } }, destroy(err) { // error encountered } } )
useSSRContext()
一个运行时 API,用于获取已传递给 renderToString() 或其他服务端渲染 API 的上下文对象。
类型
function useSSRContext<T = Record<string, any>>(): T | undefined示例
得到的上下文能够作为附加信息用于渲染最终的 HTML (例如 head 中的元数据)。
<script setup> import { useSSRContext } from 'vue' // 确保只在服务端渲染时调用 // https://cn.vitejs.dev/guide/ssr.html#conditional-logic if (import.meta.env.SSR) { const ctx = useSSRContext() // ...给上下文对象添加属性 } </script>
TypeScript 工具类型
TypeScript 工具类型 介绍
INFO
此页面仅列出了一些可能需要解释其使用方式的常用工具类型。有关导出类型的完整列表,请查看源代码。
PropType <T>
用于在用运行时 props 声明时给一个 prop 标注更复杂的类型定义。
示例
import type { PropType } from 'vue' interface Book { title: string author: string year: number } export default { props: { book: { // 提供一个比 `Object` 更具体的类型 type: Object as PropType<Book>, required: true } } }
MaybeRef <T>
T | Ref<T> 的别名。对于标注组合式函数的参数很有用。
- 仅在 3.3+ 版本中支持。
MaybeRefOrGetter<T>
T | Ref<T> | (() => T) 的别名。对于标注组合式函数的参数很有用。
- 仅在 3.3+ 版本中支持。
ExtractPropTypes<T>
从运行时的 props 选项对象中提取 props 类型。提取到的类型是面向内部的,也就是说组件接收到的是解析后的 props。这意味着 boolean 类型的 props 和带有默认值的 props 总是一个定义的值,即使它们不是必需的。
要提取面向外部的 props,即父组件允许传递的 props,请使用 ExtractPublicPropTypes。
示例
const propsOptions = { foo: String, bar: Boolean, baz: { type: Number, required: true }, qux: { type: Number, default: 1 } } as const type Props = ExtractPropTypes<typeof propsOptions> // { // foo?: string, // bar: boolean, // baz: number, // qux: number // }
ExtractPublicPropTypes<T>
从运行时的 props 选项对象中提取 prop。提取的类型是面向外部的,即父组件允许传递的 props。
示例
const propsOptions = { foo: String, bar: Boolean, baz: { type: Number, required: true }, qux: { type: Number, default: 1 } } as const type Props = ExtractPublicPropTypes<typeof propsOptions> // { // foo?: string, // bar?: boolean, // baz: number, // qux?: number // }
ComponentCustomProperties
用于增强组件实例类型以支持自定义全局属性。
示例
import axios from 'axios' declare module 'vue' { interface ComponentCustomProperties { $http: typeof axios $translate: (key: string) => string } }
TIP
类型扩展必须被放置在一个模块
.ts或.d.ts文件中。查看类型扩展指南了解更多细节
- 参考:指南 - 扩展全局属性
ComponentCustomOptions
用来扩展组件选项类型以支持自定义选项。
示例
import { Route } from 'vue-router' declare module 'vue' { interface ComponentCustomOptions { beforeRouteEnter?(to: any, from: any, next: () => void): void } }
TIP
类型扩展必须被放置在一个模块
.ts或.d.ts文件中。查看类型扩展指南了解更多细节。
- 参考:指南 - 扩展自定义选项
ComponentCustomProps
用于扩展全局可用的 TSX props,以便在 TSX 元素上使用没有在组件选项上定义过的 props。
示例
declare module 'vue' { interface ComponentCustomProps { hello?: string } } export {}// 现在即使没有在组件选项上定义过 hello 这个 prop 也依然能通过类型检查了 <MyComponent hello="world" />
TIP
类型扩展必须被放置在一个模块
.ts或.d.ts文件中。查看类型扩展指南了解更多细节。
CSSProperties
用于扩展在样式属性绑定上允许的值的类型。
- 示例
允许任意自定义 CSS 属性:
declare module 'vue' {
interface CSSProperties {
[key: `--${string}`]: string
}
}<div style={ { '--bg-color': 'blue' } }><div :style="{ '--bg-color': 'blue' }"></div>TIP
类型增强必须被放置在一个模块
.ts或.d.ts文件中。查看类型增强指南了解更多细节。
参考
SFC
<style>标签支持通过v-bindCSS 函数来链接 CSS 值与组件状态。这允许在没有类型扩展的情况下自定义属性。
自定义渲染
自定义渲染 介绍
createRenderer()
创建一个自定义渲染器。通过提供平台特定的节点创建以及更改 API,你可以在非 DOM 环境中也享受到 Vue 核心运行时的特性。
类型
function createRenderer<HostNode, HostElement>( options: RendererOptions<HostNode, HostElement> ): Renderer<HostElement> interface Renderer<HostElement> { render: RootRenderFunction<HostElement> createApp: CreateAppFunction<HostElement> } interface RendererOptions<HostNode, HostElement> { patchProp( el: HostElement, key: string, prevValue: any, nextValue: any, // the rest is unused for most custom renderers isSVG?: boolean, prevChildren?: VNode<HostNode, HostElement>[], parentComponent?: ComponentInternalInstance | null, parentSuspense?: SuspenseBoundary | null, unmountChildren?: UnmountChildrenFn ): void insert( el: HostNode, parent: HostElement, anchor?: HostNode | null ): void remove(el: HostNode): void createElement( type: string, isSVG?: boolean, isCustomizedBuiltIn?: string, vnodeProps?: (VNodeProps & { [key: string]: any }) | null ): HostElement createText(text: string): HostNode createComment(text: string): HostNode setText(node: HostNode, text: string): void setElementText(node: HostElement, text: string): void parentNode(node: HostNode): HostElement | null nextSibling(node: HostNode): HostNode | null // optional, DOM-specific querySelector?(selector: string): HostElement | null setScopeId?(el: HostElement, id: string): void cloneNode?(node: HostNode): HostNode insertStaticContent?( content: string, parent: HostElement, anchor: HostNode | null, isSVG: boolean ): [HostNode, HostNode] }示例
import { createRenderer } from '@vue/runtime-core' const { render, createApp } = createRenderer({ patchProp, insert, remove, createElement // ... }) // `render` 是底层 API // `createApp` 返回一个应用实例 export { render, createApp } // 重新导出 Vue 的核心 API export * from '@vue/runtime-core'Vue 自身的
@vue/runtime-dom也是利用这套 API 实现的。要想了解一个简单一些的实现,请参考@vue/runtime-test,这是一个 Vue 自己做单元测试的私有包。
