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

相关推荐
jacGJ6 小时前
记录学习--文件读写
java·前端·学习
毕设源码-赖学姐6 小时前
【开题答辩全过程】以 基于WEB的实验室开放式管理系统的设计与实现为例,包含答辩的问题和答案
前端
幻云20106 小时前
Python深度学习:从筑基到登仙
前端·javascript·vue.js·人工智能·python
我即将远走丶或许也能高飞8 小时前
vuex 和 pinia 的学习使用
开发语言·前端·javascript
钟离墨笺8 小时前
Go语言--2go基础-->基本数据类型
开发语言·前端·后端·golang
爱吃泡芙的小白白8 小时前
Vue 3 核心原理与实战:从响应式到企业级应用
前端·javascript·vue.js
卓怡学长9 小时前
m115乐购游戏商城系统
java·前端·数据库·spring boot·spring·游戏
老陈聊架构9 小时前
『AI辅助Skill』掌握三大AI设计Skill:前端独立完成产品设计全流程
前端·人工智能·claude·skill
Ulyanov10 小时前
从桌面到云端:构建Web三维战场指挥系统
开发语言·前端·python·tkinter·pyvista·gui开发
cypking10 小时前
二、前端Java后端对比指南
java·开发语言·前端