vue 项目优化之函数式组件

前言

如果有个组件,不需要任何状态管理(在 vue2 中就是没有 data),只需要接收 props,而且还不需要监听任何传递给它的状态,即不需要监听 props 的变化,我们就可以把它定义成函数式组件来进行优化。

所谓函数式组件,是指其相较于传统组件:

  • 它是没有状态的,即没有响应式数据的组件;
  • 没有生命周期方法;
  • vue 也不会为该组件创建实例,故而不会有 this 上下文。

举例

比如有个需求是要封装一个展示标题的组件,该组件只需要接收一个 prop ------ title,即标题的文字。vue2 和 vue3 的函数式组件写法略有不同,下面分开举例。

vue2

传统组件

传统组件写法如下:

vue 复制代码
<!-- src\components\Title.vue -->
<template>
  <div class="title">{{ title }}</div>
</template>

<script>
  export default {
    name: 'Title',
    props: {
      title: {
        type: String,
        default: ''
      }
    }
  }
</script>

<style scoped>
  .title {
    font-size: 30px;
  }
</style>

函数式组件

在 vue2 中,函数式组件有 2 种写法。

写法一:.vue 文件

如果写成 .vue 文件,则需要在 <template> 添加 functional,并且在 <template> 内使用 prop 时要通过 props,比如 {{ props.title }}

vue 复制代码
<!-- src\components\FunctionalTitle.vue -->
<template functional>
  <div class="title">{{ props.title }}</div>
</template>

<script>
  export default {
    name: 'FunctionalTitle',
    props: {
      title: {
        type: String,
        default: ''
      }
    }
  }
</script>

<style scoped>
  .title {
    font-size: 30px;
  }
</style>
写法二:.js 文件

也可以直接写成函数,需要定义 functionaltrue

vue 复制代码
// src\components\FunctionalTitle.js
export default {
  name: 'FunctionalTitle',
  functional: true,
  props: {
    title: {
      type: String,
      default: ''
    }
  },
  render(h, ctx) {
    return h('div', { style: { 'font-size': '30px' } }, ctx.props.title)
  }
}

其中 render 函数的第 1 个参数 h渲染函数,第 2 个参数 ctx 打印如下,可以看到其中有个属性 props,可以获取到传入的 title

父组件

父组件代码如下,点击"加载传统组件"按钮则循环生成 10000 个传统组件;点击"加载函数式组件"则循环生成 10000 个函数式组件:

vue 复制代码
<!-- src\App.vue -->
<template>
  <div id="app">
    <button @click="num1 = 10000">加载传统组件</button>
    <button @click="num2 = 10000">加载函数式组件</button>
    <div class="box">
      <div>
        <div v-for="item in num1" :key="item">
          <Title :title="'标题-' + item" />
        </div>
      </div>
      <div>
        <div v-for="item in num2" :key="item">
          <FunctionalTitle :title="'标题-' + item" />
        </div>
      </div>
    </div>
  </div>
</template>

<script>
  import Title from './components/Title.vue'
  // import FunctionalTitle from './components/FunctionalTitle.vue'
  import FunctionalTitle from './components/FunctionalTitle.js'
  export default {
    components: {
      Title,
      FunctionalTitle
    },
    data() {
      return {
        num1: 0,
        num2: 0
      }
    }
  }
</script>

<style>
  .box {
    display: flex;
  }
</style>

vue3

传统组件

vue3 中传统组件写法如下:

vue 复制代码
<!-- src\components\Title.vue -->
<script lang="ts" setup>
  const { title = '' } = defineProps<{ title?: string }>()
</script>

<template>
  <div class="title">{{ title }}</div>
</template>

<style scoped>
  .title {
    font-size: 30px;
  }
</style>

函数式组件

vue3 中,函数式组件可以直接在父组件通过函数定义:

vue 复制代码
<!-- src\App.vue -->
<script setup lang="ts">
import Title from './components/Title.vue'
import { h, ref } from 'vue'
import type { FunctionalComponent } from 'vue'

const num1 = ref(0)
const num2 = ref(0)

// 创建函数式组件
type FComponentProps = {
  title: string
}
const FunctionalTitle: FunctionalComponent<FComponentProps> = (props, ctx) => {
  return h('div', { style: { 'font-size': '30px' } }, props.title)
}
</script>

<template>
  <button @click="num1 = 10000">加载传统组件</button>
  <button @click="num2 = 10000">加载函数式组件</button>
  <div class="box">
    <div>
      <div v-for="item in num1" :key="item">
        <Title :title="'标题-' + item" />
      </div>
    </div>
    <div>
      <div v-for="item in num2" :key="item">
        <FunctionalTitle :title="'标题-' + item" />
      </div>
    </div>
  </div>
</template>

<style scoped>
.box {
  display: flex;
}
</style>

定义函数式组件的函数参数中,如果打印查看第 2 个参数 ctx,结果如下:

性能对比

以 vue2 项目为例进行测试,调出谷歌浏览器的"性能"面板,点击"录制",然后分别点击"加载传统组件"和"加载函数式组件"按钮,结果如下。

传统组件

加载 10000 个传统组件,js 的运行时间为 189 毫秒,内存从 61.2 MB 增加到了 100 MB,增加了近 40 MB:

函数式组件

而加载 10000 个函数式组件,js 的运行时间为 114 毫秒,内存从 100 MB 增加到了 125 MB,增加了 25 MB:

如果在 App.vue 添加生命周期函数,将实例 this 传给 window.vm

javascript 复制代码
mounted() {
  window.vm = this
}

然后将点击 2 个按钮的 click 事件改为如下所示,即只各自渲染 1 个传统组件和函数式组件:

html 复制代码
<button @click="num1 = 1">加载传统组件</button>
<button @click="num2 = 1">加载函数式组件</button>

在控制台打印查看 vm,可以看到 vue 只为传统组件创建了实例 VueComponent,故而传统组件要比函数式组件的渲染速度更快,占用内存更少:

vm_vnode 属性上,可以看到对于传统组件,它对应的 tagvue-component-2-Title

而函数式组件对应的 tag 直接就是个 div

结论

通过对比得出结论,函数式组件相比传统组件,其优势在于:

  1. 渲染速度更快;
  2. 渲染开销更低。

但是劣势就是只适用于比较简单的 ui 元素,所以什么时候应用函数式组件进行优化,还是需要根据具体场景进行权衡。

相关推荐
EndingCoder8 分钟前
React从基础入门到高级实战:React 实战项目 - 项目三:实时聊天应用
前端·react.js·架构·前端框架
阿阳微客1 小时前
Steam 搬砖项目深度拆解:从抵触到真香的转型之路
前端·笔记·学习·游戏
德育处主任Pro2 小时前
『React』Fragment的用法及简写形式
前端·javascript·react.js
CodeBlossom2 小时前
javaweb -html -CSS
前端·javascript·html
打小就很皮...3 小时前
HBuilder 发行Android(apk包)全流程指南
前端·javascript·微信小程序
集成显卡4 小时前
PlayWright | 初识微软出品的 WEB 应用自动化测试框架
前端·chrome·测试工具·microsoft·自动化·edge浏览器
前端小趴菜054 小时前
React - 组件通信
前端·react.js·前端框架
Amy_cx5 小时前
在表单输入框按回车页面刷新的问题
前端·elementui
dancing9995 小时前
cocos3.X的oops框架oops-plugin-excel-to-json改进兼容多表单导出功能
前端·javascript·typescript·游戏程序
后海 0_o5 小时前
2025前端微服务 - 无界 的实战应用
前端·微服务·架构