函数式组件使用场景和原理?

什么是函数式组件

函数式组件即无状态组件 ,没有datacomputedwatch,也没有生命周期方法,组件中也没有this上下文,只有props传参。在开发中,有很多组件仅仅只用到了props和插槽,这部分组件就可以提炼为函数式组件。借用官网demo,最简单的函数式组件如下:

js 复制代码
Vue.component('my-component', {  
    functional: true,  
    // Props 是可选的  
    props: {    
        // ...  
    },  
    // 为了弥补缺少的实例,提供第二个参数作为上下文  
    render: function (createElement, context) {    
        // ...  
    }
})

函数式组件与普通组件的区别:

组件实例化过程大致分为四步,状态初始化 --> 模板编译 --> 生成VNode --> 转换为真实DOM。接下来对比普通组件与函数式组件常用配置项,比较下差异。

从上表中可以看出,普通组件与函数式组件最大的差别在于函数式组件没有独立作用域,没有响应式数据声明。

没有独立作用域,会有以下优点:

  • 由于函数式组件不需要实例化,无状态,没有生命周期,所以渲染性能比普通组件好。

  • 函数式组件结构比较简单,代码结构更清晰。

  • 没有组件实例化(new vnode.componentOptions.Ctor(options)),函数式组件获取VNode仅仅是普通函数调用。

    • 无公共属性、方法拷贝

    • 无生命周期钩子调用

  • 函数式组件直接挂载到父组件中,缩短首次渲染、diff更新路径。

    • 函数式组件在父组件生成VNode时,函数式组件render方法会被调用,生成VNode挂载到父组件children中,patch阶段可直接转换成真是DOM,普通组件则在createElm时,走组件初始化流程。

    • diff更新时,函数式组件调用render,直接创建普通VNode,而普通组件创建的VNode的是包含组件作用域的,diff操作时,还有额外调用updateChildComponent更新属性、自定义事件等,调用链路会比较长。

具体区别

  • 函数式组件需要在声明组件时指定 functional: true。
js 复制代码
//TempVar.js

export default {
    functional: true,
    render: (h, ctx) => {
        return ctx.scopedSlots.default && ctx.scopedSlots.default(ctx.props || {});
    }
}
js 复制代码
<TempVar 
 :var1 = "`hello ${name}`"
 :var2 = "destoryClock ? 'Hello Vue' :'Hello React">

 <template v-slot="{var1, var2}">
   {{var1}}{{var2}}
 </template>
</TempVar>

以上代码,使用 TempVar 传递了var1, var2 这两个变量,通过作用域插槽返回 var1 和 var2,这时 var1 和 var2 成为临时变量。之后可以在作用插槽中随便使用 var1 和 var2。

  • 不需要实例化,所以没有 this, this 通过 Render 函数的第二个参数 context 来代替。
js 复制代码
const FunctionalComponent = (props, context) => {
    // ... 
}

第二个参数 context 包含三个 property:attrsemitslots。它们分别相当于实例的 $attrs$emit$slots 这几个 property。

大多数常规组件的配置选项在函数式组件中都不可用。然而我们还是可以把 propsemits 作为 property 加入,以达到定义它们的目的:

js 复制代码
FunctionalComponent.props = ['value'];
FunctionalComponent.emits = ['click'];

如果这个 props 选项没有被定义,那么被传入函数的 props 对象就会像 attrs 一样会包含所有 attribute。除非指定了 props 选项,否则每个 prop 的名字将不会基于驼峰命名法被一般化处理。

函数式组件可以像普通组件一样被注册和消费。如果你将一个函数作为第一个参数传入 h,它将会被当作一个函数式组件来对待。

js 复制代码
import { h } from 'vue';
const DynamicHeading = (props, context) => {
    return h(`h${props.level}`, context.attrs, context.slots);
}
DynamicHeading.props = ['level'];
export default DynamicHeading;
  • 没有声明周期钩子函数,不能使用计算属性 watch 。

  • 不能通过 $emit 对外暴露事件,调用事件只能通过 context.listener.click 的方式调用外部传入的事件,因为函数式组件是没有实例化的,所以在外部通过 ref 去引用组件时,实际引用的是 HTMLElement 。

  • 函数式组件的 props 可以不用显示声明,所以没有在 props 里声明的属性会被自动隐式解析为 prop,而普通组件所有未声明的属性都解析到 $atters 里,并且自动挂载到组件根元素上(可以通过 inheritAttrs属性禁止)。

使用场景

  • 简单展示组件,作为容器组件使用,例如 Router-View 就是函数式组件。
  • 高阶组件--用于接收一个组件作为参数,返回以一个被包装过的组件。
相关推荐
FreeCultureBoy33 分钟前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom1 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom1 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom1 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom1 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom2 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试
LaoZhangAI2 小时前
2025最全GPT-4o图像生成API指南:官方接口配置+15个实用提示词【保姆级教程】
前端
ONE_Gua2 小时前
chromium魔改——CDP(Chrome DevTools Protocol)检测01
前端·后端·爬虫
LaoZhangAI3 小时前
2025最全Cherry Studio使用MCP指南:8种强大工具配置方法与实战案例
前端
咖啡教室3 小时前
前端开发日常工作每日记录笔记(2019至2024合集)
前端·javascript