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

什么是函数式组件

函数式组件即无状态组件 ,没有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 就是函数式组件。
  • 高阶组件--用于接收一个组件作为参数,返回以一个被包装过的组件。
相关推荐
m0_7482409117 分钟前
常见问题QA的前端代码
前端
好青崧37 分钟前
HTML 图像标签使用陷阱
前端·html
程序员大金1 小时前
基于SSM+Vue的个性化旅游推荐系统
前端·vue.js·mysql·java-ee·tomcat·mybatis·旅游
m0_748237151 小时前
前端:纯前端快速实现html导出word和pdf
前端·html·word
KevinRay_1 小时前
【Python入门】类和对象
服务器·前端·python·类和对象
m0_748246872 小时前
前端实现读取word文件,并将其进行原样式展示的几种方案
前端·word
桃园码工2 小时前
11_HTML5 拖放 --[HTML5 API 学习之旅]
前端·html5·拖放
2402_857589362 小时前
便捷就医新引擎:SSM 医院预约挂号系统 Vue 实现方案设计
前端·javascript·vue.js
ADFVBM2 小时前
后端使用Spring Boot框架 + 前端VUE 实现滑动模块验证码
前端·vue.js·spring boot
人才程序员3 小时前
【无标题】
c语言·前端·c++·qt·软件工程·qml·界面