主页 > 手机  > 

vue3学习-2(深入组件)

vue3学习-2(深入组件)

vue3学习-2(深入组件) 1.开始2.基础3.深入组件注册全局注册局部注册组件名格式 PropsProps 声明响应式 Props 解构 3.5+将解构的 props 传递到函数中单向数据流更改对象 / 数组类型的 propsProp 校验 事件触发与监听事件事件参数声明触发的事件事件校验 组件 v-model基本用法底层机制`v-model` 的参数多个 `v-model` 绑定处理 `v-model` 修饰符 透传 AttributesAttributes 继承对 `class` 和 `style` 的合并`v-on` 监听器继承深层组件继承禁用 Attributes 继承多根节点的 Attributes 继承在 JavaScript 中访问透传 Attributes 插槽 Slots插槽内容与出口渲染作用域默认内容具名插槽条件插槽动态插槽名作用域插槽具名作用域插槽高级列表组件示例无渲染组件 依赖注入Prop 逐级透传问题Provide (提供)Inject (注入)和响应式数据配合使用使用 Symbol 作注入名 异步组件基本用法加载与错误状态惰性激活 3.5在空闲时进行激活在可见时激活在媒体查询匹配时进行激活交互时激活自定义策略搭配 Suspense 使用

1.开始 2.基础 3.深入组件 注册

一个 Vue 组件在使用前需要先被“注册”,这样 Vue 才能在渲染模板时找到其对应的实现。组件注册有两种方式:全局注册和局部注册。

全局注册

Vue 应用实例的 ponent() 方法,让组件在当前 Vue 应用中全局可用。

import { createApp } from 'vue' const app = createApp({}) app ponent( // 注册的名字 'MyComponent', // 组件的实现 { /* ... */ } )

如果使用单文件组件,你可以注册被导入的 .vue 文件:

js

import MyComponent from './App.vue' app ponent('MyComponent', MyComponent)

全局注册的组件可以在此应用的任意组件的模板中使用

局部注册

全局注册虽然很方便,但有以下几个问题:

全局注册,但并没有被使用的组件无法在生产打包时被自动移除 (也叫“tree-shaking”)。如果你全局注册了一个组件,即使它并没有被实际使用,它仍然会出现在打包后的 JS 文件中。全局注册在大型项目中使项目的依赖关系变得不那么明确。在父组件中使用子组件时,不太容易定位子组件的实现。和使用过多的全局变量一样,这可能会影响应用长期的可维护性。

相比之下,局部注册的组件需要在使用它的父组件中显式导入,并且只能在该父组件中使用。它的优点是使组件之间的依赖关系更加明确,并且对 tree-shaking 更加友好。

在使用 <script setup> 的单文件组件中,导入的组件可以直接在模板中使用,无需注册:

<script setup> import ComponentA from './ComponentA.vue' </script> <template> <ComponentA /> </template>

如果没有使用 <script setup>,则需要使用 components 选项来显式注册:

import ComponentA from './ComponentA.js' export default { components: { ComponentA }, setup() { // ... } }

对于每个 components 对象里的属性,它们的 key 名就是注册的组件名,而值就是相应组件的实现。上面的例子中使用的是 ES2015 的缩写语法,等价于:

js

export default { components: { ComponentA: ComponentA } // ... }

注意:局部注册的组件在后代组件中不可用。

组件名格式

在整个指引中,我们都使用 PascalCase 作为组件名的注册格式,这是因为:

PascalCase 是合法的 JavaScript 标识符。这使得在 JavaScript 中导入和注册组件都很容易,同时 IDE 也能提供较好的自动补全。<PascalCase /> 在模板中更明显地表明了这是一个 Vue 组件,而不是原生 HTML 元素。同时也能够将 Vue 组件和自定义元素 (web components) 区分开来。

在单文件组件和内联字符串模板中,我们都推荐这样做。但是,PascalCase 的标签名在 DOM 内模板中是不可用的,详情参见 DOM 内模板解析注意事项。

为了方便,Vue 支持将模板中使用 kebab-case 的标签解析为使用 PascalCase 注册的组件。这意味着一个以 MyComponent 为名注册的组件,在模板 (或由 Vue 渲染的 HTML 元素) 中可以通过 <MyComponent> 或 <my-component> 引用。这让我们能够使用同样的 JavaScript 组件注册代码来配合不同来源的模板。

Props Props 声明

在使用 <script setup> 的单文件组件中,props 可以使用 defineProps() 宏来声明:

<script setup> //字符串数组方式声明 const props = defineProps(['foo']) //对象方式声明 //defineProps({ // title: String, // likes: Number //}) console.log(props.foo) </script>

在没有使用 <script setup> 的组件中,props 可以使用 props 选项来声明:

export default { //字符串数组方式声明 props: ['foo'], //对象方式声明 //props: { // title: String, // likes: Number //} setup(props) { // setup() 接收 props 作为第一个参数 console.log(props.foo) } }

TypeScript 使用 <script setup>,也可以使用类型标注来声明 props:

<script setup lang="ts"> defineProps<{ title?: string likes?: number }>() </script> 响应式 Props 解构 3.5+ const { foo } = defineProps(['foo']) watchEffect(() => { // 在 3.5 之前只运行一次 // 在 3.5+ 中在 "foo" prop 变化时重新执行,`foo` 由编译器转换为 `props.foo` // 在 3.4 及以下版本,`foo` 是一个实际的常量,永远不会改变。 console.log(foo) })

此外,你可以使用 JavaScript 原生的默认值语法声明 props 默认值。这在使用基于类型的 props 声明时特别有用。

const { foo = 'hello' } = defineProps<{ foo?: string }>() 将解构的 props 传递到函数中 const { foo } = defineProps(['foo']) // 错误,它等价于 watch(props.foo, ...)——我们给 watch 传递的是一个值而不是响应式数据源 // watch(foo, /* ... */) watch(() => foo, /* ... */)

此外,当我们需要传递解构的 prop 到外部函数中并保持响应性时,这是推荐做法:

useComposable(() => foo)

传递组件的props官方推荐使用kebab-case,定义的时候推荐使用驼峰。

单向数据流

所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递。

不应该在子组件中去更改一个 prop

prop 被用于传入初始值;而子组件想在之后将其作为一个局部数据属性。在这种情况下,最好是新定义一个局部数据属性,从 props 上获取初始值即可:

const props = defineProps(['initialCounter']) // 计数器只是将 props.initialCounter 作为初始值 // 像下面这样做就使 prop 和后续更新无关了 const counter = ref(props.initialCounter)

需要对传入的 prop 值做进一步的转换。在这种情况中,最好是基于该 prop 值定义一个计算属性:

const props = defineProps(['size']) // 该 prop 变更时计算属性也会自动更新 const normalizedSize = computed(() => props.size.trim().toLowerCase()) 更改对象 / 数组类型的 props

当对象或数组作为 props 被传入时,虽然子组件无法更改 props 绑定,但仍然可以更改对象或数组内部的值。这是因为 JavaScript 的对象和数组是按引用传递,不要改,否则子组件隐式传递给父组件。

Prop 校验 defineProps({ // 基础类型检查 // (给出 `null` 和 `undefined` 值则会跳过任何类型检查) propA: Number, // 多种可能的类型 propB: [String, Number], // 必传,且为 String 类型 propC: { type: String, required: true }, // 必传但可为 null 的字符串 propD: { type: [String, null], required: true }, // Number 类型的默认值 propE: { type: Number, default: 100 }, // 对象类型的默认值 propF: { type: Object, // 对象或数组的默认值 // 必须从一个工厂函数返回。 // 该函数接收组件所接收到的原始 prop 作为参数。 default(rawProps) { return { message: 'hello' } } }, // 自定义类型校验函数 // 在 3.4+ 中完整的 props 作为第二个参数传入 propG: { validator(value, props) { // The value must match one of these strings return ['success', 'warning', 'danger'].includes(value) } }, // 函数类型的默认值 propH: { type: Function, // 不像对象或数组的默认,这不是一个 // 工厂函数。这会是一个用来作为默认值的函数 default() { return 'Default function' } } })

defineProps() 宏中的参数不可以访问 <script setup> 中定义的其他变量,因为在编译时整个表达式都会被移到外部的函数中。

事件 触发与监听事件

在组件的模板表达式中,可以直接使用 $emit 方法触发自定义事件 (例如:在 v-on 的处理函数中):

<!-- MyComponent --> <button @click="$emit('someEvent')">Click Me</button>

父组件可以通过 v-on (缩写为 @) 来监听事件:

<MyComponent @some-event="callback" />

同样,组件的事件监听器也支持 .once 修饰符:

<MyComponent @some-event.once="callback" />

建议:和原生 DOM 事件不一样,组件触发的事件没有冒泡机制。你只能监听直接子组件触发的事件。平级组件或是跨越多层嵌套的组件间通信,应使用一个外部的事件总线,或是使用一个全局状态管理方案。

事件参数

所有传入 $emit() 的额外参数都会被直接传向监听器。举例来说,$emit('foo', 1, 2, 3) 触发后,监听器函数将会收到这三个参数值。

声明触发的事件

组件可以显式地通过 defineEmits() 宏来声明它要触发的事件:

我们在 <template> 中使用的 $emit 方法不能在组件的 <script setup> 部分中使用,但 defineEmits() 会返回一个相同作用的函数供我们使用:

<script setup> const emit = defineEmits(['inFocus', 'submit']) function buttonClick() { emit('submit') } </script> // 显式地使用了 setup 函数而不是 <script setup> export default { emits: ['inFocus', 'submit'], setup(props, ctx) { ctx.emit('submit') } }

也可以这样解构

export default { emits: ['inFocus', 'submit'], setup(props, { emit }) { emit('submit') } } 事件校验 <script setup> const emit = defineEmits({ // 没有校验 click: null, // 校验 submit 事件 submit: ({ email, password }) => { if (email && password) { return true } else { console.warn('Invalid submit event payload!') return false } } }) function submitForm(email, password) { emit('submit', { email, password }) } </script> 组件 v-model 基本用法

v-model 可以在组件上使用以实现双向绑定。

从 Vue 3.4 开始,推荐的实现方式是使用 defineModel() 宏:

<!-- Child.vue --> <script setup> const model = defineModel() function update() { model.value++ } </script> <template> <div>Parent bound v-model is: {{ model }}</div> <button @click="update">Increment</button> </template>

父组件可以用 v-model 绑定一个值:

<!-- Parent.vue --> <Child v-model="countModel" />

包装原生 input 元素

<script setup> const model = defineModel() </script> <template> <input v-model="model" /> </template> 底层机制

defineModel 是一个便利宏。编译器将其展开为以下内容:

一个名为 modelValue 的 prop,本地 ref 的值与其同步;一个名为 update:modelValue 的事件,当本地 ref 的值发生变更时触发。

在 3.4 版本之前,你一般会按照如下的方式来实现上述相同的子组件:

<!-- Child.vue --> <script setup> const props = defineProps(['modelValue']) const emit = defineEmits(['update:modelValue']) </script> <template> <input :value="props.modelValue" @input="emit('update:modelValue', $event.target.value)" /> </template>

然后,父组件中的 v-model="foo" 将被编译为:

<!-- Parent.vue --> <Child :modelValue="foo" @update:modelValue="$event => (foo = $event)" /> v-model 的参数

组件上的 v-model 也可以接受一个参数:

<MyComponent v-model:title="bookTitle" />

在子组件中,我们可以通过将字符串作为第一个参数传递给 defineModel() 来支持相应的参数:

<!-- MyComponent.vue --> <script setup> const title = defineModel('title') </script> <template> <input type="text" v-model="title" /> </template>

如果需要额外的 prop 选项,应该在 model 名称之后传递:

const title = defineModel('title', { required: true }) 多个 v-model 绑定

组件上的每一个 v-model 都会同步不同的 prop,而无需额外的选项:

<UserName v-model:first-name="first" v-model:last-name="last" /> <script setup> const firstName = defineModel('firstName') const lastName = defineModel('lastName') </script> <template> <input type="text" v-model="firstName" /> <input type="text" v-model="lastName" /> </template> 处理 v-model 修饰符

创建一个自定义的修饰符 capitalize,它会自动将 v-model 绑定输入的字符串值第一个字母转为大写:

<MyComponent v-model.capitalize="myText" />

通过像这样解构 defineModel() 的返回值,可以在子组件中访问添加到组件 v-model 的修饰符,可以给 defineModel() 传入 get 和 set 这两个选项,处理返回的值:

<script setup> const [model, modifiers] = defineModel({ set(value) { if (modifiers.capitalize) { return value.charAt(0).toUpperCase() + value.slice(1) } return value } }) </script> <template> <input type="text" v-model="model" /> </template> 透传 Attributes Attributes 继承 “透传 attribute”指的是传递给一个组件,却没有被该组件声明为 props 或 emits 的 attribute 或者 v-on 事件监听器。最常见的例子就是 class、style 和 id。 对 class 和 style 的合并 如果一个子组件的根元素已经有了 class 或 style attribute,它会和从父组件上继承的值合并。 v-on 监听器继承 子组件和父组件继承的监听器都会被触发 深层组件继承

例如子组件中有子子组件

此时 <MyButton> 接收的透传 attribute 会直接继续传给 <BaseButton>。

请注意:

透传的 attribute 不会包含 <MyButton> 上声明过的 props 或是针对 emits 声明事件的 v-on 侦听函数,换句话说,声明过的 props 和侦听函数被 <MyButton>“消费”了。透传的 attribute 若符合声明,也可以作为 props 传入 <BaseButton>。 禁用 Attributes 继承

不想要一个组件自动地继承 attribute,从 3.3 开始你也可以直接在 <script setup> 中使用 defineOptions:

<script setup> defineOptions({ inheritAttrs: false }) // ...setup 逻辑 </script>

这些透传进来的 attribute 可以在模板的表达式中直接用 $attrs 访问到。$attrs 对象包含了除组件所声明的 props 和 emits 之外的所有其他 attribute,例如 class,style,v-on 监听器等等。

和 props 有所不同,透传 attributes 在 JavaScript 中保留了它们原始的大小写,所以像 foo-bar 这样的一个 attribute 需要通过 $attrs['foo-bar'] 来访问。

像 @click 这样的一个 v-on 事件监听器将在此对象下被暴露为一个函数 $attrs.onClick。

如果只是想在button应用透传来的attributes 在div不使用

<div class="btn-wrapper"> <button class="btn" v-bind="$attrs">Click Me</button> </div>

没有参数的 v-bind 会将一个对象的所有属性都作为 attribute 应用到目标元素上。

多根节点的 Attributes 继承

和单根节点组件有所不同,有着多个根节点的组件没有自动 attribute 透传行为。如果 $attrs 没有被显式绑定,将会抛出一个运行时警告。

<CustomLayout id="custom-layout" @click="changeValue" />

如果 $attrs 被显式绑定,则不会有警告:

<header>...</header> <main v-bind="$attrs">...</main> <footer>...</footer> 在 JavaScript 中访问透传 Attributes

你可以在 <script setup> 中使用 useAttrs() API 来访问一个组件的所有透传 attribute:

<script setup> import { useAttrs } from 'vue' const attrs = useAttrs() </script>

如果没有使用 <script setup>,attrs 会作为 setup() 上下文对象的一个属性暴露:

export default { setup(props, ctx) { // 透传 attribute 被暴露为 ctx.attrs console.log(ctx.attrs) } } attrs 的特性: attrs 总是反映最新的透传属性,但不是响应式的。不能通过 watch 或 computed 监听其变化。 解决方案: 使用 onUpdated 生命周期钩子,在每次更新时访问最新的 attrs。使用 watchEffect 或 watch,尽管 attrs 本身非响应式,但可以通过这些工具响应某些属性的变化。如果需要响应性,可以将属性定义为 props。 插槽 Slots 插槽内容与出口

<slot> 元素是一个插槽出口 (slot outlet),标示了父元素提供的插槽内容 (slot content) 将在哪里被渲染。

插槽内容可以是任意合法的模板内容,不局限于文本。例如我们可以传入多个元素,甚至是组件

渲染作用域

插槽内容可以访问到父组件的数据作用域,因为插槽内容本身是在父组件模板中定义的。插槽内容无法访问子组件的数据。

默认内容

中添加内容后,如果父组件不传内容,会显示这个默认内容

具名插槽 <script setup> import BaseLayout from './BaseLayout.vue' </script> <template> <BaseLayout> <template #header> <h1>Here might be a page title</h1> </template> <!-- 默认的用#default或者不用 #和v-slot一样 --> <template #default> <p>A paragraph for the main content.</p> <p>And another one.</p> </template> <template #footer> <p>Here's some contact info</p> </template> </BaseLayout> </template> <template> <div class="container"> <header> <slot name="header"></slot> </header> <main> <slot></slot> </main> <footer> <slot name="footer"></slot> </footer> </div> </template> <style> footer { border-top: 1px solid #ccc; color: #666; font-size: 0.8em; } </style>

条件插槽

根据传入的插槽名包装样式

<template> <div class="card"> <div v-if="$slots.header" class="card-header"> <slot name="header" /> </div> <div v-if="$slots.default" class="card-content"> <slot /> </div> <div v-if="$slots.footer" class="card-footer"> <slot name="footer" /> </div> </div> </template> 动态插槽名 <base-layout> <template v-slot:[dynamicSlotName]> ... </template> <!-- 缩写为 --> <template #[dynamicSlotName]> ... </template> </base-layout> 作用域插槽 <!-- <MyComponent> 的模板 --> <div> <slot :text="greetingMessage" :count="1"></slot> </div> <MyComponent v-slot="slotProps"> {{ slotProps.text }} {{ slotProps.count }} </MyComponent>

你可以将作用域插槽类比为一个传入子组件的函数。子组件会将相应的 props 作为参数传给它。

具名作用域插槽

v-slot:name="slotProps"。当使用缩写时是这样

<MyComponent> <template #header="headerProps"> {{ headerProps }} </template> <template #default="defaultProps"> {{ defaultProps }} </template> <template #footer="footerProps"> {{ footerProps }} </template> </MyComponent>

向具名插槽中传入 props:

<slot name="header" message="hello"></slot>

注意插槽上的 name 是一个 Vue 特别保留的 attribute,不会作为 props 传递给插槽。因此最终 headerProps 的结果是 { message: 'hello' }。

如果你同时使用了具名插槽与默认插槽,则需要为默认插槽使用显式的 <template> 标签。尝试直接为组件添加 v-slot 指令将导致编译错误。

<MyComponent> <!-- 使用显式的默认插槽 --> <template #default="{ message }"> <p>{{ message }}</p> </template> <template #footer> <p>Here's some contact info</p> </template> </MyComponent> 高级列表组件示例

看一个 <FancyList> 组件的例子。它会渲染一个列表,并同时会封装一些加载远端数据的逻辑、使用数据进行列表渲染、或者是像分页或无限滚动这样更进阶的功能。然而我们希望它能够保留足够的灵活性,将对单个列表元素内容和样式的控制权留给使用它的父组件。我们期望的用法可能是这样的:

<FancyList :api-url="url" :per-page="10"> <template #item="{ body, username, likes }"> <div class="item"> <p>{{ body }}</p> <p>by {{ username }} | {{ likes }} likes</p> </div> </template> </FancyList>

在 <FancyList> 之中,我们可以多次渲染 <slot> 并每次都提供不同的数据 (注意我们这里使用了 v-bind 来传递插槽的 props):

<ul> <li v-for="item in items"> <slot name="item" v-bind="item"></slot> </li> </ul> 无渲染组件

一些组件可能只包括了逻辑而不需要自己渲染内容,视图输出通过作用域插槽全权交给了消费者组件。我们将这种类型的组件称为无渲染组件。

<MouseTracker v-slot="{ x, y }"> Mouse is at: {{ x }}, {{ y }} </MouseTracker>

大部分能用无渲染组件实现的功能都可以通过组合式 API 以另一种更高效的方式实现,并且还不会带来额外组件嵌套的开销。

依赖注入 Prop 逐级透传问题

有一些多层级嵌套的组件,深层的组件需要用一个较远的祖先组件的时候,需要层层传递,中间组件是不需要这些props的但是需要层层传递,这一问题被称为“prop 逐级透传”,这样一是会影响其他组件,二是比较麻烦,这里就会使用到provide 和 inject 。一个父组件相对于其所有的后代组件,会作为依赖提供者。任何后代的组件树,无论层级有多深,都可以注入由父组件提供给整条链路的依赖。

Provide (提供)

要为组件后代提供数据,需要使用到 provide() 函数:

<script setup> import { provide } from 'vue' provide(/* 注入名 */ 'message', /* 值 */ 'hello!') </script>

如果不使用 <script setup>,请确保 provide() 是在 setup() 同步调用的:

import { provide } from 'vue' export default { setup() { provide(/* 注入名 */ 'message', /* 值 */ 'hello!') } }

provide() 函数接收两个参数。第一个参数被称为注入名,可以是一个字符串或是一个 Symbol。

第二个参数是提供的值,值可以是任意类型,包括响应式的状态,比如一个 ref:

import { ref, provide } from 'vue' const count = ref(0) provide('key', count)

也可以在应用层全局提供。

import { createApp } from 'vue' const app = createApp({}) app.provide(/* 注入名 */ 'message', /* 值 */ 'hello!') Inject (注入) <script setup> import { inject } from 'vue' const message = inject('message') </script> // 如果没有祖先组件提供 "message" // `value` 会是 "这是默认值" const value = inject('message', '这是默认值') //默认值可能需要通过调用一个函数或初始化一个类来取得。为了避免在用不到默认值的情况下进行不必要的计算或产生副作用,我们可以使用工厂函数来创建默认值 const value = inject('key', () => new ExpensiveClass(), true) 和响应式数据配合使用

当提供 / 注入响应式的数据时,建议尽可能将任何对响应式状态的变更都保持在供给方组件中。这样可以确保所提供状态的声明和变更操作都内聚在同一个组件内,使其更容易维护。

<!-- 在供给方组件内 --> <script setup> import { provide, ref } from 'vue' const location = ref('North Pole') function updateLocation() { location.value = 'South Pole' } provide('location', { location, updateLocation }) </script> <!-- 在注入方组件 --> <script setup> import { inject } from 'vue' const { location, updateLocation } = inject('location') </script> <template> <button @click="updateLocation">{{ location }}</button> </template>

最后,如果你想确保提供的数据不能被注入方的组件更改,你可以使用 readonly() 来包装提供的值

<script setup> import { ref, provide, readonly } from 'vue' const count = ref(0) provide('read-only-count', readonly(count)) </script> 使用 Symbol 作注入名

正在构建大型的应用,包含非常多的依赖提供,或者你正在编写提供给其他开发者使用的组件库,建议最好使用 Symbol 来作为注入名以避免潜在的冲突。

我们通常推荐在一个单独的文件中导出这些注入名 Symbol:

// keys.js export const myInjectionKey = Symbol() // 在供给方组件中 import { provide } from 'vue' import { myInjectionKey } from './keys.js' provide(myInjectionKey, { /* 要提供的数据 */ }) // 注入方组件 import { inject } from 'vue' import { myInjectionKey } from './keys.js' const injected = inject(myInjectionKey) 异步组件 基本用法

在大型项目中,我们可能需要拆分应用为更小的块,并仅在需要时再从服务器加载相关组件。Vue 提供了 defineAsyncComponent 方法来实现此功能:

import { defineAsyncComponent } from 'vue' const AsyncComp = defineAsyncComponent(() => { return new Promise((resolve, reject) => { // ...从服务器获取组件 resolve(/* 获取到的组件 */) }) }) // ... 像使用其他一般组件一样使用 `AsyncComp` import { defineAsyncComponent } from 'vue' const AsyncComp = defineAsyncComponent(() => import('./components/MyComponent.vue') ) 加载与错误状态 const AsyncComp = defineAsyncComponent({ // 加载函数 loader: () => import('./Foo.vue'), // 加载异步组件时使用的组件 loadingComponent: LoadingComponent, // 展示加载组件前的延迟时间,默认为 200ms delay: 200, // 加载失败后展示的组件 errorComponent: ErrorComponent, // 如果提供了一个 timeout 时间限制,并超时了 // 也会显示这里配置的报错组件,默认值是:Infinity timeout: 3000 }) 惰性激活 3.5 在空闲时进行激活

通过 requestIdleCallback 进行激活:

import { defineAsyncComponent, hydrateOnIdle } from 'vue' const AsyncComp = defineAsyncComponent({ loader: () => import('./Comp.vue'), hydrate: hydrateOnIdle(/* 传递可选的最大超时 */) }) 在可见时激活

通过 IntersectionObserver 在元素变为可见时进行激活。

import { defineAsyncComponent, hydrateOnVisible } from 'vue' const AsyncComp = defineAsyncComponent({ loader: () => import('./Comp.vue'), hydrate: hydrateOnVisible() })

可以选择传递一个侦听器的选项对象值:

hydrateOnVisible({ rootMargin: '100px' }) 在媒体查询匹配时进行激活

当指定的媒体查询匹配时进行激活。

import { defineAsyncComponent, hydrateOnMediaQuery } from 'vue' const AsyncComp = defineAsyncComponent({ loader: () => import('./Comp.vue'), hydrate: hydrateOnMediaQuery('(max-width:500px)') }) 交互时激活

当组件元素上触发指定事件时进行激活。完成激活后,触发激活的事件也将被重放。

import { defineAsyncComponent, hydrateOnInteraction } from 'vue' const AsyncComp = defineAsyncComponent({ loader: () => import('./Comp.vue'), hydrate: hydrateOnInteraction('click') })

也可以是多个事件类型的列表:

hydrateOnInteraction(['wheel', 'mouseover']) 自定义策略 import { defineAsyncComponent, type HydrationStrategy } from 'vue' const myStrategy: HydrationStrategy = (hydrate, forEachElement) => { // forEachElement 是一个遍历组件未激活的 DOM 中所有根元素的辅助函数, // 因为根元素可能是一个片段而非单个元素 forEachElement(el => { // ... }) // 准备好时调用 `hydrate` hydrate() return () => { // 如必要,返回一个销毁函数 } } const AsyncComp = defineAsyncComponent({ loader: () => import('./Comp.vue'), hydrate: myStrategy }) 搭配 Suspense 使用

异步组件可以搭配内置的 <Suspense> 组件一起使用,若想了解 <Suspense> 和异步组件之间交互,请参阅 <Suspense> 章节。

标签:

vue3学习-2(深入组件)由讯客互联手机栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“vue3学习-2(深入组件)