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

相关推荐
weifexie1 分钟前
ruby可变参数
开发语言·前端·ruby
千野竹之卫2 分钟前
3D珠宝渲染用什么软件比较好?渲染100邀请码1a12
开发语言·前端·javascript·3d·3dsmax
sunbyte2 分钟前
初识 Three.js:开启你的 Web 3D 世界 ✨
前端·javascript·3d
半兽先生23 分钟前
WebRtc 视频流卡顿黑屏解决方案
java·前端·webrtc
Amd7941 小时前
FastAPI依赖注入性能优化策略
单例模式·性能优化·fastapi·依赖注入·错误处理·异步编程·缓存机制
南星沐2 小时前
Spring Boot 常用依赖介绍
java·前端·spring boot
孙_华鹏2 小时前
手撸一个可以语音操作高德地图的AI智能体
前端·javascript·coze
zhangxingchao2 小时前
Jetpack Compose 动画
前端
@PHARAOH2 小时前
HOW - 缓存 React 自定义 hook 的所有返回值(包括函数)
前端·react.js·缓存
拉不动的猪2 小时前
设计模式之--------工厂模式
前端·javascript·架构