vant组件做表格_如何编写React组件-思考与最佳实践

vant组件做表格_如何编写React组件-思考与最佳实践_第1张图片

前言

在学习React的两个月内,我大部分时间反而在阅读vant-ui @vue3的源码,不得不说有了一些很大的帮助,不论是一些思想上的提升还是作用于项目中的体验,其实大部分都是借鉴而来的。在很多时候,在写一个组件时,大部分都是会进行拆分的,很少有会直接写在一个jsx或者是.vue文件中的。因此,组件的编写就变得非常的需要艺术性了。

我也开始反思过我自身的一些问题,以前也没有去考虑太多的组件性能和组件颗粒度的问题,现今不论是从业务还是其他方面都不得不去思考组件的方案了。QAQ。

其实到了这里很多朋友就要问了,为什么写React组件会去看vant的源码,这不是买哈密瓜去了西瓜摊子吗。在这里我说下原因,其实看vant的源码还是因为vue3的关系,主要是想看下composition和hook去写组件的差异化对比,从而去理解两个框架在不同方向上对业务逻辑的理解。整体阅读下来,其实composition组件和无状态组件写起来还是非常像的。唠叨话就不多说,进入正题吧。

一些不足之处或者说是错误点可以在评论区指点下作者。

考虑角度

在编写组件的时候,需要考虑的角度或多或少就那么几个,我们也都是在拆分,重组,拼接等几个方向去进行,其是总结起来在写组件的时候考虑点也可以总结成下面几点:

  • 组件分类
  • 耦合程度
  • 划分程度
  • 依赖程度

vant组件做表格_如何编写React组件-思考与最佳实践_第2张图片

组件分类

展示类组件

大多数时候,我们的组件可以分为以下几种,最常见使用的就是展示类和交互类组件了,绝大部分业务中,都是由两者组成的。举个例子,如下图是JD购物的首页的商品选项就是一个展示类组件,它并不具有很复杂的交互性,它唯一做的就是通过props的值进行数据的显示,并没有进行更多更多深入行的逻辑交互。因此它就相当于展示性组件。

vant组件做表格_如何编写React组件-思考与最佳实践_第3张图片

交互型组件

在举例子,JD购物的选择商品规格的界面就是一个典型的交互性组件,当你点击规格标签的同时,你的价格和已选中的数据会发生改变,且商品的状态也在实时判断,当商品缺货时,确认按钮就会禁止用户进行进一步的操作等等不同的状态。这就是一个交互型组件。

vant组件做表格_如何编写React组件-思考与最佳实践_第4张图片

数据型组件

对于不可感知的数据类组件一般都是由框架或者是库提供的,如现在react-router和react-redux都是一个数据类组件,它们会在web应用 开始时就准备好需要的数据,然后供后续使用。因此它是属于一个聚合渠道的一个组件。

HOC高阶组件

从高阶函数到高阶组件,大多数的时候,为了能够复用某些逻辑,往往都会使用到HOC,随着业务的不断扩展,产生的宽容性也需要组件来进行支持,如果说某个逻辑的组件反复被用到,那么这个时候,就需要根据其视图的差异性考虑进行高阶组件的一个拆分,不论是vue2的mixin还是custom hook,在多数都是为开发者提供简单的逻辑复用。

耦合程度

通过上述几种的组件方式,对于组件已经有了初步的认识,那么就到了组件的耦合度这块了,理想化的情况是在java中,我们都知道,一个类里面有很多方法,我们希望通过方法来描述这个类的行为模式和特征,因此大多数时候,一个方法通常只做一见事情,并不会过多的进行干预。同样的,在组件中,render函数非常的杂乱,如果没有稍加注释可能就会形成阅读死区,其他人如果没有文档,非常难看懂代码的含义,因此也就多了很多前人栽树,后人就真的凉的情况,好的组件库它的代码大部分都是解耦过的,我们可以根据不同的组件类型作用进行一定的组件解耦。

实例

在vant ui中,我觉得它们现在的组件拆分非常的清晰,刚好最近Taro框架碰到了组件库的问题,就以一个popup组件来进行实例。 代码中可以看到,Popup分为两个部分组成,一部分是内容,一部分是overflow遮罩,我们通过不同的render方法进行声明,最后整理在return当中,每一个render方法只做一个逻辑的处理或者是其他关联逻辑的引入,彼此之间不去干涉,那么其他人在阅读组件的时候,能够从功能块入手,快速理清逻辑,进行Bug Fix 或者是 feature。

import React from 'react'
import { View } from '@tarojs/components'

import VtIcon from '@/components/vt-icon'
import VtOverflow from '@/components/vt-overflow'
import { CSSTransition } from 'react-transition-group'

import classNames from 'classnames'
import PropTypes from 'prop-types'
import '@/styles/vt-popup.scss'

function VtPopUp (props) {
  const {
    customStyle,
    className,
    overlayClass,
    overlayStyle,
    children,
    overlay,
    show,
    zIndex,
    position,
    round,
    transition,
  } = props

  /**
   * overflow点击事件
   */
  const handelOverFlowClick = () => { 
    console.log('click overflow')
  }

  /**
   * 遮罩层
   */
  const renderOverlay = () => {
    if (overlay) {
      return (
        
      )
    }
  }

  /**
   * TODO: 关闭图标
   */
  const renderCloseIcon = () => {

  }

  /**
   * Popup组件
   */
  const renderPopup = () => {
    return (
      show ? 
        {children}
        {/* {renderCloseIcon()} */}
       : null
    )
  }

  const renderTransition = () => {
    const name = position === 'center' ? 
      'vt-fade' : 
      `vt-popup-slide-${position}`;

    return (
         
        {renderPopup()}
      
    )
  }
  
  return (
    <>
      { renderOverlay() }
      { renderTransition() }
    
  )
}

VtPopUp.propTypes = {
  className: PropTypes.string,
  customStyle: PropTypes.object,
  children: PropTypes.element,
  show: PropTypes.bool, // 显示popup
  zIndex: PropTypes.oneOfType([ // zIndex层级
    PropTypes.string,
    PropTypes.number,
  ]),
  duration: PropTypes.oneOfType([ // 动画时长
    PropTypes.string,
    PropTypes.number,
  ]),
  overlayClass: PropTypes.string, // 遮罩样式
  overlayStyle: PropTypes.object, // 遮罩样式
  overlay: PropTypes.bool, // 是否显示遮罩
  lockScroll: PropTypes.bool, 
  lazyRender: PropTypes.bool, // 懒加载
  closeOnClickOverlay: PropTypes.bool // 是否在点击遮罩层后关闭

}

VtPopUp.defaultProps = {
  show: false,
  overlay: true,
  zIndex: 2,
  position: 'center',
  lockScroll: true,
  lazyRender: true,
  closeOnClickOverlay: true,
}

export default VtPopUp

依赖程度

说完了组件的耦合,那么说下组件的划分吧。我们都知道组件有全局组件,布局组件这两种使用形式。大多时候都是拆成子组件进行一个重复调用,不同的组件或多或少都会依赖props的值进行传递,那么这个时候就会出现一个头疼的问题,组件都有自己的变量域,子组件有子组件的state,父组件有父组件的state,props传递的data大部分都来自父组件的props,

因此这里就需要考虑就是当父组件state发生了更新,这个state的依赖程度究竟有多深,相比于脏数据,依赖重的组件往往会因为蝴蝶扇动翅膀,缺发生了很大的变化。

因此我们在写组件中,适当的进行计算属性的包裹,防止因为其他原因产生的变化。让只需要更新的组件更新。因此,如果你的组件依赖计算非常的重,性能消耗大,还是老老实实使用Memo包一下吧。

const realInfo = useMemo(() => {
// 组件内容, 只有
}, [props.payid, props.openid])

总结一下:

  • 避免组件props过渡重量化,props越多(不是数据越多,是依赖越多)越容易发生组件频繁render。
  • 性能消耗大的组件可以适当使用Memo Hook记忆化数据
  • 大部分状态尽量在内部维护,而不是全部都传递下来
  • 管理组件状态,未知状态不要滥用。

总结

感觉全文都是一些理解性的概念,React资深的你或许用不到,但其实大部分受限于业务的同学都没有去考虑过组件的一些拆分,数据的流向。往往可以看到一个jsx文件可以是1000 ~ 1500行的规模,这个时候,其实就非常的臃肿了,开发者知道吗?知道,但受限于业务,基本没时间去处理过多的组件方案,慢慢的堆叠起来,代码就变得非常的糟糕,过一个月回来一看,自己本身都需要去理清一些思路才知道代码的含义。React如此,Vue也如此,最终却还是有很多人在高呼React比Vue性能好。但这本身就是一个很难去回答的问题,同样的工具,不同开发者使用的情况不一样,创造的价值也不一样。

大铁锹有人用它去挖地,而同样的有人却用它做菜。React就是这把铁锹,它只是一个工具,它本身并不带使用约束,一切的用途都是开发者自身去进行的。
觉得有帮助,可以点个赞。如果哪里写的不对,欢迎在评论区交流,共同学习。

你可能感兴趣的:(vant组件做表格)