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对象。父组件在填充插槽时接收了这个对象,并使用了它的name和age属性。
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>,以及一些其他的自定义块。
<template>:定义了组件的HTML结构。在Vue 3中,模板语法得到了进一步的优化,支持更多的指令和功能,如条件渲染、列表渲染、事件处理等。<script>:包含了组件的JavaScript逻辑。在这里,可以定义组件的数据、方法、计算属性、生命周期钩子等。Vue 3引入了组合式API(Composition API),使得逻辑复用和组件组织变得更加灵活。<style>:用于定义组件的样式。Vue 3支持scoped属性,使得样式只作用于当前组件,避免了全局样式的污染。此外,还可以使用CSS预处理器(如Sass、Less等)来编写更复杂的样式。
2.2 单文件组件的优点
- 模块化:将组件的模板、脚本和样式封装在一起,使得组件更加模块化,易于维护和复用。
- 语法熟悉:使用熟悉的HTML、CSS和JavaScript语法来编写组件,降低了学习成本。
- 预编译模板:Vue 3在构建阶段会对模板进行预编译,避免了运行时的编译开销,提高了性能。
- 组件作用域的CSS:通过
scoped属性,可以确保样式只作用于当前组件,避免了全局样式的冲突。 - 更好的IDE支持:许多现代IDE都提供了对Vue单文件组件的良好支持,包括语法高亮、自动补全、类型检查等功能。
- 热更新(HMR):在开发阶段,可以支持模块热替换(Hot Module Replacement),使得开发者可以在不刷新页面的情况下更新组件。
2.3 单文件组件的使用
- 创建组件:使用Vue CLI或Vite等构建工具可以轻松地创建Vue 3单文件组件。这些工具提供了合理的默认配置,并可以通过插件进行扩展。
- 导入组件:在父组件中,可以使用
import语句来导入子组件,并在components选项中注册它。然后,就可以在父组件的模板中使用这个子组件了。 - 自定义块:除了
<template>、<script>和<style>之外,.vue文件还可以包含其他自定义块。这些自定义块可以用于文档、国际化、类型定义等目的。自定义块的处理需要依赖工具链,如Vite插件或Webpack loader等。
2.4 单文件组件的编译
在构建过程中,Vue 3单文件组件会被编译为标准的JavaScript和CSS。具体来说:
- 模板编译:
<template>中的内容会被提取出来,传递给@vue/compiler-dom进行预编译,生成JavaScript渲染函数,并附在导出的组件上作为其render选项。 - 脚本编译:
<script>中的内容会被当作ES模块来执行。如果使用了<script setup>语法糖,它会被预处理为组件的setup()函数。 - 样式编译:
<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>
- onBeforeMount:在组件挂载之前调用。此时,组件的模板已经渲染为HTML,但还没有添加到页面中。
- onMounted:在组件挂载完成后调用。此时,可以执行依赖于DOM的操作,例如获取元素的尺寸或启动动画。也可以进行DOM操作、数据请求等。
- onBeforeUpdate:在组件更新之前调用。此时,内存中的数据已经被更新,但视图中的数据还没有更新。
- onUpdated:在组件更新完成后调用。此时,可以执行依赖于更新后DOM的操作。
- onBeforeUnmount:在组件卸载之前调用。此时,组件即将被销毁,可以进行清理工作,如移除事件监听器、取消定时器等。
- 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的使用
- 定义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');}}
};
- 在组件中使用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组件继承了myMixin的mixinData数据、created生命周期钩子和mixinMethod方法。当MyComponent组件被创建时,会调用myMixin的created钩子函数,并在控制台中输出"Mixin created hook called"。同时,可以在组件模板中访问mixinData数据,并调用mixinMethod方法。
4.1.3 Mixins的合并策略
当组件和Mixins具有相同的选项时,Vue 3会按照以下策略进行合并:
- 数据(data):如果组件和Mixins都有
data函数,那么它们返回的对象会被合并到一个新的对象中。如果键名冲突,则组件的data会覆盖Mixins的data。 - 方法(methods):如果组件和Mixins都有相同的方法名,那么组件的方法会覆盖Mixins的方法。
- 生命周期钩子:如果组件和Mixins都有相同的生命周期钩子,那么这些钩子函数会被依次调用。Mixins的钩子会在组件的钩子之前调用。
- 计算属性(computed) 和 侦听器(watch):这些选项也会被合并,如果键名冲突,则组件的选项会覆盖Mixins的选项。
4.1.4 Mixins的注意事项
- 命名冲突:由于Mixins的选项会被合并到组件中,因此如果组件和Mixins具有相同的选项名,可能会导致命名冲突。为了避免这种情况,可以使用命名空间来区分Mixins中的选项。
- 可维护性:随着项目的增长,使用过多的Mixins可能会导致代码难以维护。因此,在大型项目中,建议优先考虑使用Vue 3的组合式API(Composition API)来实现代码复用和逻辑组织。
- 调试困难:由于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 的核心部分
-
setup 函数
setup函数是 Composition API 的入口函数,它在组件创建之前被调用,用于初始化响应式数据、计算属性、侦听器等。setup函数接收两个参数:props和context。props是父组件传递给子组件的属性,而context是一个包含attrs、slots、emit和expose的对象。setup函数返回一个对象,该对象中的属性、方法可以在模板中直接使用。
-
响应式数据
reactive函数用于创建响应式对象,该对象的属性值会自动更新并反映其依赖项的变化。ref函数用于创建响应式引用,可以通过.value属性来访问或修改它的值。ref更适合包装简单类型的值(如数字、字符串、布尔值等)。
-
计算属性
computed函数用于创建计算属性,它基于其依赖项进行缓存,只有在其依赖项发生变化时才会重新计算。这可以避免不必要的计算和性能开销。
-
侦听器
watch和watchEffect函数用于侦听响应式数据的变化,并在数据变化时执行相应的回调函数。watch需要显式指定侦听的数据源和回调函数,支持立即执行、深度侦听等选项。watchEffect自动侦听回调函数中使用的响应式数据,无需显式指定数据源。当回调函数中使用的数据变化时,watchEffect会自动重新执行回调函数。
4.2.3 Composition API 的使用示例
以下是一个综合示例,展示了如何在 Vue 3 组件中使用 Composition API,包括 setup 函数、响应式数据(reactive 和 ref)、计算属性(computed)以及侦听器(watch 和 watchEffect)。
<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函数接收props和context作为参数,但在这个例子中并没有使用props和context的内容。- 使用
ref创建了一个响应式引用count,用于存储一个数字。 - 使用
reactive创建了一个响应式对象state,包含title和isButtonClicked两个属性。 - 使用
computed创建了一个计算属性doubleCount,它基于count的值计算得到。 - 定义了
increment和resetCount两个方法,用于修改count的值和重置组件的状态。 - 使用
watch侦听count的变化,并在控制台输出新旧值。当count达到 5 时,改变state.isButtonClicked的值。 - 使用
watchEffect自动侦听doubleCount的依赖(即count),并在控制台输出doubleCount的值。注意,watchEffect不需要显式指定侦听源。
最后,将 count、state、doubleCount、increment 和 resetCount 暴露给模板,以便在模板中使用这些属性和方法。
4.2.4 Composition API 的优势
- 更好的代码组织:随着组件功能的增加,Options API 可能会导致代码难以维护和理解。而 Composition API 通过将逻辑拆分为多个可复用的函数,提高了代码的可读性和可维护性。
- 逻辑复用:在 Options API 中,复用逻辑通常需要通过 mixins 或高阶组件实现,但这些方式可能导致命名冲突和关系不清晰。而 Composition API 中的函数可以像普通 JavaScript 函数一样被复用,无需担心命名冲突。
- 更好的 TypeScript 支持:Vue 3 与 TypeScript 的集成更加紧密,而 Composition API 的函数式编程风格更适合 TypeScript 的类型推断和静态检查。
4.2.5 注意事项
- 避免全局状态管理:Composition API 鼓励将状态局部化,避免全局状态管理。这有助于减少组件之间的耦合度,提高代码的可维护性。
- 合理使用 Ref 和响应式对象:根据需要选择使用
ref还是响应式对象来存储数据。对于简单类型的值(如数字、字符串、布尔值等),可以使用ref;对于对象或数组等复杂类型的数据,可以使用reactive。 - 性能优化:在使用 Composition API 时,要注意性能优化。例如,避免不必要的计算和重复操作,使用计算属性来缓存计算结果等。
5 动态组件与异步组件
5.1 动态组件
5.1.1 动态组件的基本概念
动态组件指的是在同一个页面中使用多个组件,并且可以根据某些条件(如用户点击、数据变化等)动态地切换这些组件的显示。这种机制使得开发者能够创建更加灵活和交互性强的用户界面。
5.1.2 实现动态组件的方式
Vue 3 提供了多种方式来实现动态组件的切换,主要包括使用 <component> 标签的 is 属性和使用 v-if/v-show 指令。
-
使用
<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>标签渲染的组件。 -
使用
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指令来根据showComponentA和showComponentB的值动态地渲染ComponentA和ComponentB。
5.1.3 动态组件的高级用法
-
使用
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所指向的组件实例,当再次切换回该组件时,它会保持之前的状态,而不会重新渲染。 -
动态创建和销毁组件实例
在某些情况下,可能需要动态地创建和销毁组件实例,而不是仅仅在模板中切换它们的显示状态。这可以通过 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 官方提供的,它简化了异步组件的使用过程,并提供了丰富的配置选项。
-
基本用法
使用
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这个模块。 -
配置选项
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 中的一个内置组件,用于在组件树中协调对异步依赖的处理。它允许开发者在组件树上层等待下层的多个嵌套异步依赖项解析完成,并可以在等待时渲染一个加载状态。
-
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…” 内容。 -
异步组件的 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 组件的插槽 插槽(Slot)是Vue组件中的一个重要概念,它允许父组件向子组件中插入HTML结构或其他组件,从而实现内容的自定义和复用。以下是对Vue 3组件插槽的详细讲解: 1.1 插槽的基本概念 插槽可以被视为子组…...
python 爬虫 入门 四、线程,进程,协程
目录 一、进程 特征: 使用: 初始代码 进程改装代码 二、线程 特征: 使用: 三、协程 后续:五、抓取图片、视频 线程和进程大部分人估计都知道,但协程就不一定了。 一、进程 进程是操作系统分配资…...
cloak斗篷伪装下的独立站
随着互联网的不断进步,越来越多的跨境电商卖家开始认识到独立站的重要性,并纷纷建立自己的独立站点。对于那些有志于进入这一领域的卖家来说,独立站是什么呢?独立站是指个人或小型团队自行搭建和运营的网站。 独立站能够帮助跨境…...
【Nas】X-DOC:在Mac OS X 中使用 WOL 命令唤醒局域网内 PVE 主机
【Nas】X-DOC:在Mac OS X 中使用 WOL 命令唤醒局域网内 PVE 主机 1、Mac OS X 端2、PVE 端(Debian Linux) 1、Mac OS X 端 (1)安装 wakeonlan 工具 brew install wakeonlan(2)唤醒 PVE 命令 …...
u盘装win10系统提示“windows无法安装到这个磁盘,选中的磁盘采用GPT分区形式”解决方法
我们在u盘安装原版win10 iso镜像时,发现在选择硬盘时提示了“windows无法安装到这个磁盘,选中的磁盘采用GPT分区形式”,直接导致了无法继续安装下去。出现这种情况要怎么解决呢?下面小编分享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计算相对强弱指数(RSI)进阶 废话不多说,直接上主题:> 代码实现 以下是实现RSI计算的完整代码: # 创建一个DataFramedata {DATE: date_list, # 日期CLOSE: close_px_list, # 收盘价格 }df pd.DataF…...
vue 解决:npm ERR! code ERESOLVE 及 npm ERR! ERESOLVE could not resolve 的方案
1、问题描述: 其一、需求为: 想要安装项目所需依赖,成功运行 vue 项目,想要在浏览器中能成功访问项目地址 其二、问题描述为: 在 package.json 文件打开终端平台,通过执行 npm install 命令,…...
Android 原生开发与Harmony原生开发浅析
Android系统 基于Linux ,架构如下 底层 (Linux )> Native ( C层) > FrameWork层 (SystemService) > 系统应用 (闹钟/日历等) 从Android发版1.0开始到现在15,经历了大大小小的变革 从Android6.0以下是个分水岭,6.0之前权限都是直接卸载Manifest中配置 6.0开始 则分普…...
VIVO售后真好:屏幕绿线,4年免费换屏
只要亮屏就有。这也太影响使用了。 本来想换趁机换手机,看了VIVO发布的X200,决定等明年的X200 ULTRA。手头这个就准备修。 查了一下价格,换屏1600,优惠1100。咸鱼上X70 PRO也就800。能不能简单维修就解决呢?于是联系…...
数据类型【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)(二)
继续上篇介绍,本篇介绍一下封装RTP的数据格式,如何将摄像头采集的码流,音频的码流,封装到rtp里,传输。 有自己私有协议例子,有rtp协议,参考代码。注意不是rtsp协议。 一、私有协议 玩过tcp协议…...
在 Kakarot ZkEVM 上使用 Starknet Scaffold 构建应用
Starknet 和 EVM 我们所知的智能合约世界一直围绕着以太坊虚拟机(EVM),其主要语言是 Solidity。 尽管 Starknet 通过 STARKs 为以太坊开辟了新的可能性,但其缺点是它有一个不同的虚拟机 (CairoVM),这要求开发者学习 …...
DBeave如何连接达梦数据库,设置达梦驱动,真酷
前言 我们在使用DBeaver连接数据库时,默认可以连接常用的数据库,如mysql数据库,postgresql数据库,oracle数据库。但是,我们的国产数据库达梦数据库,默认在IDEA里面没有驱动,所以还得配置一下才…...
2024年全球 MoonBit 编程创新赛-零基础早鸟教程-使用wasm4八小时开发井子棋小游戏
前言 本篇文章主要分享 “2024年全球 MoonBit 编程创新赛 游戏赛道”参赛过程中九宫棋游戏的开发技巧和心得。以此抛砖引玉。首先介绍下 MoonBit。 月兔语言 MoonBit 是一个用于云计算和边缘计算的 WebAssembly 端到端的编程语言工具链。 您可以访问 https://try.moonbitlang.…...
机器学习4
第3章 线性模型 3.1 线性模型的基本形式 3.1.1 线性模型的核心公式 线性模型通过属性的线性组合进行预测,其核心公式为: [ f(x) \omega_1 X_1 \omega_2 X_2 … \omega_d X_d b ] 其中: ω 1 , ω 2 , . . . , ω d \omega_1, \omega_…...
Python数值计算(33)——simpson 3/8积分公式
1. 背景知识 既然前的Simpson可以通过使用三个点构造二次曲线近似积分,那么,如果点数增加到了4个,然后不就可以构造三次多项式的曲线,实现对目标值的积分吗? 如果采用和上一节介绍的同样的方法,我们可以推…...
<项目代码>YOLOv8路面垃圾识别<目标检测>
YOLOv8是一种单阶段(one-stage)检测算法,它将目标检测问题转化为一个回归问题,能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法(如Faster R-CNN),YOLOv8具有更高的…...
Java中的注解(白金版)
Spring中常用注解 Springboot中@Validated注解的使用 Swagger中常用注解 @Validate...
actor模型
Actor模型(Actor Model)是一种用于并发计算的数学模型和编程概念,它最早由计算机科学家 Carl Hewitt 等人提出,用于简化对多线程或并发系统的设计和实现。Actor模型在并发编程、分布式系统、消息传递系统等领域具有广泛应用。 核…...
手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...
无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...
嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...
Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案
在大数据时代,海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构,在处理大规模数据抓取任务时展现出强大的能力。然而,随着业务规模的不断扩大和数据抓取需求的日益复杂,传统…...
