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 元素,所以什么时候应用函数式组件进行优化,还是需要根据具体场景进行权衡。

相关推荐
2分钟速写快排24 分钟前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60611 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角2 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!2 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰2 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi4 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen5 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马5 小时前
AI源码分析:拆解模型底层实现逻辑
前端
IT_陈寒6 小时前
React子组件莫名其妙重渲染?你可能漏了这个Hook
前端·人工智能·后端
乘风gg6 小时前
DeepSeek V4.1 Flash 来了,明天中午 Flash 降价 60%
前端·ai编程·claude