当前位置: 首页 > news >正文

Vue 3 的组件式开发(2)

1 Vue 3 组件的插槽

插槽(Slot)是Vue组件中的一个重要概念,它允许父组件向子组件中插入HTML结构或其他组件,从而实现内容的自定义和复用。以下是对Vue 3组件插槽的详细讲解:

1.1 插槽的基本概念

插槽可以被视为子组件中预留给父组件填充内容的占位符。在子组件中,你可以使用<slot>标签来定义插槽,而在父组件中,你可以通过在该标签内部放置内容来填充这个插槽。

1.2 默认插槽(匿名插槽)

默认插槽是最简单的插槽类型,它没有名字,因此也被称为匿名插槽。在子组件中,你只需要使用一个<slot>标签来定义它。在父组件中,你可以直接在该标签内部放置需要插入的内容。

<!-- 子组件 -->
<template><div><p>我是子组件</p><slot></slot> <!-- 默认插槽 --></div>
</template><!-- 父组件 -->
<template><div><ChildComponent><p>这是父组件插入到子组件的内容</p></ChildComponent></div>
</template><script setup>
import ChildComponent from './ChildComponent.vue';
</script>

1.3 具名插槽

当子组件中有多个插槽时,你可以使用具名插槽来区分它们。在子组件中,你需要给<slot>标签添加一个name属性来指定插槽的名字。在父组件中,你可以使用v-slot:插槽名(简写为#插槽名)来指定要填充哪个插槽。

<!-- 子组件 -->
<template><div><p>我是子组件</p><slot name="header"></slot> <!-- 具名插槽 --><slot name="content"></slot> <!-- 具名插槽 --><slot name="footer"></slot> <!-- 具名插槽 --></div>
</template><!-- 父组件 -->
<template><div><ChildComponent><template #header><h1>这是头部内容</h1></template><template #content><p>这是主体内容</p></template><template #footer><p>这是底部内容</p></template></ChildComponent></div>
</template><script setup>
import ChildComponent from './ChildComponent.vue';
</script>

1.4 动态插槽名

Vue 2.6.0+版本支持动态插槽名,这意味着插槽名可以是一个变量,你可以根据变量的值来动态地选择填充哪个插槽。在父组件中,你可以使用v-slot:[动态插槽名](简写为#[动态插槽名])来实现这一点。

<!-- 父组件 -->
<template><div><ChildComponent><template #[currentSlotName]><p>这是动态插槽的内容</p></template></ChildComponent><button @click="changeSlotName">切换插槽</button></div>
</template><script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';const currentSlotName = ref('header');function changeSlotName() {currentSlotName.value = currentSlotName.value === 'header' ? 'content' : 'header';
}
</script>

1.5 作用域插槽

作用域插槽允许子组件向父组件传递数据,以便父组件在填充插槽时使用这些数据。在子组件中,你可以使用v-bind(简写为:)在<slot>标签上绑定数据。在父组件中,你可以使用v-slot指令接收这些数据,并根据需要进行处理。

<!-- 子组件 -->
<template><div><slot :user="userData"></slot></div>
</template><script setup>
import { ref } from 'vue';const userData = ref({name: '张三',age: 30
});
</script><!-- 父组件 -->
<template><div><ChildComponent v-slot:default="{ user }"><p>用户名:{{ user.name }}</p><p>年龄:{{ user.age }}</p></ChildComponent></div>
</template><script setup>
import ChildComponent from './ChildComponent.vue';
</script>

在这个例子中,子组件通过作用域插槽向父组件传递了userData对象。父组件在填充插槽时接收了这个对象,并使用了它的nameage属性。

1.6 插槽的默认内容

当父组件没有提供任何插槽内容时,你可以为子组件的插槽指定默认内容。这可以通过在<slot>标签内部放置默认内容来实现。

<!-- 子组件 -->
<template><div><p>我是子组件</p><slot>这是默认内容</slot> <!-- 带有默认内容的插槽 --></div>
</template><!-- 父组件 -->
<template><div><ChildComponent></ChildComponent> <!-- 没有提供插槽内容 --></div>
</template><script setup>
import ChildComponent from './ChildComponent.vue';
</script>

在这个例子中,如果父组件没有提供任何插槽内容,子组件将显示默认内容“这是默认内容”。

2 Vue 3 单文件组件

Vue 3单文件组件(Single-File Components,简称SFC)是一种特殊的文件格式,它允许开发者将Vue组件的模板(Template)、脚本(Script)和样式(Style)封装在同一个文件中,从而提高代码的可维护性和可读性。以下是对Vue 3单文件组件的详细讲解:

2.1 单文件组件的结构

每一个.vue文件都由三种顶层语言块构成:<template><script><style>,以及一些其他的自定义块。

  1. <template>:定义了组件的HTML结构。在Vue 3中,模板语法得到了进一步的优化,支持更多的指令和功能,如条件渲染、列表渲染、事件处理等。
  2. <script>:包含了组件的JavaScript逻辑。在这里,可以定义组件的数据、方法、计算属性、生命周期钩子等。Vue 3引入了组合式API(Composition API),使得逻辑复用和组件组织变得更加灵活。
  3. <style>:用于定义组件的样式。Vue 3支持scoped属性,使得样式只作用于当前组件,避免了全局样式的污染。此外,还可以使用CSS预处理器(如Sass、Less等)来编写更复杂的样式。

2.2 单文件组件的优点

  1. 模块化:将组件的模板、脚本和样式封装在一起,使得组件更加模块化,易于维护和复用。
  2. 语法熟悉:使用熟悉的HTML、CSS和JavaScript语法来编写组件,降低了学习成本。
  3. 预编译模板:Vue 3在构建阶段会对模板进行预编译,避免了运行时的编译开销,提高了性能。
  4. 组件作用域的CSS:通过scoped属性,可以确保样式只作用于当前组件,避免了全局样式的冲突。
  5. 更好的IDE支持:许多现代IDE都提供了对Vue单文件组件的良好支持,包括语法高亮、自动补全、类型检查等功能。
  6. 热更新(HMR):在开发阶段,可以支持模块热替换(Hot Module Replacement),使得开发者可以在不刷新页面的情况下更新组件。

2.3 单文件组件的使用

  1. 创建组件:使用Vue CLI或Vite等构建工具可以轻松地创建Vue 3单文件组件。这些工具提供了合理的默认配置,并可以通过插件进行扩展。
  2. 导入组件:在父组件中,可以使用import语句来导入子组件,并在components选项中注册它。然后,就可以在父组件的模板中使用这个子组件了。
  3. 自定义块:除了<template><script><style>之外,.vue文件还可以包含其他自定义块。这些自定义块可以用于文档、国际化、类型定义等目的。自定义块的处理需要依赖工具链,如Vite插件或Webpack loader等。

2.4 单文件组件的编译

在构建过程中,Vue 3单文件组件会被编译为标准的JavaScript和CSS。具体来说:

  1. 模板编译<template>中的内容会被提取出来,传递给@vue/compiler-dom进行预编译,生成JavaScript渲染函数,并附在导出的组件上作为其render选项。
  2. 脚本编译<script>中的内容会被当作ES模块来执行。如果使用了<script setup>语法糖,它会被预处理为组件的setup()函数。
  3. 样式编译<style>中的内容会被抽取、合并成单独的CSS文件(在生产环境下),并注入到页面中。如果使用了scoped属性,Vue会在编译时为每个组件生成唯一的属性选择器,以确保样式只作用于当前组件。

2.5 示例

以下是一个简单的Vue 3单文件组件示例:

<template><div class="hello"><h1>{{ msg }}</h1><button @click="increment">点击我</button><p>当前计数:{{ count }}</p></div>
</template><script setup>
import { ref } from 'vue';const msg = 'Hello, Vue 3!';
const count = ref(0);function increment() {count.value++;
}
</script><style scoped>
.hello {text-align: center;
}button {margin-top: 20px;padding: 10px 20px;font-size: 16px;
}
</style>

在这个示例中,我们定义了一个简单的Vue 3单文件组件,它包含一个消息、一个按钮和一个计数器。通过点击按钮,可以增加计数器的值。样式部分使用了scoped属性,确保样式只作用于当前组件。

3 Vue 3 组件的生命周期

在Vue.js框架中,组件生命周期是指组件从创建到销毁的整个过程。Vue 3引入了新的组件生命周期钩子函数,为开发者提供了更精细的控制能力。下面是对Vue 3组件生命周期中关键钩子函数的详细讲解:

3.1 beforeCreate / created

  • beforeCreate

    • 触发时机:在实例初始化之后、数据观测之前被调用。
    • 组件状态:此时组件的data和methods还未初始化,Vue实例的数据和事件都没有初始化。
    • 用途:由于此时组件尚未完全创建,因此一般不在此阶段进行组件的逻辑处理。但在一些特殊情况下,如需要全局变量或事件监听器的初始化设置,可以在此阶段进行。
  • created

    • 触发时机:在实例创建完成后被立即调用。
    • 组件状态:此时组件的data和methods已经初始化,但模板渲染还未开始,尚未挂载DOM。Vue实例已经完成了数据观测(data observer)和事件初始化。
    • 用途:此阶段可以进行一些异步请求、事件监听器的注册等操作。因为此时组件的数据和方法已经可用,但DOM还未生成,所以适合进行一些与数据相关的初始化工作。

3.2 beforeMount / mounted

  • beforeMount

    • 触发时机:在挂载之前被调用,此时模板编译已经完成,但DOM尚未生成。
    • 组件状态:组件已经完成了其响应式状态的设置,但还没有创建DOM节点。Vue实例已经生成了一个虚拟DOM,并且将要被渲染到实际的DOM元素上。
    • 用途:此阶段可以进行一些DOM相关的操作,例如获取DOM节点、样式等。因为此时模板已经编译完成,但还未挂载到DOM上,所以适合进行一些与模板编译相关的准备工作。
  • mounted

    • 触发时机:在挂载之后被调用,此时组件已经被挂载到页面上,可以访问到组件的DOM元素。
    • 组件状态:所有同步子组件都已经被挂载,其自身的DOM树已经创建完成并插入了父容器中。
    • 用途:此阶段通常用于执行需要访问组件所渲染的DOM树相关的操作,例如对DOM元素进行样式修改、事件绑定等。因为此时组件已经挂载到DOM上,所以可以安全地操作DOM元素。

3.3 beforeUpdate / updated

  • beforeUpdate

    • 触发时机:在组件更新之前被调用,此时数据已经被更改,但页面还未重新渲染。
    • 组件状态:Vue实例的数据已经发生了改变,但是DOM还没有被重新渲染。
    • 用途:此阶段可以用于在Vue更新DOM之前访问DOM状态,或者进行一些在更新前需要完成的逻辑处理。例如,可以在此阶段手动更改DOM元素的状态以匹配新的数据。
  • updated

    • 触发时机:在组件更新后被调用,此时组件的DOM已经重新渲染完毕。
    • 组件状态:Vue实例的数据已经发生了改变,并且DOM已经被重新渲染。
    • 用途:此阶段通常用于执行更新后的副作用操作,例如更新后的DOM元素样式调整、动画效果等。但需要注意避免在此阶段更改组件的状态,这可能会导致无限更新循环。

3.4 beforeUnmount / unmounted

  • beforeUnmount

    • 触发时机:在组件卸载之前被调用,此时组件仍然可用,但DOM已经被删除。
    • 组件状态:组件即将被销毁,但尚未完全销毁。此时组件实例依然还保有全部的功能。
    • 用途:此阶段可以用于执行一些清理操作,例如取消事件监听器、清除定时器等。这些操作有助于避免内存泄漏和不必要的资源消耗。
  • unmounted

    • 触发时机:在组件卸载之后被调用,此时组件已经完全销毁,无法再次使用。
    • 组件状态:组件已经被销毁,所有的事件监听器和子组件都将被移除,所有的子组件的destroyed钩子也会被调用。此时组件的所有指令都已解绑,所有的响应式作用(渲染作用以及setup()时创建的计算属性和侦听器)都已经停止。
    • 用途:此阶段通常用于执行一些最终的清理工作,例如手动清理一些副作用(计时器、DOM事件监听器或者与服务器的连接等)。但需要注意此时组件已经完全销毁,无法再进行任何操作。

3.4 综合示例

<!-- MyComponent.vue -->
<template><div><h1>{{ message }}</h1><button @click="updateMessage">Update Message</button></div>
</template><script>
import { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue';export default {setup() {const message = ref('Hello, Vue 3!');// 在组件挂载之前调用onBeforeMount(() => {console.log('Before mount');// 此时,组件的模板已经渲染为HTML,但还没有添加到页面中});// 在组件挂载完成后调用onMounted(() => {console.log('Mounted');// 此时,可以执行依赖于DOM的操作,例如获取元素的尺寸或启动动画// 也可以进行DOM操作、数据请求等});// 在组件更新之前调用onBeforeUpdate(() => {console.log('Before update');// 此时,内存中的数据已经被更新,但视图中的数据还没有更新});// 在组件更新完成后调用onUpdated(() => {console.log('Updated');// 此时,可以执行依赖于更新后DOM的操作});// 在组件卸载之前调用onBeforeUnmount(() => {console.log('Before unmount');// 此时,组件即将被销毁,可以进行清理工作,如移除事件监听器、取消定时器等});// 在组件卸载完成后调用onUnmounted(() => {console.log('Unmounted');// 此时,组件已经被销毁,可以进行一些后续操作});// 更新message的方法const updateMessage = () => {message.value += '!';};return {message,updateMessage,};},
};
</script><style scoped>
h1 {color: blue;
}
</style>
  1. onBeforeMount:在组件挂载之前调用。此时,组件的模板已经渲染为HTML,但还没有添加到页面中。
  2. onMounted:在组件挂载完成后调用。此时,可以执行依赖于DOM的操作,例如获取元素的尺寸或启动动画。也可以进行DOM操作、数据请求等。
  3. onBeforeUpdate:在组件更新之前调用。此时,内存中的数据已经被更新,但视图中的数据还没有更新。
  4. onUpdated:在组件更新完成后调用。此时,可以执行依赖于更新后DOM的操作。
  5. onBeforeUnmount:在组件卸载之前调用。此时,组件即将被销毁,可以进行清理工作,如移除事件监听器、取消定时器等。
  6. onUnmounted:在组件卸载完成后调用。此时,组件已经被销毁,可以进行一些后续操作。

使用场景:

  • 挂载前后:进行DOM操作、数据请求等。
  • 更新前后:响应数据变化,执行依赖于DOM的操作。
  • 卸载前后:清理资源,如移除事件监听器、取消定时器等。

4 组件的复用与组合

4.1 Mixin

在Vue 3中,混入(Mixins)是一种将一组组件选项合并到目标组件中的技术。通过混入,开发者可以在多个组件之间共享重复的逻辑、方法和数据,从而提高代码的可复用性和可维护性。以下是对Vue 3组件使用Mixins的详细讲解:

4.1.1 Mixins的定义

Mixins是一个普通的JavaScript对象,它可以包含任何组件选项,如data、methods、computed、watch、生命周期钩子等。当组件使用Mixins时,所有Mixins的选项都会被“混入”到该组件自身的选项中。

4.1.2 Mixins的使用

  1. 定义Mixins

首先,需要定义一个Mixins对象。例如,创建一个名为myMixin的Mixins对象,它包含一个响应式数据mixinData、一个生命周期钩子created和一个方法mixinMethod

// mixins.js
export default {data() {return {mixinData: 'This is data from mixin'};},created() {console.log('Mixin created hook called');},methods: {mixinMethod() {console.log('This is a method from mixin');}}
};
  1. 在组件中使用Mixins

接下来,在Vue 3组件中通过mixins选项引入并使用这个Mixins对象。例如,创建一个名为MyComponent的组件,并在其中引入myMixin

<template><div><p>{{ mixinData }}</p><button @click="mixinMethod">Call Mixin Method</button></div>
</template><script>
import myMixin from './mixins.js';export default {mixins: [myMixin],data() {return {componentData: 'This is data from the component'};},mounted() {console.log('Component mounted hook called');}
};
</script>

在这个例子中,MyComponent组件继承了myMixinmixinData数据、created生命周期钩子和mixinMethod方法。当MyComponent组件被创建时,会调用myMixincreated钩子函数,并在控制台中输出"Mixin created hook called"。同时,可以在组件模板中访问mixinData数据,并调用mixinMethod方法。

4.1.3 Mixins的合并策略

当组件和Mixins具有相同的选项时,Vue 3会按照以下策略进行合并:

  1. 数据(data):如果组件和Mixins都有data函数,那么它们返回的对象会被合并到一个新的对象中。如果键名冲突,则组件的data会覆盖Mixins的data
  2. 方法(methods):如果组件和Mixins都有相同的方法名,那么组件的方法会覆盖Mixins的方法。
  3. 生命周期钩子:如果组件和Mixins都有相同的生命周期钩子,那么这些钩子函数会被依次调用。Mixins的钩子会在组件的钩子之前调用。
  4. 计算属性(computed)侦听器(watch):这些选项也会被合并,如果键名冲突,则组件的选项会覆盖Mixins的选项。

4.1.4 Mixins的注意事项

  1. 命名冲突:由于Mixins的选项会被合并到组件中,因此如果组件和Mixins具有相同的选项名,可能会导致命名冲突。为了避免这种情况,可以使用命名空间来区分Mixins中的选项。
  2. 可维护性:随着项目的增长,使用过多的Mixins可能会导致代码难以维护。因此,在大型项目中,建议优先考虑使用Vue 3的组合式API(Composition API)来实现代码复用和逻辑组织。
  3. 调试困难:由于Mixins的选项会被合并到组件中,因此在调试时可能会难以追踪某个选项的来源。为了解决这个问题,可以在Mixins中使用注释或文档来记录每个选项的用途和来源。

4.1.5 Mixins与Composition API的比较

虽然Mixins在Vue 3中仍然有效,但Vue 3官方更推荐使用组合式API(Composition API)来实现代码复用和逻辑组织。组合式API提供了一种更加灵活和模块化的方式来编写组件逻辑,可以更容易地实现逻辑关注点分离和代码复用。此外,组合式API还提供了更好的TypeScript支持和更好的性能优化机会。

综上所述,Vue 3中的Mixins是一种强大的代码复用机制,但需要注意命名冲突、可维护性和调试困难等问题。在大型项目中,建议优先考虑使用组合式API来实现代码复用和逻辑组织。

4.2 Composition API

4.2.1 Composition API 概述

Composition API 是 Vue 3 提供的一组新的 API,旨在简化组件的逻辑复用与组织,使得组件变得更易于理解和维护。与 Vue 2 中的 Options API(如 data、methods、computed 等选项)不同,Composition API 提供了一种更加灵活和可组合的方式来编写组件逻辑。

4.2.2 Composition API 的核心部分

  1. setup 函数

    • setup 函数是 Composition API 的入口函数,它在组件创建之前被调用,用于初始化响应式数据、计算属性、侦听器等。
    • setup 函数接收两个参数:propscontextprops 是父组件传递给子组件的属性,而 context 是一个包含 attrsslotsemitexpose 的对象。
    • setup 函数返回一个对象,该对象中的属性、方法可以在模板中直接使用。
  2. 响应式数据

    • reactive 函数用于创建响应式对象,该对象的属性值会自动更新并反映其依赖项的变化。
    • ref 函数用于创建响应式引用,可以通过 .value 属性来访问或修改它的值。ref 更适合包装简单类型的值(如数字、字符串、布尔值等)。
  3. 计算属性

    • computed 函数用于创建计算属性,它基于其依赖项进行缓存,只有在其依赖项发生变化时才会重新计算。这可以避免不必要的计算和性能开销。
  4. 侦听器

    • watchwatchEffect 函数用于侦听响应式数据的变化,并在数据变化时执行相应的回调函数。
    • watch 需要显式指定侦听的数据源和回调函数,支持立即执行、深度侦听等选项。
    • watchEffect 自动侦听回调函数中使用的响应式数据,无需显式指定数据源。当回调函数中使用的数据变化时,watchEffect 会自动重新执行回调函数。

4.2.3 Composition API 的使用示例

以下是一个综合示例,展示了如何在 Vue 3 组件中使用 Composition API,包括 setup 函数、响应式数据(reactiveref)、计算属性(computed)以及侦听器(watchwatchEffect)。

<template><div><h1>{{ title }}</h1><p>Count: {{ count }}</p><p>Double Count: {{ doubleCount }}</p><button @click="increment">Increment</button><button @click="resetCount">Reset Count</button></div>
</template><script>
import { ref, reactive, computed, watch, watchEffect } from 'vue';export default {name: 'CompositionApiExample',setup(props, context) {// 响应式数据const count = ref(0);const state = reactive({title: 'Composition API Example',isButtonClicked: false});// 计算属性const doubleCount = computed(() => count.value * 2);// 方法const increment = () => {count.value++;};const resetCount = () => {count.value = 0;state.isButtonClicked = false; // 也可以同时重置其他响应式数据};// 侦听器 - 使用 watchwatch(count, (newValue, oldValue) => {console.log(`Count changed from ${oldValue} to ${newValue}`);if (newValue === 5) {state.isButtonClicked = true; // 当 count 达到 5 时,改变另一个响应式数据的状态}});// 侦听器 - 使用 watchEffectwatchEffect(() => {console.log(`Double Count is ${doubleCount.value}`);// 注意:这里不需要显式指定 doubleCount 作为侦听源,因为 watchEffect 会自动侦听其依赖的响应式数据});// 暴露给模板的属性和方法return {count,state,doubleCount,increment,resetCount};}
};
</script><style scoped>
/* 样式可以根据需要自定义 */
button {margin-right: 10px;
}
</style>

在这个示例中:

  • setup 函数接收 propscontext 作为参数,但在这个例子中并没有使用 propscontext 的内容。
  • 使用 ref 创建了一个响应式引用 count,用于存储一个数字。
  • 使用 reactive 创建了一个响应式对象 state,包含 titleisButtonClicked 两个属性。
  • 使用 computed 创建了一个计算属性 doubleCount,它基于 count 的值计算得到。
  • 定义了 incrementresetCount 两个方法,用于修改 count 的值和重置组件的状态。
  • 使用 watch 侦听 count 的变化,并在控制台输出新旧值。当 count 达到 5 时,改变 state.isButtonClicked 的值。
  • 使用 watchEffect 自动侦听 doubleCount 的依赖(即 count),并在控制台输出 doubleCount 的值。注意,watchEffect 不需要显式指定侦听源。

最后,将 countstatedoubleCountincrementresetCount 暴露给模板,以便在模板中使用这些属性和方法。

4.2.4 Composition API 的优势

  1. 更好的代码组织:随着组件功能的增加,Options API 可能会导致代码难以维护和理解。而 Composition API 通过将逻辑拆分为多个可复用的函数,提高了代码的可读性和可维护性。
  2. 逻辑复用:在 Options API 中,复用逻辑通常需要通过 mixins 或高阶组件实现,但这些方式可能导致命名冲突和关系不清晰。而 Composition API 中的函数可以像普通 JavaScript 函数一样被复用,无需担心命名冲突。
  3. 更好的 TypeScript 支持:Vue 3 与 TypeScript 的集成更加紧密,而 Composition API 的函数式编程风格更适合 TypeScript 的类型推断和静态检查。

4.2.5 注意事项

  1. 避免全局状态管理:Composition API 鼓励将状态局部化,避免全局状态管理。这有助于减少组件之间的耦合度,提高代码的可维护性。
  2. 合理使用 Ref 和响应式对象:根据需要选择使用 ref 还是响应式对象来存储数据。对于简单类型的值(如数字、字符串、布尔值等),可以使用 ref;对于对象或数组等复杂类型的数据,可以使用 reactive
  3. 性能优化:在使用 Composition API 时,要注意性能优化。例如,避免不必要的计算和重复操作,使用计算属性来缓存计算结果等。

5 动态组件与异步组件

5.1 动态组件

5.1.1 动态组件的基本概念

动态组件指的是在同一个页面中使用多个组件,并且可以根据某些条件(如用户点击、数据变化等)动态地切换这些组件的显示。这种机制使得开发者能够创建更加灵活和交互性强的用户界面。

5.1.2 实现动态组件的方式

Vue 3 提供了多种方式来实现动态组件的切换,主要包括使用 <component> 标签的 is 属性和使用 v-if/v-show 指令。

  1. 使用 <component> 标签的 is 属性

    <component> 标签是 Vue 中的一个特殊标签,它可以根据 is 属性的值动态地渲染不同的组件。is 属性可以是一个字符串,表示要渲染的组件的名称,也可以是一个计算属性或方法,返回要渲染的组件的引用。

    示例代码:

    <template><div><button @click="currentComponent = 'ComponentA'">Show Component A</button><button @click="currentComponent = 'ComponentB'">Show Component B</button><component :is="currentComponent"></component></div>
    </template><script>
    import ComponentA from './ComponentA.vue';
    import ComponentB from './ComponentB.vue';export default {data() {return {currentComponent: 'ComponentA'};},components: {ComponentA,ComponentB}
    };
    </script>
    

    在这个例子中,点击按钮会改变 currentComponent 的值,从而动态地切换 <component> 标签渲染的组件。

  2. 使用 v-if/v-show 指令

    除了使用 <component> 标签外,还可以使用 v-if/v-show 指令来实现动态组件的切换。v-if 指令会根据表达式的真假值来条件性地渲染元素,而 v-show 指令则只是简单地切换元素的 CSS 属性(如 display)来显示或隐藏元素。

    示例代码:

    <template><div><button @click="showComponentA = true; showComponentB = false">Show Component A</button><button @click="showComponentA = false; showComponentB = true">Show Component B</button><ComponentA v-if="showComponentA" /><ComponentB v-if="showComponentB" /></div>
    </template><script>
    import ComponentA from './ComponentA.vue';
    import ComponentB from './ComponentB.vue';export default {data() {return {showComponentA: true,showComponentB: false};},components: {ComponentA,ComponentB}
    };
    </script>
    

    在这个例子中,使用 v-if 指令来根据 showComponentAshowComponentB 的值动态地渲染 ComponentAComponentB

5.1.3 动态组件的高级用法

  1. 使用 keep-alive 包裹动态组件

    <keep-alive> 是 Vue 中的一个内置组件,它可以缓存不活动的组件实例,而不是销毁它们。这对于需要频繁切换且不希望重新渲染的组件来说非常有用。

    示例代码:

    <template><div><button @click="currentComponent = 'ComponentA'">Show Component A</button><button @click="currentComponent = 'ComponentB'">Show Component B</button><keep-alive><component :is="currentComponent"></component></keep-alive></div>
    </template>
    

    在这个例子中,<keep-alive> 会缓存 currentComponent 所指向的组件实例,当再次切换回该组件时,它会保持之前的状态,而不会重新渲染。

  2. 动态创建和销毁组件实例

    在某些情况下,可能需要动态地创建和销毁组件实例,而不是仅仅在模板中切换它们的显示状态。这可以通过 Vue 的 createApp 方法来实现,它允许在运行时创建一个新的 Vue 应用实例,并将其挂载到 DOM 上。然后,可以使用 unmount 方法来销毁该实例。

    示例代码(动态 Toast 组件):

    // Toast.vue
    <template><div class="toast">{{ message }}</div>
    </template><script>
    export default {props: {message: {type: String,required: true}}
    };
    </script>// 在需要显示 Toast 的地方调用 createToast 函数
    import { createApp } from 'vue';
    import Toast from './Toast.vue';function createToast(message) {const app = createApp(Toast, { message });const mountNode = document.createElement('div');document.body.appendChild(mountNode);const instance = app.mount(mountNode);// 可以在这里设置定时器来自动销毁 Toast 组件setTimeout(() => {instance.unmount();document.body.removeChild(mountNode);}, 3000); // 例如,3秒后自动销毁
    }// 使用示例
    createToast('这是一个动态创建的 Toast 消息');
    

5.2 异步组件

5.2.1 异步组件的基本概念

异步组件是指在需要时才被加载和渲染的组件。这意味着,当应用启动时,这些组件不会被立即加载,而是在真正需要显示它们时,才会动态地从服务器或文件系统中加载。这种机制有助于减少初始包的大小,降低应用的初始加载时间,提高加载速度和应用的性能。

5.2.2 定义异步组件的方式

在 Vue 3 中,可以使用 defineAsyncComponent 函数来定义异步组件。这个函数是 Vue 官方提供的,它简化了异步组件的使用过程,并提供了丰富的配置选项。

  1. 基本用法

    使用 defineAsyncComponent 函数时,需要传入一个返回 Promise 的函数。这个 Promise 在解析时应该返回一个组件配置对象(通常是一个组件的选项对象或一个组件定义函数)。这个函数通常使用动态导入 import() 来实现。

    示例代码:

    <template><div><AsyncComponent /></div>
    </template><script>
    import { defineAsyncComponent } from 'vue';const AsyncComponent = defineAsyncComponent(() => import('./AsyncComponent.vue'));export default {components: {AsyncComponent}
    };
    </script>
    

    在这个例子中,当 AsyncComponent 被渲染时,Vue 会异步地加载 ./AsyncComponent.vue 这个模块。

  2. 配置选项

    defineAsyncComponent 函数还可以接受一个可选的配置对象,该对象可以包含以下属性:

    • loadingComponent:当异步组件正在加载时显示的组件。默认情况下,如果没有提供,Vue 会显示一个默认的加载指示器。
    • errorComponent:当异步组件加载失败时显示的组件。默认情况下,如果没有提供,Vue 会显示一个错误信息。
    • delay:一个数字,表示在显示加载组件之前等待的时间(以毫秒为单位)。默认为 0,即立即显示加载组件。
    • timeout:一个数字,表示异步组件加载的超时时间(以毫秒为单位)。如果超时,将触发错误处理。默认为 Infinity,即没有超时限制。
    • onError:一个函数,当异步组件加载失败时调用。这个函数接收错误对象作为参数。

    示例代码:

    <template><div><AsyncComponent /></div>
    </template><script>
    import { defineAsyncComponent, ref } from 'vue';
    import LoadingComponent from './LoadingComponent.vue';
    import ErrorComponent from './ErrorComponent.vue';const AsyncComponent = defineAsyncComponent({loader: () => import('./AsyncComponent.vue'),loadingComponent: LoadingComponent,errorComponent: ErrorComponent,delay: 200, // 延迟 200 毫秒加载timeout: 3000 // 3 秒超时
    });export default {components: {AsyncComponent}
    };
    </script>
    

    在这个例子中,当异步组件正在加载时,会显示 LoadingComponent;如果加载失败,则会显示 ErrorComponent;同时设置了 200 毫秒的延迟加载和 3 秒的超时时间。

5.2.3 异步组件与 Suspense 的配合使用

Suspense 是 Vue 3 中的一个内置组件,用于在组件树中协调对异步依赖的处理。它允许开发者在组件树上层等待下层的多个嵌套异步依赖项解析完成,并可以在等待时渲染一个加载状态。

  1. Suspense 的基本用法

    Suspense 组件有两个插槽:#default#fallback#default 插槽用于显示要加载的组件,而 #fallback 插槽则用于在异步组件加载时显示的内容(如加载指示器)。

    示例代码:

    <template><div><Suspense><template #default><AsyncComponent /></template><template #fallback><div>Loading...</div></template></Suspense></div>
    </template><script>
    import { defineAsyncComponent } from 'vue';const AsyncComponent = defineAsyncComponent(() => import('./AsyncComponent.vue'));export default {components: {AsyncComponent}
    };
    </script>
    

    在这个例子中,当 AsyncComponent 正在加载时,会显示 #fallback 插槽中的 “Loading…” 内容。

  2. 异步组件的 suspensible 特性

    异步组件默认是 “suspensible” 的,这意味着如果组件关系链上有一个 Suspense,那么这个异步组件就会被当作这个 Suspense 的一个异步依赖。在这种情况下,加载状态是由 Suspense 控制的,而该组件自己的加载、报错、延时和超时等选项都将被忽略。

    如果希望异步组件不使用 Suspense 控制其加载状态,可以在定义异步组件时指定 suspensible: false

5.2.4 动态加载异步组件

除了静态地定义异步组件外,Vue 3 还支持根据条件动态地加载不同的异步组件。这可以通过在 loader 函数中返回不同的 Promise 来实现。

示例代码:

<template><div><button @click="loadComponent('ComponentA')">Load Component A</button><button @click="loadComponent('ComponentB')">Load Component B</button><component :is="dynamicComponent" /></div>
</template><script>
import { ref, defineAsyncComponent } from 'vue';const componentName = ref('ComponentA');
let dynamicComponent = null;const loadComponent = async (name) => {dynamicComponent = await defineAsyncComponent(() =>import(`./components/${name}.vue`));
};export default {setup() {return {dynamicComponent,loadComponent};}
};
</script>

在这个例子中,点击按钮会根据 componentName 的值动态地加载不同的异步组件,并将其赋值给 dynamicComponent,然后在模板中使用 <component :is="dynamicComponent" /> 来渲染该组件。

相关文章:

Vue 3 的组件式开发(2)

1 Vue 3 组件的插槽 插槽&#xff08;Slot&#xff09;是Vue组件中的一个重要概念&#xff0c;它允许父组件向子组件中插入HTML结构或其他组件&#xff0c;从而实现内容的自定义和复用。以下是对Vue 3组件插槽的详细讲解&#xff1a; 1.1 插槽的基本概念 插槽可以被视为子组…...

python 爬虫 入门 四、线程,进程,协程

目录 一、进程 特征&#xff1a; 使用&#xff1a; 初始代码 进程改装代码 二、线程 特征&#xff1a; 使用&#xff1a; 三、协程 后续&#xff1a;五、抓取图片、视频 线程和进程大部分人估计都知道&#xff0c;但协程就不一定了。 一、进程 进程是操作系统分配资…...

cloak斗篷伪装下的独立站

随着互联网的不断进步&#xff0c;越来越多的跨境电商卖家开始认识到独立站的重要性&#xff0c;并纷纷建立自己的独立站点。对于那些有志于进入这一领域的卖家来说&#xff0c;独立站是什么呢&#xff1f;独立站是指个人或小型团队自行搭建和运营的网站。 独立站能够帮助跨境…...

【Nas】X-DOC:在Mac OS X 中使用 WOL 命令唤醒局域网内 PVE 主机

【Nas】X-DOC&#xff1a;在Mac OS X 中使用 WOL 命令唤醒局域网内 PVE 主机 1、Mac OS X 端2、PVE 端&#xff08;Debian Linux&#xff09; 1、Mac OS X 端 &#xff08;1&#xff09;安装 wakeonlan 工具 brew install wakeonlan&#xff08;2&#xff09;唤醒 PVE 命令 …...

u盘装win10系统提示“windows无法安装到这个磁盘,选中的磁盘采用GPT分区形式”解决方法

我们在u盘安装原版win10 iso镜像时&#xff0c;发现在选择硬盘时提示了“windows无法安装到这个磁盘,选中的磁盘采用GPT分区形式”&#xff0c;直接导致了无法继续安装下去。出现这种情况要怎么解决呢&#xff1f;下面小编分享u盘安装win10系统提示“windows无法安装到这个磁盘…...

Linux系统之dc计算器工具的基本使用

Linux系统之dc计算器工具的基本使用 一、DC工具介绍二、dc命令的基本用法2.1 dc命令的help帮助信息2.2 dc命令基本用法2.3 dc命令常用操作符 三、dc命令的基本使用3.1dc命令的用法步骤3.2 简单数学计算3.3 通过文件来计算3.4 使用--expression计算3.5 使用dc命令进行高精度计算…...

使用Python计算相对强弱指数(RSI)进阶

使用Python计算相对强弱指数&#xff08;RSI&#xff09;进阶 废话不多说&#xff0c;直接上主题&#xff1a;> 代码实现 以下是实现RSI计算的完整代码&#xff1a; # 创建一个DataFramedata {DATE: date_list, # 日期CLOSE: close_px_list, # 收盘价格 }df pd.DataF…...

vue 解决:npm ERR! code ERESOLVE 及 npm ERR! ERESOLVE could not resolve 的方案

1、问题描述&#xff1a; 其一、需求为&#xff1a; 想要安装项目所需依赖&#xff0c;成功运行 vue 项目&#xff0c;想要在浏览器中能成功访问项目地址 其二、问题描述为&#xff1a; 在 package.json 文件打开终端平台&#xff0c;通过执行 npm install 命令&#xff0c…...

Android 原生开发与Harmony原生开发浅析

Android系统 基于Linux ,架构如下 底层 (Linux )> Native ( C层) > FrameWork层 (SystemService) > 系统应用 (闹钟/日历等) 从Android发版1.0开始到现在15,经历了大大小小的变革 从Android6.0以下是个分水岭,6.0之前权限都是直接卸载Manifest中配置 6.0开始 则分普…...

VIVO售后真好:屏幕绿线,4年免费换屏

只要亮屏就有。这也太影响使用了。 本来想换趁机换手机&#xff0c;看了VIVO发布的X200&#xff0c;决定等明年的X200 ULTRA。手头这个就准备修。 查了一下价格&#xff0c;换屏1600&#xff0c;优惠1100。咸鱼上X70 PRO也就800。能不能简单维修就解决呢&#xff1f;于是联系…...

数据类型【MySQL】

文章目录 建立表查看表删除表数据类型floatcharvarcharchar&&varchar 时间日期类型enum和setenum和set查找 建立表 mysql> create table if not exists user1(-> id int ,-> name varchar (20) comment 用户名 ,-> password char (32) comment 用户名的…...

流媒体协议.之(RTP,RTCP,RTSP,RTMP,HTTP)(二)

继续上篇介绍&#xff0c;本篇介绍一下封装RTP的数据格式&#xff0c;如何将摄像头采集的码流&#xff0c;音频的码流&#xff0c;封装到rtp里&#xff0c;传输。 有自己私有协议例子&#xff0c;有rtp协议&#xff0c;参考代码。注意不是rtsp协议。 一、私有协议 玩过tcp协议…...

在 Kakarot ZkEVM 上使用 Starknet Scaffold 构建应用

Starknet 和 EVM 我们所知的智能合约世界一直围绕着以太坊虚拟机&#xff08;EVM&#xff09;&#xff0c;其主要语言是 Solidity。 尽管 Starknet 通过 STARKs 为以太坊开辟了新的可能性&#xff0c;但其缺点是它有一个不同的虚拟机 (CairoVM)&#xff0c;这要求开发者学习 …...

DBeave如何连接达梦数据库,设置达梦驱动,真酷

前言 我们在使用DBeaver连接数据库时&#xff0c;默认可以连接常用的数据库&#xff0c;如mysql数据库&#xff0c;postgresql数据库&#xff0c;oracle数据库。但是&#xff0c;我们的国产数据库达梦数据库&#xff0c;默认在IDEA里面没有驱动&#xff0c;所以还得配置一下才…...

2024年全球 MoonBit 编程创新赛-零基础早鸟教程-使用wasm4八小时开发井子棋小游戏

前言 本篇文章主要分享 “2024年全球 MoonBit 编程创新赛 游戏赛道”参赛过程中九宫棋游戏的开发技巧和心得。以此抛砖引玉。首先介绍下 MoonBit。 月兔语言 MoonBit 是一个用于云计算和边缘计算的 WebAssembly 端到端的编程语言工具链。 您可以访问 https://try.moonbitlang.…...

机器学习4

第3章 线性模型 3.1 线性模型的基本形式 3.1.1 线性模型的核心公式 线性模型通过属性的线性组合进行预测&#xff0c;其核心公式为&#xff1a; [ f(x) \omega_1 X_1 \omega_2 X_2 … \omega_d X_d b ] 其中&#xff1a; ω 1 , ω 2 , . . . , ω d \omega_1, \omega_…...

Python数值计算(33)——simpson 3/8积分公式

1. 背景知识 既然前的Simpson可以通过使用三个点构造二次曲线近似积分&#xff0c;那么&#xff0c;如果点数增加到了4个&#xff0c;然后不就可以构造三次多项式的曲线&#xff0c;实现对目标值的积分吗&#xff1f; 如果采用和上一节介绍的同样的方法&#xff0c;我们可以推…...

<项目代码>YOLOv8路面垃圾识别<目标检测>

YOLOv8是一种单阶段&#xff08;one-stage&#xff09;检测算法&#xff0c;它将目标检测问题转化为一个回归问题&#xff0c;能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法&#xff08;如Faster R-CNN&#xff09;&#xff0c;YOLOv8具有更高的…...

Java中的注解(白金版)

Spring中常用注解 Springboot中@Validated注解的使用 Swagger中常用注解 @Validate...

actor模型

Actor模型&#xff08;Actor Model&#xff09;是一种用于并发计算的数学模型和编程概念&#xff0c;它最早由计算机科学家 Carl Hewitt 等人提出&#xff0c;用于简化对多线程或并发系统的设计和实现。Actor模型在并发编程、分布式系统、消息传递系统等领域具有广泛应用。 核…...

合约门合同全生命周期管理系统:企业智能合同管理的新时代

合约门合同全生命周期管理系统&#xff1a;企业智能合同管理的新时代 1. 引言 随着现代企业的快速发展&#xff0c;合同管理的复杂性日益增加。无论是采购合同、销售合同还是合作协议&#xff0c;合同管理已成为企业运营中至关重要的一环。传统的手工合同管理方式往往效率低下…...

vscode如何debug环境配置?torchrun与deepspeed库又该如何配置?

文章目录 前言一、vscode命令参数传递1、验证参数py文件2、第一种vscode调用方法(launch.json配置)3、第二种vscode调用方法(launch.json配置)二、deepspeed运行py文件代码(deepspeed_test.py)三、deepspeed命令调用(无法debug)四、deepspeed使用vscode进行调试(能debug)五、vs…...

Qt元对象系统 —— 信号与槽

信号与槽讨论的是Qt对象之间的连接与交互。我们就是使用这种方式实现了一个简单的异步调用。换而言之&#xff0c;信号与槽让我们可以不必考虑复杂的调用。只需要当我们需要在程序中表达&#xff1a;“希望在程序中通知一个事件而且按照我们设定的方式给出回应”的时候&#xf…...

单细胞配色效果模拟器 | 简陋版(已有颜色数组)

目的&#xff1a;假设你有一组颜色了&#xff0c;怎么模拟查看它们在单细胞DimPlot中的美学效果呢&#xff1f;要足够快&#xff0c;还要尽可能有模拟效果。 1. 尝试1: 随机矩阵&#xff0c;真的UMAP降维后绘图&#xff08;失败&#xff09; 造一个随机矩阵&#xff0c;使用S…...

面向对象编程中类与类之间的关系(一)

目录 1.引言 2."有一个"关系 3."是一个"关系(继承) 4.“有一个”与“是一个”的区别 5.not-a关系 6.层次结构 7.多重继承 8.混入类 1.引言 作为程序员&#xff0c;必然会遇到这样的情况&#xff1a;不同的类具有共同的特征&#xff0c;至少看起来彼…...

streamlit 实现 flink SQL运行界面

实现效果 streamlit flink-playground.py 文件如下&#xff1a; import streamlit as st import io import contextlib import sys import os import uuid import subprocess from jinja2 import Templatest.set_page_config(layout"wide")# 设置页面标题 st.title…...

鲸鱼优化算法(Whale Optimization Algorithm, WOA)原理与MATLAB例程

鲸鱼优化算法&#xff08;Whale Optimization Algorithm, WOA&#xff09;是一种基于鲸鱼捕食行为的智能优化算法。它模拟了座头鲸在狩猎时的“气泡网”捕食策略。 文章目录 1.适应度函数2. 更新公式2.1 突袭行为2.2 螺旋更新3.线性递减参数4. 边界处理 MATLAB 实现示例代码说明…...

MFC七段码显示实例

在MFC中添加iSenvenSegmentAnalogX控件&#xff0c;添加编辑框和按钮实现在编辑框中输入数字点击按钮后数字用七段码显示 1、在对话框中点击右键如下图添加控件和变量 2、在sevenDlg.h中添加代码 public: void ShowInd(int,double);3、在sevenDlg.cpp中添加代码 void CSe…...

【日常知识点】到底推不推荐用JWT?

👉博主介绍: 博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家,WEB架构师,阿里云专家博主,华为云云享专家,51CTO 专家博主 ⛪️ 个人社区:个人社区 💞 个人主页:个人主页 🙉 专栏地址: ✅ Java 中级 🙉八股文专题:剑指大厂,手撕 J…...

网络编程项目之FTP服务器

项目介绍 模拟FTP核心原理&#xff1a;客户端连接服务器后&#xff0c;向服务器发送一个文件。文件名可以通过参数指定&#xff0c;服务器端接收客户端传来的文件&#xff08;文件名随意&#xff09;&#xff0c;如果文件不存在自动创建文件&#xff0c;如果文件存在&#xff0…...

拉萨营销型网站建设/怎么用手机创建网站

本文章给大家介绍在php中类和对象的protected与const属性用法&#xff0c;有需要了解的朋友可参考参考。const属性用const属性定义的字段是一个常量&#xff0c;类中的常量和静态变量类似&#xff0c;不同之处就是常量的值一旦赋值不能被改变。const定义常量不需要加$符号&…...

网站做好了怎么做后台/独立站

最近在整理之前工作的文件&#xff0c;发现大概有50个小时的专家call & 会议录音啥的&#xff0c;于是就研究了一下如何批量把长语音转成格式优美的文字文档。 当然做事情之前先来知乎搜了搜有没有现成的解决方案可用&#xff0c;于是发现了这个问题&#xff0c;但一楼说的…...

企业建个网站要多少钱/2021年搜索引擎排名

fink生态 spark生态 hadoop生态 大数据技术体系与主流技术栈...

重庆疫情新闻发布会/网站seo优化分析

SQL映射 SQL映射文件 SQL映射xml文件是所有sql语句放置的地方&#xff0c;不同于JDBC连接的方法&#xff0c;需要构造方法&#xff0c;写statement和resultset语句才可以调用指定的sql语句&#xff0c;只需要把所有的sql语句写在配置文件中&#xff0c;根据不同的id&#xff0c…...

郑州品牌网站建设官网/免费技能培训在哪里报名

目录 Kafka的基本介绍Kafka的设计原理分析Kafka数据传输的事务特点Kafka消息存储格式副本&#xff08;replication&#xff09;策略Kafka消息分组&#xff0c;消息消费原理Kafak顺序写入与数据读取消费者&#xff08;读取数据&#xff09; Kafka的基本介绍 Kafka是最初由Lin…...

驻马店做网站建设的公司/整站seo排名费用价格

/* * 画线 * 输入参数&#xff1a; * x1、y1 : 起点坐标 * x2、y2 : 终点坐标 * color : 颜色值 * 对于16BPP: color的格式为0xAARRGGBB (AA 透明度), * 需要转换为5:6:5格式 * 对于8BPP: color为调色板中的索引值&#xff0c; * …...