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

什么是函数式组件

函数式组件即无状态组件 ,没有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 就是函数式组件。
  • 高阶组件--用于接收一个组件作为参数,返回以一个被包装过的组件。
相关推荐
祈澈菇凉20 分钟前
如何结合使用thread-loader和cache-loader以获得最佳效果?
前端
垣宇23 分钟前
Vite 和 Webpack 的区别和选择
前端·webpack·node.js
java1234_小锋26 分钟前
一周学会Flask3 Python Web开发-客户端状态信息Cookie以及加密
前端·python·flask·flask3
化作繁星30 分钟前
如何在 React 中测试高阶组件?
前端·javascript·react.js
Au_ust37 分钟前
千峰React:函数组件使用(2)
前端·javascript·react.js
爱吃南瓜的北瓜1 小时前
npm install 卡在“sill idealTree buildDeps“
前端·npm·node.js
TTc_1 小时前
记录首次安装远古时代所需的运行环境成功npm install --save-dev node-sass
前端·npm·sass
翻滚吧键盘1 小时前
npm使用了代理,但是代理软件已经关闭导致创建失败
前端·npm·node.js
烂蜻蜓1 小时前
Uniapp 设计思路全分享
前端·css·vue.js·uni-app·html
GAMESLI-GIS1 小时前
【WebGL】fbo双pass案例
前端·javascript·webgl