vue2中如何使用函数式组件

news2024/11/27 2:36:14

vue2 中如何使用函数式组件

    • 用 render 定义函数式组件如何处理 props
    • 如何在函数式组件中触发自定义事件?
    • injection
    • 如何使用 computed 和 methods
    • 定义一个函数式组件的 MyButton
    • 函数式组件有何优势
    • 哪种场景适合使用函数式组件
    • 函数式组件的问题
    • 参考

函数式组件(functional component)是一个不持有状态 data 、实例 this 和生命周期的组件。

函数式组件没有 data、生命周期和 this ,函数式组件又叫无状态组件(stateless component)。

模板定义:template functional

<template functional>
  <div>
    <h1>{{ props.title }}</h1>
  </div>
</template>

<script>
  export default {
    name: 'FunOne',
    props: {
      title: [String]
    }
  }
</script>

<style></style>

render 函数定义 – functional: true

export default {
  name: 'FunTwo',
  functional: true,
  props: {
    title: [String]
  },
  render(h, {
    props
  }) {
    return h('div', {}, [h('h1', {}, props.title)])
  }
}

不能这样定义:

<template>
  <div>
    <h1>{{ title }}</h1>
  </div>
</template>

<script>
  export default {
    name: 'FunOne',
    // NOTE 不能这样定义
    functional: true,
    props: {
      title: [String]
    }
  }
</script>

<style></style>

定义一个函数式组件 MyInput,看看如何使用 props、data 以及事件

MyInput.jsx

export default {
  name: 'MyInput',
  // 声明是一个函数式组件
  functional: true,
  props: {
    value: {
      type: [String, Number],
      default: ''
    }
  },
  // NOTE 函数式组件没有 this
  render(h, context) {
    const {
      props,
      listeners,
      data
    } = context
    return h('input', {
      // DOM 属性
      domProps: {
        value: props.value
      },
      on: {
        input: ({
          target
        }) => {
          data.on['my-change'](Math.random().toString(36))
          // listeners 是 data.on 的别名
          listeners['my-input'](target.value)
          listeners.input(target.value)
        }
      }
    })
  }
}

在 render 函数中使用 MyInput

import MyInput from './MyInput'
export default {
  name: 'MyInputExample',
  data() {
    return {
      value: ''
    }
  },
  render(h) {
    // MyInput 是一个组件对象选项
    return h(MyInput, {
      model: {
        value: this.value,
        callback: value => {
          console.log('model', value)
          this.value = value
        }
      },
      on: {
        // NOTE 在父组件的 MyInputExample 上监听 event-name 事件,
        // 在函数式组件的 listeners 对象
        // 上就会有一个 event-name 方法
        // 用于发送数据到外部
        'my-input': value => {
          console.log('my-input', value)
        },
        'my-change': value => {
          console.log('my-change', value)
        }
      }
    })
  }
}

注意
在父组件的 MyInputExample 上监听 event-name 事件,在函数式组件的 listeners 对象上 才会有 一个 event-name 方法。

用 render 定义函数式组件如何处理 props

vue 在 render 函数的第二个参数中提供了 context ,用于访问 propsslots 等属性:

props: 组件 props 对象。
data: 组件的数据对象,即 h 的第二个参数。
listeners: 组件上监听的事件对象,在组件上监听 `event-name`,listeners 对象就有 `event-name` 属性,值为函数,数据可通过该函数的参数抛到父组件。listeners 是 `data.on` 的别名。
slots: 函数,返回了包含所有插槽的对象。
scopedSlots: 对象,每个属性为返回插槽的 VNode 的函数,可传递参数。
children:子节点数组,可直接传入 `h` 函数的第三个参数。
parent: 父组件,可通过它修改父组件的 data 或者调用父组件的方法。
injection:注入对象。

props 和普通组件的 props 一样,不要求强制,但是声明后可对其类型进行约束,组件接口也更加清晰。

注意
slots() 和 children 的区别?

slots() 返回所有插槽的 对象 ,children 是一个 VNode 数组 ,不包含 template 上的 v-slot

slots() 返回对象
slots

<FunTwo>
  <p slot="left">left</p>
  <span style="color:red;">按钮</span>
  <template v-slot:right>
    <div>right</div>
  </template>
  <template slot="middle">
    <span>left</span>
  </template>
</FunTwo>

children 中包含 pspanspan ,不包含 div

children 是一个数组
children 是一个vnode数组
slots 和 children 同时提供,由你决定渲染谁。

slots 和 scopedSlots 的区别?

slots 是函数,返回包含所有插槽的 VNode 的对象,属性为插槽名字,不能传参数。

scopedSlots 是对象,属性为插槽名,是一个函数,该函数返回对插槽的 VNode,可传参。

scopedSlots
scopedSlots 更加强大一些,可传递参数。

注意

children、slots、scopedSlots 使用谁?

组件外部使用 v-slot 指定插槽,优先使用 scopedSlots ,因为它可以传参。

data 对象

<FunTwo 
 :class="'fun-com'" 
 class="class2" 
 :style="{ 'background-color': '#ccc', color, padding: '20px' }" 
 style="font-size:20px;" 
 :title="title" 
 dataKey="title" 
 @click="onClick" />

包含下列属性:
data对象
on 属性是一个对象,key 是组件上监听的事件, on.click(params) 可把 params 发送到父组件,即和 this.$emit('click',params) 效果一样。

attrs 非 props 属性。

样式处理:包含动态的 class 、静态 staticClass 静态 staticStyle、动态 style,vue 会把 style 归一化。

如何在函数式组件中触发自定义事件?

data.on['event-name'](params) event-name 是组件上监听的事件名称,

listeners['event-name'](params) .

event-name 是组件上监听的事件名称,params 是事件的实参。

注意:父组件监听不监听该事件,就没有该属性。

injection

父组件提供实例:

  provide() {
    return {
      parent: this
    }
  },

在子组件中引入:

inject: ['parent'],

injection 就是一个包含 parent 属性的对象了。

不能用 injection.parent.$emit() 触发自定义事件。$emit 会在内部绑定 this,而 函数式组件 没有实例。

如何使用 computed 和 methods

函数式组件没有像普通组件那样使用计算属性和方法。

模板定义的函数式组件有一个办法 — 直接定义函数,然后在模板中调用 ,比如下面的fullName

<template functional>
  <div>
    <h1>{{ props.title }} {{ $options.fullName(props) }}</h1>
  </div>
</template>

<script>
  export default {
    name: 'FunOne',
    props: {
      title: [String]
    },
    fullName(props) {
      return props.title + 'jack' + 'chou'
    }
  }
</script>

render 定义的函数式组件,不能把函数声明在 props 同级的地方,可在 render 内部声明。

export default {
  name: 'FunButton',
  functional: true,
  props: {
    title: [String]
  },
  render(h, {
    data,
    props,
    children,
    slots,
    scopedSlots,
    injections,
    parent
  }) {
    const fullName = props => {
      return props.title + 'jack' + 'chou'
    }
    return h('div', data, [h('button', {}, fullName(props)), props.title])
  }
}

定义一个函数式组件的 MyButton

MyButton.jsx

export default {
  name: 'MyButton',
  functional: true,
  props: {
    person: {
      type: Object,
      default: () => ({
        name: 'jack',
        age: 23
      })
    }
  },
  render(h, {
    props,
    scopedSlots,
    listeners
  }) {
    // NOTE default 是关键字,需要重命名
    const {
      left,
      right,
      default: _defaultSlot
    } = scopedSlots
    const defaultSlot = (_defaultSlot && _defaultSlot({
      person: props.person
    })) || < span > 按钮 < /span>
    const leftSlot = (left && left()) || ''
    const rightSlot = right && right(props.person)
    const button = h(
      'button', {
        on: {
          click: () => {
            listeners.click && listeners.click(props.person)
          }
        }
      },
      [defaultSlot]
    )
    return ( <
      div > {
        leftSlot
      } {
        button
      } {
        rightSlot
      } <
      /div>
    )
  }
}

模板定义方式

<template functional>
  <div>
    <slot name="left"></slot>
    <button @click="listeners['click'] && listeners['click'](props.person)">
      <slot :person="props.person">
        <span>按钮</span>
      </slot>
    </button>
    <slot name="right" :age="props.age"></slot>
  </div>
</template>

<script>
  export default {
    name: 'MyButton',
    props: {
      person: {
        type: Object,
        default: () => ({
          name: '函数式组件',
          age: 24
        })
      }
    }
  }
</script>

函数式组件有何优势

函数式组件可读性差,为何还有呢?

快速,即性能好。

函数式组件没有状态,也就不需要针对 Vue 反应式系统等额外的初始化了

会对新传入的 props 等做出反应,但对于组件自身,并不知晓其数据何时改变,因为其并不维护自己的状态,即没有 data。

哪种场景适合使用函数式组件

  1. 纯展示的组件,这类组件往往逻辑简单。
  2. v-for 循环很多的情况,把这部分代码提取成函数式组件。
  3. 动态得选择多个组件中一个来渲染。
  4. 在将 children、props、data slots 传递给子组件之前操作它们 ---- 相当于使用函数式组件作为其父组件,对其二次封装
  5. 高阶组件(HOC)— 通过 props 接收一个返回 VNode 的函数,函数的第一个参数为h,在一个函数式组件中执行该函数,此函数式组件就是高阶组件。

高阶组件的例子:

export default {
  name: 'Container',
  functional: true,
  render(h, {
    props
  }) {
    return props.renderContainer(h, props.data)
  }
}

在模板中使用高阶组件:

<template>
  <div class="zm-form-table">
    <ul>
      <li v-for="(item, index) in titleList" :key="index">
        <Container v-if="typeof item.prop === 'function'" :renderContainer="item.prop" :data="data" />
        <span v-else>
          {{
              ![null, void 0, ''].includes(data[item.prop] &&
                data[item.prop] ||''
            }}
        </span>
  </div>
  </li>
  </ul>
  </div>
</template>

<script>
  import Container from './container.js'
  export default {
    name: 'FormTable',
    components: {
      Container,
    },
    props: {
      titleList: {
        type: Array,
        default: () => {
          return []
        },
      },
      data: {
        type: Object,
        default: () => {
          return {}
        },
      },
    },
  }
</script>

这样传递 props:

<template>
  <FormTable :titleList="titleList" :data="detail" />
</template>
<script>
  export default {
    name: 'BaseInfo',
    data() {
      return {
        detail: {},
        titleList: [{
            title: '家长身份',
            // NOTE data 是从 Container 的 render 函数里传入的,prop 返回 VNode 能是组件扩展性更好
            prop: (h, data) => {
              const options = [{
                  label: '爸爸',
                  value: 'father'
                },
                {
                  label: '妈妈',
                  value: 'mother'
                },
                {
                  label: '爷爷',
                  value: 'grandpa'
                },
                {
                  label: '奶奶',
                  value: 'grandma'
                },
                {
                  label: '外公',
                  value: 'grandfather'
                },
                {
                  label: '外婆',
                  value: 'grandmother'
                },
                {
                  label: '其他',
                  value: 'other'
                }
              ]
              const identity = options.find(item => data[key] === item.value)
              // 返回 jsx 实现自定义展示内容
              return <span > {
                identity && identity.label
              } < /span>
            }
          },
          {
            title: '家长微信',
            prop: 'weiXin'
          }
        ]
      }
    },
    created() {
      setTimeout(() => {
        // 模拟接口返回
        this.detail = {
          identity: 'father',
          weiXin: 'jack8848'
        }
      }, 1000)
    }
  }
</script>

说明:
把 render 函数通过 props 传入组件,这种模式极为有用,它能组件的使用真的自定义渲染内容。

react 中高阶组件:
组件作为入参,新组件作为返回值的函数,在返回之前,可以做一些其他处理,做其他处理才是高阶组件的目的。vue 也能实现这样的组件,但是有点麻烦了。
可参考这里: 探索 Vue 高阶组件

函数式组件的问题

  1. 样式的 scoped 在函数式组件和状态组件表现不同。

scoped 样式的函数式组件把 scoped 样式的函数式组件作为子组件,css 选择器相同,父组件的样式生效,即子组件的 scoped 没有生效。

参考

Scoped styles inconsistent between functional and stateful components

Renderless Components in Vue.js

[译]: Vue.js 函数式组件:what, why & when?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1803087.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

WPS JSA 宏脚本入门和样例

1入门 WPS window版本才支持JSA宏的功能。 可以自动化的操作文档中的一些内容。 参考文档&#xff1a; WPS API 参考文档&#xff1a;https://open.wps.cn/previous/docs/client/wpsLoad 微软的Word API文档&#xff1a;Microsoft.Office.Interop.Word 命名空间 | Microsoft …

测试工具链

缺陷管理 bug管理工具 devops---项目管理--缺陷管理 bug管理地址 https://devsecops.mychery.com:8443/chery/project?filterROLE&statusACTIVE bug管理环境 采用公司的devops平台&#xff0c;对每个项目的bug进行管理。目前在使用 接口测试和服务端性能测试 工具…

基础乐理入门

基础概念 乐音&#xff1a;音高&#xff08;频率&#xff09;固定&#xff0c;振动规则的音。钢琴等乐器发出的是乐音&#xff0c;听起来悦耳、柔和。噪音&#xff1a;振动不规则&#xff0c;音高也不明显的音。风声、雨声、机器轰鸣声是噪音&#xff0c;大多数打击乐器&#…

【CS.SE】使用 docker pull confluentinc/cp-kafka 的全面指南

文章目录 1 引言2 准备工作2.1 安装 Docker2.1.1 在 Linux 上安装 Docker2.1.2 在 macOS 上安装 Docker2.1.3 在 Windows 上安装 Docker 2.2 验证 Docker 安装 3 拉取 confluentinc/cp-kafka Docker 镜像3.1 拉取镜像3.2 验证镜像 4 运行 Kafka 容器4.1 启动 ZooKeeper4.2 启动…

Nextjs学习教程

一.手动创建项目 建议看这个中文网站文档,这个里面的案例配置都是手动的,也可以往下看我这个博客一步步操作 1.在目录下执行下面命令,初始化package.json文件 npm init -y2.安装react相关包以及next包 yarn add next react react-dom // 或者 npm install --save next react…

2024最新 Jenkins + Docker实战教程(八)- Jenkins实现集群并发构建

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

转型AI产品经理(4):“认知负荷”如何应用在Chatbot产品

认知负荷理论主要探讨在学习过程中&#xff0c;人脑处理信息的有限容量以及如何优化信息的呈现方式以促进学习。认知负荷定律认为&#xff0c;学习者的工作记忆容量是有限的&#xff0c;而不同类型的认知任务会对工作记忆产生不同程度的负荷&#xff0c;从而影响学习效果。以下…

Java面试八股之什么是自动装箱和自动拆箱

什么是自动装箱和自动拆箱 在Java中&#xff0c;自动装箱&#xff08;Autoboxing&#xff09;和自动拆箱&#xff08;Auto-unboxing&#xff09;是两个与基本数据类型和它们对应的包装类之间的转换相关的特性。这两个概念自Java 5&#xff08;也称为Java SE 5或JDK 5&#xff…

SpringBoot3整合Mybatis-Plus3.5.5出现的问题

主要是由于 mybatis-plus 中 mybatis 的整合包版本不够导致的 排除 mybatis-plus 中自带的 mybatis 整合包&#xff0c;单独引入即可 java.lang.IllegalArgumentException: Invalid value type for attribute factoryBeanObjectType: java.lang.Stringat org.springframework.…

Linux安装Qt5.14.2

下载 qt 5.14.2下载网址 下载qt-opensource-linux-x64-5.14.2.run Linux系统下载.run文件&#xff08;runfile文件&#xff09;&#xff0c;windows系统下载.exe文件&#xff0c;mac系统下载.dmg文件。 md5sums.txt中是各个文件对应的MD5校验码。 验证MD5校验码 md5sum是li…

例54:Draw使用

建立一个控制台工程&#xff0c;输入代码&#xff1a; Screen 13 移动到&#xff08;50,50&#xff09;而不绘图 Draw "BM 50,50" B:移动但不绘制,M:移动到指定位置 将绘图颜色设置为2&#xff08;绿色&#xff09; Draw "C2" C将颜色改为n …

后台管理系统排序混乱,分页出现重复条例

检查了接口和请求参数都没有问题。 查询数据库发现是排序字段create_time 都相同导致的。没有区分度。 解决方案 按照唯一id排序 避免create_time 大批量相同 order by create_time &#xff0c;xxx 两个排序字段

171.二叉树:二叉树的所有路径(力扣)

代码解决 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr, right(nullptr) {}* Tree…

电脑提示msvcp140.dll丢失的解决方法(附带详细msvcp140.dll文件分析)

msvcp140.dll是一个动态链接库&#xff08;DLL&#xff09;文件&#xff0c;属于Microsoft Visual C 2015 Redistributable的一部分。它全称为 "Microsoft C Runtime Library" 或 "Microsoft C Runtime Library"&#xff0c;表明该文件是微软C运行时库的一…

Vue3【十二】09Computed计算属性

Vue3【十二】09Computed计算属性 计算属性 获取全名 这种方式是只读的不能修改 这样定义fullName是一个计算属性&#xff0c;可读可写 案例截图 目录结构 代码 Person.vue <template><div class"person"><h1>我是 Person 组件</h1>姓&…

09.2手工制作docker镜像-kod服务

手工制作docker镜像-kod服务 基于centos6.9系统镜像&#xff0c;搭建kod服务&#xff0c;提交镜像 创建并进入容器 添加centos6系统的yum源和epel源 yum源 curl -o /etc/yum.repos.d/CentOS-Base.repo https://www.xmpan.com/Centos-6-Vault-Aliyun.repo epel源 curl -o /e…

家宽动态公网IP,使用docker+ddns 实现动态域名解析

官方地址&#xff1a;https://github.com/jeessy2/ddns-go 安装docker docker pull jeessy/ddns-godocker run -d --name ddns-go --restartalways --nethost -v /opt/ddns-go:/root jeessy/ddns-go然后访问ip端口 配置时注意如下

基于OpenVINO实现无监督异常检测

异常检测(AD) 在欺诈检测、网络安全和医疗诊断等关键任务应用中至关重要。由于数据的高维性和底层模式的复杂性&#xff0c;图像、视频和卫星图像等视觉数据中的异常检测尤其具有挑战性。然而&#xff0c;视觉异常检测对于检测制造中的缺陷、识别监控录像中的可疑活动以及检测医…

突发!凌晨4点某制造业大厂国产数据库集群故障...

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 作者&#xff1a;IT邦德 中国DBA联盟(ACDU)成员&#xff0c;10余年DBA工作经验&#xff0c; Oracle、PostgreSQL ACE CSDN博客专家及B站知名UP主&#xff0c;全网粉丝10万 擅长主流Oracle、My…

02Linux文件,目录,过滤,管道常用命令

Linux基础概述 Linux基础目录 Linux没有盘符这个概念, 只有一个顶级根目录 /, 所有文件都在它下面 在Windows系统中路径之间的层级关系使用/来表示在Linux系统中路径之间的层级关系使用/来表示,出现在开头的/表示根目录, /home/a.txt表示根目录下的home文件夹内有a.txt文件 …