《精通react/vue组件设计》之5分钟实现一个Tag(标签)组件和Empty(空状态)组件

前言

本文是笔者写组件设计的第五篇文章,之所以会写组件设计相关的文章,是因为作为一名前端优秀的前端工程师,面对各种繁琐而重复的工作,我们不应该按部就班的去"辛勤劳动",而是要根据已有前端的开发经验,总结出一套自己的高效开发的方法.作为数据驱动的领导者react/vue等MVVM框架的出现,帮我们减少了工作中大量的冗余代码, 一切皆组件的思想深得人心.所以, 为了让工程师们有更多的时间去考虑业务和产品迭代,我们不得不掌握高质量组件设计的思路和方法.所以笔者将花时间去总结各种业务场景下的组件的设计思路和方法,并用原生框架的语法去实现各种常用组件的开发,希望等让前端新手或者有一定工作经验的朋友能有所收获.

今天主要带大家一起实现一个Tag组件和Empty(空状态)组件,在介绍组件设计之前,先给大家介绍一个免费开源的图标库icomoon,

可以在线导入SVG格式字体,并进行编辑,然后下载来使用,在组件设计中有具体的使用介绍.

如果对于react/vue组件设计原理不熟悉的,可以参考我的之前写的组件设计系列文章:

正文

在开始组件设计之前希望大家对css3和js有一定的基础.我们先看看实现后的组件效果: 由图可以知道tag组件可以自定义颜色主题(color theme), 可以手动关闭标签, 空状态主要是提供用户数据展示用的, 实现起来很简单,重点在图标的使用上.

1. 组件设计思路

按照之前笔者写的组件设计原则,我们第一步是要确认需求. 一个tag标签组件一般都会有如下需求点:

  • 可以改变标签颜色
  • 提供关闭标签的配置,让用户可以关闭标签
  • 关闭标签的回调,让用户能控制标签关闭后触发的动作

需求收集好之后,作为一个有追求的程序员, 会得出如下线框图:

对于react选手来说,如果没用typescript,建议大家都用PropTypes, 它是react内置的类型检测工具,我们可以直接在项目中导入. vue有自带的属性检测方式,这里就不一一介绍了.

2. 基于react实现一个Tag组件

2.1. tag组件框架设计

首先我们先根据需求将组件框架写好,这样后面写业务逻辑会更清晰:

1import React from 'react' 2import classnames from 'classnames' 3import styles from './index.less' 4 5/** 6 * 标签组件 7 * @param {closable} boolean 是否可关闭 8 * @param {onClose} func 标签关闭时的回调 9 * @param {color} string 标签的颜色,不设置则为默认颜色 10 */ 11export default function Tag(props) { 12 let { children, closable, onClose, color } = props 13 return <div 14 className={styles.xTag} 15 style={{ backgroundColor: color }} 16 { children } 17 </div> 18}

有了这个框架,我们来一步步往里面实现内容吧. 根据需求,颜色这个属性好实现,在上述代码中已经实现了, 我们看看closable和onClose如何实现.我们要向关闭tag,实际上是需要将这个标签隐藏,比如说使用display:none,或者从dom中移除, 笔者就参考antd的实现方式,通过display:none来实现.

首先我们要想在react的函数式组件操作dom, 最好的方式是使用ref, 关于ref的使用如果不熟悉的可以参考react官方文档,这里实现如下:

1import React from 'react' 2import classnames from 'classnames' 3import styles from './index.less' 4 5/** 6 * 标签组件 7 * @param {closable} boolean 是否可关闭 8 * @param {onClose} func 标签关闭时的回调 9 * @param {color} string 标签的颜色,不设置则为默认颜色 10 */ 11export default function Tag(props) { 12 let { children, closable, onClose, color } = props 13 let tag = React.createRef() 14 let handleClose = () => { 15 onClose && onClose() 16 tag.current.style.display = 'none' 17 } 18 return <div 19 className={classnames(styles.xTag, color ? styles.xTagHasColor : '')} 20 style={{ backgroundColor: color }} 21 ref={tag}> 22 { children } 23 { closable && <span className={styles.closeBtn} onClick={handleClose}>x</span> } 24 </div> 25}

通过react的createRef API,我们很方便的拿到了当前的dom对象, 在handleClose可以进行属性的分配. 组件的js代码基本实现完成了,接下来看看css:

1.xTag { 2 margin-bottom: 8px; 3 margin-right: 8px; 4 display: inline-block; 5 border-radius: 4px; 6 border: 1px solid #d9d9d9; 7 padding: 0 7px; 8 font-size: 12px; 9 line-height: 20px; 10 white-space: nowrap; 11 background-color: #fafafa; 12 &.xTagHasColor { 13 border-color: transparent; 14 color: #fff; 15 .closeBtn { 16 color: rgba(255, 255, 255, .6) 17 } 18 } 19 .closeBtn { 20 margin-left: 5px; 21 color: rgba(0, 0, 0, 0.45); 22 cursor: pointer; 23 } 24}

健壮性支持:

1import PropTypes from 'prop-types' 2// ... 3Tag.propTypes = { 4 color: PropTypes.string, 5 closable: PropTypes.bool, 6 onClose: PropTypes.func 7}

是不是很简单? 这样一个可定制对的tag组件就完成了,关于代码中的css module和classnames的使用大家可以自己去官网学习,非常简单.

3. 基于react实现一个Empty(空状态)组件

这个组件非常好写, 目前常用的空状态组件一般是图片和文字组合, 图片文字都可替换,具体实现如下:

1import classnames from 'classnames' 2import styles from './index.less' 3 4/** 5 * 空状态组件 6 * @param {className} string 自定义类名 7 * @param {text} string 空状态文本 8 */ 9export default function Empty(props) { 10 let { text, className } = props 11 12 return <div className={classnames(styles.emptyWrap, className)}> 13 <div className={styles.emptyInner}><span className="icon-finder"></span></div> 14 <p>{ text ? text : '空空如也'}</p> 15 </div> 16}

这里主要介绍icon-finder的由来.正如文章开始提到的,笔者采用icomoon作为图标库, 我们可以在其官网上定制自己的图标,笔者大概选了40多了免费图标,项目中使用基本够用了.主要介绍一下他的具体功能:

  1. 可导入,下载,管理自己的图标库
  2. 可编辑图标,生成svg图标或者字体图标 当然国内的iconfont也非常优秀,大家可以多尝试.

我们将下载icomoon图标文件后,会有一个html的demo文件,里面有具体的使用方法和离线编辑功能,如下:

笔者在这里就不多做介绍了, 其次我们也可以类似于antd一样,将icon封装成react的组件, 这样用起来也非常方便.

最后

后续笔者将会继续实现modal(模态窗), alert(警告提示), drawer(抽屉), tooltip(工具提示条), Skeleton(骨架屏), Message(全局提示), 日期/日历等组件, 来复盘笔者多年的组件化之旅. 如果想学习更多H5游戏, webpacknodegulpcss3javascriptnodeJScanvas数据可视化等前端知识和实战,欢迎在公号《趣谈前端》加入我们一起学习讨论,共同探索前端的边界。

更多推荐

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

java将前端的json数组字符串转换为列表

记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang