vue2中如何使用函数式组件

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 函数式组件没有 thisrender(h, context) {const {props,listeners,data} = contextreturn 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} = scopedSlotsconst 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.mzph.cn/bicheng/24814.shtml

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

相关文章

FineReport使用小记(不断更新中…………)

FineReport使用小记 1. 单元格相关设置1.1. 单元格值样式 2. 报表块设置2.1. 给报表块加单位 1. 单元格相关设置 1.1. 单元格值样式 1. 百分比样式 选中单元格&#xff0c;单元格属性——>文本——>格式——>百分比 下面可以选择保留几位小数&#xff0c;图中为保留…

【MySQL】常见可执行程序

本文使用的版本是MySQL8&#xff0c;5.7可能会有所不同。 MySQL提供了一些重要的程序用来管理和操作数据库。这里会介绍一些常用的程序及其使用。对于MySQL程序的使用&#xff0c;可以查看官方帮助手册来学习。 MySQL :: MySQL 8.0 Reference Manual :: 6 MySQL Programs 程序…

找了半天,还不如自己写一个图片转ico格式的程序

关于jpg、png等图片转ICO格式 最近突然急需一张ico格式的文件&#xff0c;就拿着处理好的png图片出网上找在线转换器&#xff0c;找了一个小时&#xff0c;绝了&#xff0c;不是需要注册充钱就是下载不下来&#xff0c;好不容易下载下来还是个文件错误。想着找个PS插件直接导出…

烧写uboot、linux镜像、根文件系统到开发板

烧写uboot、linux镜像、根文件系统到开发板 环境介绍 本博客使用x6818开发板。 公司&#xff1a;三星 ARM架构 Cortex-A53核 型号&#xff1a;S5P6818 特性&#xff1a;8核&#xff0c;最高主频2GHz 烧写uboot 使用网络烧写 网络烧写上位机是Ubuntu虚拟机。 先利用上…

基于STM32智能小车

一、前置准备 前置知识&#xff1a;需要学习stm32&#xff0c;建议去b站看江科大的视频&#xff0c;讲的很详细&#xff0c;学完串口那一块就可以制作了&#xff0c;软件用的是Keil5&#xff0c;开发语言C语言&#xff0c;手机连接蓝牙模块软件是蓝牙调试器。 需要准备的器件…

数学+思维,CF1056B - Divide Candies

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 Problem - 1056B - Codeforces 二、解题报告 1、思路分析 考虑i^2 j^2 | m 而m的余数有限&#xff0c;且m很小 我们枚举两重循环&#xff0c;都枚举m的余数&#xff0c;分别记为x&#xff0c;y 如果x ^ …

Linux☞进程控制

在终端执行命令时&#xff0c;Linux会建立进程&#xff0c;程序执行完&#xff0c;进程会被终止&#xff1b;Linux是一个多任务的OS,允许多个进程并发运行&#xff1b; Linxu中启动进程的两种途径&#xff1a; ①手动启动(前台进程(命令gedit)...后台进程(命令‘&’)) ②…

数据库安全加固与API防护策略

在数字化时代&#xff0c;数据库作为企业核心资产的安全性至关重要。然而&#xff0c;随着网络攻击手段的不断演进&#xff0c;数据库和API接口成为了黑客的主要攻击目标。本文将探讨数据库被攻击、API接口被滥用的情况&#xff0c;并提供一系列实用的防护措施&#xff0c;旨在…

【玩转C语言】第三讲---> scanf 和 printf 函数详解(非常重要)!

&#x1f525;博客主页&#x1f525;&#xff1a;【 坊钰_CSDN博客 】 欢迎各位点赞&#x1f44d;评论✍收藏⭐ 引言&#xff1a; 大家好&#xff0c;我是坊钰&#xff0c;为了让大家深入了解C语言&#xff0c;我开创了【玩转C语言系列】&#xff0c;将为大家介绍C语言相关知识…

C++ ─── STL 以及string

前言&#xff1a;什么是STL STL(standard template libaray-标准模板库)&#xff1a;是C标准库的重要组成部分&#xff0c;不仅是一个可复用的组件库&#xff0c;而且 是一个包罗数据结构与算法的软件框架 STL的六大组件 1. 为什么学习string类&#xff1f; 1.1 C语言中的字符…

Ambari集成Apache Kyuubi实践

目前还有很多公司基于HDP来构建自己的大数据平台&#xff0c;随着Apache Kyuubi的持续热度&#xff0c;如何基于原有的HDP产品来集成Apache Kyuubi&#xff0c;很多人都迫切的需求。集成Apache Kyuubi到HDP中&#xff0c;主要涉及Ambari的二次开发。本文详细叙述了集成Apache K…

分享一个用python的本地WIFI密码查看器

本章教程&#xff0c;主要分享一个本地wifi密码查看器&#xff0c;用python实现的&#xff0c;感兴趣的可以试一试。 具体代码 import subprocess # 导入 subprocess 模块&#xff0c;用于执行系统命令 import tkinter as tk # 导入 tkinter 模块&#xff0c;用于创建图形用…

实现Ingress-Nginx Controller高可用方案

文章目录 前提准备1.修改Ingress-Controller 运行模式为hostNetwork并生效2.给部署ingress-controller的节点打标签3.查看ingress-controller的部署情况 方式一&#xff1a;LVSKeepalivedNginxIngress一、部署ipvsadm和keepalived二、配置keepalived1.配置lvs01(keepalived mas…

Java Web学习笔记27——对话框、表单组件

常见组件对话框&#xff1a; Dialog对话框&#xff1a;在保留当前页面状态下&#xff0c;告知用户并承载相关操作。 dialogTableVisible: false 默认是不可见的。 在按钮属性中设置为true的意思&#xff0c;点击按钮的时候&#xff0c;才会true&#xff0c;对话框才会显示。 …

python Tk 获取输入框内容,分割内容

创建输入框、一个按钮和一个标签的GUI。 用户可以在输入框中输入文本&#xff0c;点击按钮后&#xff0c;程序将在控制台打印输入的文本&#xff08;已经分割为列表&#xff09;&#xff0c;并在GUI中的标签上显示一些静态文本。 import tkinter as tk# 创建主窗口 root tk.…

基于SpringBoot+Vue学生请假管理系统设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝1W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;还…

linux指令--sed

sed 主要用来自动编辑一个或多个文件、简化对文件的反复操作、编写转换程序等。 语法解析 sed [选项] 编辑命令 文件 选项&#xff1a; -n&#xff1a;只显示匹配处理的行-e&#xff1a;执行多个编辑命令时-i&#xff1a;在原文件中进行修改&#xff0c;不输出到屏幕-…

qsort函数

学习c语言的过程中少不了的就是排序&#xff0c;例如冒泡排序&#xff08;不清楚的同学可以翻找一下之前的文章&#xff09;&#xff0c; 我们这里将冒泡排序作为一个自定义函数来呈现一下 #include<stdio.h>void bubble_sort(int arr[], int len) {for (int i 0; i &…

【Vue】mutations

文章目录 一、定义mutations二、组件中提交 mutations三、带参数的 mutations 一、定义mutations mutations是vuex中的对象&#xff0c;这个对象可以定义在当前store的配置项中 const store new Vuex.Store({state: {count: 0},// 定义mutations// mutations是一个对象&#x…

Progressive Feature Fusion Framework Based on Graph Convolutional Network

以Resnet50作为主干网络&#xff0c;然后使用GCN逐层聚合多级特征&#xff0c;逐级聚合这种模型架构早已不新鲜&#xff0c;这篇文章使用GCN的方式对特征进行聚合&#xff0c;没有代码。这篇文章没有过多的介绍如何构造的节点特征和邻接矩阵&#xff0c;我觉得对于图卷积来说&a…