【三十天精通Vue 3】第十二天 Vue 3 的函数式组件详解(过滤器已废弃)

news/2025/1/11 23:50:07/

请添加图片描述

✅创作者:陈书予
🎉个人主页:陈书予的个人主页
🍁陈书予的个人社区,欢迎你的加入: 陈书予的社区
🌟专栏地址: 三十天精通 Vue 3

文章目录

    • 引言
    • 一、Vue3 中的函数式组件
      • 1.1 函数式组件的概念和特点
      • 1.2 函数式组件和普通组件的区别
      • 1.3 如何定义和使用函数式组件
    • 二、Vue3 函数式组件的应用场景
      • 2.1 列表渲染
      • 2.2 条件渲染
      • 2.3 表单控件
      • 2.4 路由导航
    • 三、Vue3 函数式组件的高级应用
      • 3.1 Scoped Slots
      • 3.2 组件缓存
      • 3.3 嵌套组件
      • 3.4 Vue3 函数式组件的高级应用 动态组件
    • 四、Vue3 函数式组件的注意事项和常见问题
      • 4.1 子组件如何更新父组件的状态
      • 4.2 函数式组件中如何使用this
      • 4.3 函数式组件中的v-model

引言

函数式组件是 Vue3 中的一个重要概念,它是一种轻量级的组件形式,具有高效、简洁和可复用等优点。在本文中,我们将详细介绍 Vue3 函数式组件的概念、应用场景、高级应用以及注意事项和常见问题。我们还会给出相应的代码示例,帮助读者更好地理解和掌握函数式组件的使用方法。

一、Vue3 中的函数式组件

image-20230421170359640.png

1.1 函数式组件的概念和特点

函数式组件是指没有状态(没有响应式数据)和实例(没有 this 上下文)的组件,它只接受 props 作为输入,并返回渲染结果。函数式组件的定义形式如下:

const MyFunctionalComponent = (props, context) => {// 函数式组件的渲染逻辑
}

函数式组件具有以下特点:

  • 函数式组件是纯函数,它不依赖于组件实例的状态,也不会影响组件外部的状态。
  • 函数式组件的渲染结果只由输入的 props 决定,因此具有高效和可预测的特点。
  • 函数式组件的代码量少,结构简单,易于维护和测试。

1.2 函数式组件和普通组件的区别

在 Vue3 中,函数式组件和普通组件有以下区别:

  • 普通组件通过 new Vue() 实例化,而函数式组件通过函数调用实现。
  • 普通组件具有响应式数据和实例上下文,而函数式组件没有。
  • 普通组件具有生命周期钩子函数和状态管理能力,而函数式组件没有。
  • 普通组件支持自定义指令、计算属性和事件处理函数,而函数式组件不支持。

1.3 如何定义和使用函数式组件

定义函数式组件非常简单,只需要将组件的 template 部分替换为一个返回 VNode 的函数即可。例如:

// 函数式组件的定义
const FunctionalComponent = (props, context) => {return h('div', `Hello, ${props.name}!`);
};// 函数式组件的使用
<template><FunctionalComponent name="Vue3" />
</template>

上述代码中,我们定义了一个名为 FunctionalComponent 的函数式组件,并将其用作了模板中的子组件。这个组件接收一个名为 name 的 prop,并返回一个包含 Hello, ${props.name}! 内容的 div 元素。使用函数式组件与使用普通组件的方式是一样的,只需要将组件的名称放在模板中即可。

二、Vue3 函数式组件的应用场景

image-20230421170501301.png

2.1 列表渲染

在 Vue 3 中,我们通常使用 v-for 指令来渲染列表。而对于简单的列表项,我们可以使用函数式组件来提高性能。函数式组件相比普通组件的优势在于不需要维护状态,仅仅是一个纯展示组件。因此,对于仅包含静态内容的列表项,我们可以使用函数式组件来提高渲染性能。

下面是一个使用函数式组件渲染简单列表的示例代码:

<template><ul><li v-for="item in items" :key="item.id"><ListItem :item="item" /></li></ul>
</template><script>
import { defineComponent } from 'vue'
import ListItem from './ListItem.vue'export default defineComponent({components: {ListItem},props: {items: {type: Array,required: true}}
})
</script>

在上述代码中,我们通过 v-for 指令渲染列表,并将每个列表项包装在一个 ListItem 函数式组件中。ListItem 函数式组件不需要维护状态,只需要展示传入的 item 数据即可。

下面是 ListItem 函数式组件的示例代码:

<template functional><div><h2>{{ props.item.title }}</h2><p>{{ props.item.content }}</p></div>
</template>

在上述代码中,我们通过 functional 标识符声明了一个函数式组件。函数式组件的模板只包含一个函数参数 props,通过 props.item 访问传入的列表项数据。

2.2 条件渲染

在 Vue3 中,条件渲染可以通过 v-ifv-show 指令来实现。对于简单的条件渲染,使用普通组件即可满足需求。但对于条件渲染嵌套过深,或需要频繁切换的场景,使用函数式组件可以有效提升性能。

下面是一个使用函数式组件实现条件渲染的示例:

<template><div><button @click="toggleShow">Toggle Show</button><functional-comp :is="show ? 'comp-a' : 'comp-b'" /></div>
</template><script>
import CompA from './CompA.vue'
import CompB from './CompB.vue'const FunctionalComp = {functional: true,render(h, { props }) {return h(props.is)}
}export default {components: {CompA,CompB,FunctionalComp},data() {return {show: true}},methods: {toggleShow() {this.show = !this.show}}
}
</script>

在上述代码中,我们定义了一个函数式组件 FunctionalComp,它根据传入的 is 属性渲染不同的组件。通过点击按钮,我们可以动态切换 FunctionalComp 中渲染的组件。相比于使用普通组件,使用函数式组件可以减少组件的创建和销毁次数,提升渲染性能。

2.3 表单控件

在表单控件中,使用函数式组件可以避免因组件状态变化而导致的重新渲染,提升性能。

下面是一个使用函数式组件实现表单控件的示例:

<template><div><label>输入框:</label><functional-input :value="value" @input="handleChange" /><div>输入的内容是:{{ value }}</div></div>
</template><script>
const FunctionalInput = {functional: true,render(h, { props, listeners }) {return h('input', {attrs: { type: 'text' },domProps: { value: props.value },on: {input: listeners.input}})}
}export default {components: {FunctionalInput},data() {return {value: ''}},methods: {handleChange(e) {this.value = e.target.value}}
}
</script>

在上述代码中,我们定义了一个函数式组件 FunctionalInput,它接收 value、onUpdate:modelValue、placeholder、disabled 和 type 五个 props,其中 value 和 onUpdate:modelValue 用于实现 v-model 功能,placeholder 用于设置输入框的占位符,disabled 用于控制输入框是否可用,type 用于设置输入框的类型。在组件的 template 中,我们使用 h 函数创建一个 input 元素,并设置其相关属性和事件监听器,最后将其返回。由于这是一个函数式组件,它不会有自己的实例,也不会有响应式的数据,所以我们需要通过 props 将需要的数据传入组件,也需要使用 emit 函数将事件传递给父组件,实现双向绑定的效果。

2.4 路由导航

函数式组件在路由导航中也有着广泛的应用,特别是在需要在页面中嵌入动态路由参数的情况下。以 Vue Router 为例,我们可以在函数式组件中使用 props 属性来获取动态路由参数,进而实现页面的动态渲染。

首先,我们需要在路由配置中定义动态路由参数:

{path: '/user/:id',component: User,props: true
}

在上面的例子中,我们定义了一个名为 id 的动态路由参数,该参数将传递给 User 组件。

接下来,在 User 组件中,我们可以使用函数式组件来获取并渲染动态路由参数:

<template functional><div><h1>User {{ props.route.params.id }}</h1><p>This is the user page.</p></div>
</template>

在上面的例子中,我们使用了 props.route.params 来获取动态路由参数,并将其渲染到页面中。

三、Vue3 函数式组件的高级应用

image-20230421170528496.png

3.1 Scoped Slots

Vue3中函数式组件也支持Scoped Slots,它允许父组件将作用域插槽传递给函数式组件,从而可以将数据或方法传递给函数式组件。

首先,让我们定义一个接受Scoped Slots的函数式组件:

const FunctionalComponentWithScopedSlots = {functional: true,render(_, { slots }) {return slots.default({msg: 'Hello from scoped slot!'})}
}

在这个例子中,我们定义了一个函数式组件FunctionalComponentWithScopedSlots,它接受一个默认插槽,将一个对象{msg: 'Hello from scoped slot!'}传递给它,并通过slots.default将这个对象传递给子组件的插槽。

现在,让我们在父组件中使用这个函数式组件:

<template><functional-component-with-scoped-slots v-slot="{ msg }"><div>{{ msg }}</div></functional-component-with-scoped-slots>
</template><script>
import FunctionalComponentWithScopedSlots from './FunctionalComponentWithScopedSlots.vue'export default {components: {FunctionalComponentWithScopedSlots}
}
</script>

在这个例子中,我们使用了v-slot指令来绑定FunctionalComponentWithScopedSlots的Scoped Slots,将{msg: 'Hello from scoped slot!'}对象传递给插槽,并在插槽内部使用{{ msg }}来展示这个对象的msg属性。

3.2 组件缓存

Vue3 函数式组件的一个重要应用场景是组件缓存,即缓存组件的状态,以避免在组件被多次渲染时重复计算。

在 Vue2 中,我们可以使用 keep-alive 组件来实现组件缓存。但是在 Vue3 中,由于函数式组件的特殊性,我们需要使用另一种方式来实现组件缓存。

Vue3 提供了一个 cache 属性来实现组件缓存。我们可以在渲染函数中通过 cache 属性将组件状态缓存起来,并在下一次渲染时直接使用缓存的状态。

下面是一个使用函数式组件和组件缓存的示例代码:

<template><div><button @click="show = !show">Toggle Show</button><hr><component :is="myComponent" v-if="show"></component></div>
</template><script>
import { ref, computed, h } from 'vue';// 定义一个组件缓存
const cache = new Map();export default {setup() {const show = ref(true);// 定义一个计算属性,根据 show 的值返回相应的组件const myComponent = computed(() => {return show.value ? cachedComponent() : null;});// 定义一个函数式组件const FunctionalComponent = (props, { slots }) => {return h('div', {}, slots.default());};// 将函数式组件进行缓存const cachedComponent = () => {if (!cache.has(FunctionalComponent)) {cache.set(FunctionalComponent, h(FunctionalComponent));}return cache.get(FunctionalComponent);};return {show,myComponent};}
};
</script>

在这个示例代码中,我们首先定义了一个 cache 对象来缓存组件状态。然后,我们定义了一个函数式组件 FunctionalComponent,并使用 cache 对象将它进行了缓存。

setup() 函数中,我们定义了一个响应式变量 show,用来控制组件是否显示。我们通过计算属性 myComponent 根据 show 的值返回相应的组件。

最后,在模板中使用 component 元素来动态渲染组件,并使用 v-if 指令来控制组件的显示。

这个示例代码中的函数式组件很简单,只是将插槽内容包装在一个 div 元素中,但是你可以在实际应用中使用更复杂的函数式组件来实现组件缓存。

3.3 嵌套组件

在 Vue3 中,函数式组件可以像普通组件一样嵌套使用。嵌套使用函数式组件可以更好地组织代码,使得组件结构更加清晰,易于维护。

嵌套函数式组件的语法与普通组件相同。例如,我们可以定义一个包含两个函数式组件的父组件:

<template functional><div><Child1 /><Child2 /></div>
</template><script>
import Child1 from './Child1.vue'
import Child2 from './Child2.vue'export default {components: {Child1,Child2}
}
</script>

在这个例子中,我们定义了一个包含两个函数式组件 Child1Child2 的父组件。在父组件中,我们直接使用了 Child1Child2,就像普通组件一样。

注意,这里的父组件也是一个函数式组件,并且需要将 Child1Child2 组件通过 components 选项进行注册。

3.4 Vue3 函数式组件的高级应用 动态组件

<template><div><button @click="toggleComponent">Toggle Component</button><hr><component :is="currentComponent"></component></div>
</template><script>
import { ref, computed, h } from 'vue';// 定义一个组件缓存
const cache = new Map();export default {setup() {const currentComponentIndex = ref(0);// 定义一个计算属性,根据 currentComponentIndex 的值返回相应的组件const currentComponent = computed(() => {const components = [FunctionalComponent1, FunctionalComponent2];return components[currentComponentIndex.value]();});// 定义两个函数式组件const FunctionalComponent1 = () => {return h('div', {}, 'This is component 1');};const FunctionalComponent2 = () => {return h('div', {}, 'This is component 2');};// 将函数式组件进行缓存const cachedComponent = (component) => {if (!cache.has(component)) {cache.set(component, h(component));}return cache.get(component);};// 切换组件const toggleComponent = () => {currentComponentIndex.value = currentComponentIndex.value === 0 ? 1 : 0;};return {currentComponent,toggleComponent};}
};
</script>

在这个示例中,我们通过 currentComponentIndex 状态来切换两个函数式组件 FunctionalComponent1FunctionalComponent2,并将它们缓存起来,避免多次创建和销毁组件实例。然后在模板中使用动态组件来渲染当前的组件。

四、Vue3 函数式组件的注意事项和常见问题

image-20230421171146810.png

4.1 子组件如何更新父组件的状态

在普通的组件中,我们可以通过 $emit 方法向父组件派发事件来更新父组件的状态。但是在函数式组件中,由于没有实例对象,无法使用 $emit 方法,那么该如何更新父组件的状态呢?

这时可以利用函数式组件的 props 特性来解决。可以通过给函数式组件传递一个回调函数,在函数式组件内部调用该回调函数来更新父组件的状态。

例如,下面的代码演示了如何在函数式组件中通过传递回调函数来更新父组件的状态:

<!-- 父组件 -->
<template><div><h2>父组件</h2><my-functional-component :count="count" @update-count="updateCount" /><p>count: {{ count }}</p></div>
</template><script>
import { defineComponent, ref } from 'vue'
import MyFunctionalComponent from './MyFunctionalComponent.vue'export default defineComponent({components: {MyFunctionalComponent,},setup() {const count = ref(0)const updateCount = (value) => {count.value += value}return {count,updateCount,}},
})
</script>
<!-- 函数式组件 MyFunctionalComponent.vue -->
<template functional><div><h3>函数式组件</h3><button @click="updateCount(1)">增加</button><button @click="updateCount(-1)">减少</button></div>
</template><script>
export default {props: {count: {type: Number,required: true,},updateCount: {type: Function,required: true,},},
}
</script>

在父组件中,通过 :count="count" 将父组件的 count 变量传递给函数式组件,同时通过 @update-count="updateCount" 将父组件的 updateCount 方法传递给函数式组件。

在函数式组件中,通过 props.count 获取父组件传递的 count 变量,通过 props.updateCount 获取父组件传递的 updateCount 方法,并在点击按钮时调用该方法来更新父组件的 count 变量。

4.2 函数式组件中如何使用this

在 Vue3 的函数式组件中,不能像普通组件那样使用 this 来访问组件实例。因为函数式组件本质上是一个纯函数,不会有组件实例的概念。

但是,Vue3 提供了 getCurrentInstance API,可以在函数式组件内获取到当前组件实例。通过 getCurrentInstance().proxy 可以获取到当前组件实例的代理对象,进而访问组件实例的属性和方法。

以下是一个示例:

<template functional><div><p>当前计数:{{ getCurrentInstance().proxy.count }}</p><button @click="getCurrentInstance().proxy.increment">增加</button></div>
</template><script>
import { getCurrentInstance } from 'vue'export default {data() {return {count: 0}},methods: {increment() {this.count++}}
}
</script>

4.3 函数式组件中的v-model

在 Vue3 的函数式组件中,可以使用 v-bindv-on 指令来实现 v-model 的功能。具体来说,使用 v-bind:modelValue 绑定组件的值,使用 v-on:update:modelValue 监听组件的值变化。注意,这里的 modelValue 是自定义的属性名,可以根据实际情况进行命名。

以下是一个示例:

<template functional><div><input :value="props.modelValue" @input="(event) => { props['onUpdate:modelValue'](event.target.value) }"></div>
</template><script>
export default {props: {modelValue: {type: String,default: ''},onUpdate: {type: Function,default: () => {}}}
}
</script>
```![在这里插入图片描述](https://img-blog.csdnimg.cn/fdfee1ac04af4051b2e557aabd305d53.gif#pic_center)

http://www.ppmy.cn/news/47139.html

相关文章

什么是量比(急跌无量是恐吓,缓跌放量赶紧撤)

1、 什么是量比 量比&#xff0c;其实就是衡量相对成交量的一个数值和指标。它具体所指的&#xff0c;就是在股市开盘以后&#xff0c;平均每一分钟的成交量与过去五个交易日平均每分钟交易量的比。按照公式来看&#xff0c;就是量比&#xff08;当下成交总数目/当下累计开市时…

Ansys Lumerical | 光子集成电路之PN 耗尽型移相器仿真工作流

01 说明 本文旨在介绍Ansys Lumerical针对有源光子集成电路中PN耗尽型移相器的仿真分析方法。通过FDE和CHARGE求解器模拟并计算移相器的性能指标&#xff08;如电容、有效折射率扰动和损耗等&#xff09;&#xff0c;并创建用于INTERCONNECT的紧凑模型&#xff0c;然后将其表征…

【神经网络必备知识汇总】:多模态深度学习、multi-channel详解及 项目实例

多模态深度学习 多模态深度学习&#xff08;Multimodal Deep Learning&#xff09;是一种利用多种数据来源&#xff08;如文本、图像、语音等&#xff09;进行深度学习的方法。它可以将不同模态的数据进行融合&#xff0c;从而获得更加全面、准确的信息。 在多模态深度学习中&a…

【华为OD机试真题 C++】1060 - 翻牌求最大分 | 机试题+算法思路+考点+代码解析

文章目录 一、题目&#x1f538;题目描述&#x1f538;输入输出&#x1f538;样例1 二、代码参考 作者&#xff1a;KJ.JK &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &am…

蓝精灵协会 (The Smurfs‘ Society) 宣布与著名艺术家展开一系列的合作,打造传奇 PFP 系列

4 月 18 日&#xff0c;The Smurfs Society 将推出第一个由 Smurfs 品牌支持的官方 PFP 系列。该系列建立在链上游戏的基础之上&#xff0c;该游戏聚集了超过 85,000 名玩家&#xff0c;并在设计、创意和与著名艺术家的合作方面设立了新标准。 而最近&#xff0c;蓝精灵官方&am…

考验大家指针功底的时候到了:请问如何理解 (int*)1 + 1 ?

来&#xff0c;猜猜看&#xff0c;这里的执行结果是什么&#xff1f; 这是今天课上的一道理解题&#xff0c;给大家一点点思考时间。 &#xff08;心里有答案了再往下滑哦&#xff09; 5 4 3 2 1 . 答案是&#xff0c;报warning&#xff01;因为%d不是用来输出指针的哈…

String a = new String(“abc“); 创建了几个对象?String a = “abc“; 呢?

String a new String(“abc”); 创建了几个对象&#xff1f;String a “abc”; 呢&#xff1f; 答案&#xff1a;String a new String(“abc”); 创建了1个或2个对象&#xff1b;String a “abc”; 创建了0个或1个都对象 String a new String(“abc”); 创建过程 首先在…

Java基础(十六):String的常用API

Java基础系列文章 Java基础(一)&#xff1a;语言概述 Java基础(二)&#xff1a;原码、反码、补码及进制之间的运算 Java基础(三)&#xff1a;数据类型与进制 Java基础(四)&#xff1a;逻辑运算符和位运算符 Java基础(五)&#xff1a;流程控制语句 Java基础(六)&#xff1…