【组件开发笔记】组件の组织

1、组件设计基本原则

单一职责。

也就是:

① 降低组件的复杂度,减少代码量,提升可读性。

② 降低与其他组件的耦合度(低耦合),从而降低变更导致的对功能的影响。

③ 提高可复用性,功能单一(高内聚),有明确的边界,不访问其他组件的内部细节,接口最小化,单向数据流...。

 

2、TIPs

性能:无状态函数组件 > 有状态函数组件 > class组件;

减少props传递;

抽取过多的条件控制流;

不过度优化;

 

3、分类

【展示组件】-【本质:视图】

通用的组件:开发时,要以“第三方组件库”的标准考虑其设计,不和任何项目的业务耦合

项目通用的组件:可多个组件共享,可能与业务有较低耦合程度

组件特有的组件库:与业务深度耦合,不能与其他组件共享。

 

【容器组件】-【本质:逻辑】

侧重业务处理,通常为高阶组件。

数据来源:直接请求接口或从外部传入。

然后展示完整的视图。

 

【有状态组件&无状态组件】

无状态组件完全由外部props控制状态。如果想要优化它的props映射,提升性能,可以使用React.memo避免无用渲染。

React.memo类似于React.PureComponent,两者的区别为:React.memo是用于函数组件的,React.PureComponent是用于类组件的。

React.memo的用法为:

const Component = ()=> {
    return (
        <div>嘎嘎嘎</div>
    )
}
const MemodFuncComponent = React.memo(Component)

  

React.PureComponent 的用法为:

import React from 'react';

class Test extends React.PureComponent {
    constructor(props) {
        super(props);
        this.state = {
            count: 0
        }
    }
    
    componentWillUpdate(nextProps, nextState) {
        console.log('componentWillUpdate')
    }
    
    componentDidUpdate(prevProps, prevState) {
        console.log('componentDidUpdate')
    }
    
    render() {
        return ( 
            <div> 
            { this.state.count } 
            <button onClick = {
                () => this.setState({ count: 1 })
            }>点击</button> 
            </div >
        );
    }
}

export default Test;

  

React在进行组件更新时,如果发现这个组件是一个PureComponent,它会将组件现在的state和props和其下一个state和props进行浅比较,如果它们的值没有变化,就不会进行更新。

类似于写了一个shouldComponentUpdate生命周期,判断nextProps和nextState是否有变化:

import React from 'react';

class Test extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            count: 0
        }
    }
    
    componentWillUpdate(nextProps, nextState) {
        console.log('componentWillUpdate')
    }
    
    componentDidUpdate(prevProps, prevState) {
        console.log('componentDidUpdate')
    }
    
    shouldComponentUpdate(nextProps, nextState) {
        // 如果没变化,就不渲染,否则重新渲染
        if (this.state.count === nextState.count) {
            return false
        }
        return true
    }
    
    render() {
        return ( 
            <div> 
            { this.state.count } 
            <button onClick = {
                () => this.setState({ count: 1 }) }>点我</button> 
            </div>
        );
    }
}

export default Test;        

  

【纯组件&非纯组件】

纯组件其实类似于纯函数的概念。

纯组件就是[单向],给定输入,就永远返回相同的输出,过程没有副作用与外部的状态依赖(props,state,context没有变化,则输出没有变化),也就代表着这个组件不需要重新渲染,从而提高性能收益。

而很多个组件组成的比较复杂的组件树,则需要外部去维护它的状态,然后通过注入依赖,展示相对应的视图,这样可以维持组件树的纯净性。比较典型的解决方案是Redux,它把组件树看做一个状态树,分离逻辑与视图,再配合异步处理,就可以实现单向的数据流。类似的还有Cycle.js,有空学习。(#^.^#) (但还是附个链接: http://cyclejs.cn/#-%E7%BB%84%E4%BB%B6%E5%8C%96)

 

【布局组件&内容组件】

内容组件通常被约束在布局组件的占位中。抽离开来可以避免互相影响,提高可维护性。

布局组件:Grid, Layout, HorizontalSplit……

内容组件:Button, Label, Input……

 

【表单组件】

沿用通用的属性,保证兼容,减少代码重复,使用方便。

组件受控。实际开发中非受控组件的场景非常少, 自定义组件应只提供完全受控表单组件,避免组件自身维护缓存状态。

 

4、目录

 

 大牛推荐的多页面目录:

src/
  components/       # 共享组件
  containers/
    Admin/          # 后台管理页面
      components/   # 后台特定的组件库
      LoginPage/
      index.tsx
      ...
    App/
      components/  # App特定的组件库
      LoginPage/   # App页面
      index.tsx
      stores.ts    # redux stores
    AnotherApp/    # 另外一个App页面
  hooks/
  ...
  app.tsx          # 应用入口
  anotherApp.tsx   # 应用入口
  admin.tsx        # 后台入口

  

 

 。。。。。。

还有一些没用过的目录划分方式,有空学习。(#^.^#)

 

 

 5、模块

每个目录都是一个模块,都应该有一个index的唯一出口文件来统一管理模块的导出(限定模块的可见性)

(除了utils文件夹,它只是一块模块命名空间,因为它目录下是互不相关或不同类型的文件)

 相对路径不超过两级(即.././

或者将相对路径转为绝对路径, 例如webpack中可以配置resolve.alias属性来实现(直接copy一下大佬的代码):

 

    ...
    resolve: {
      ...
      alias: {
        // 可以直接使用~访问相对于src目录的模块
        // 如 ~/components/Button
        '~': context,
      },
    }

 

  

然后就可以:import { hide } from '~/utils/dom';

 

 

6、组件拆分

①拆分为render方法,比如:

// 生成数据列表工具栏
  const renderToolBar = () => {
    const toolBarList: ReactNode[] = [
      <Btn key="showMetrics" type="primary" onClick={showMetrics}>
        自定义指标
      </Btn>,
      rowKey ==='key' && <DownloadEfforts columnsData={columnsData} totalList={totalList} tableInfo={tableInfo} searchParams={searchParams} key="downloadExcel" dataType="effect"/>,
      rowKey ==='materialId' && <DownloadEfforts columnsData={columnsData} totalList={totalList} tableInfo={tableInfo} searchParams={searchParams} key="downloadExcel" dataType="materialRank"/>,
      (rowKey === 'adId' || rowKey === 'creativeId') && <DownloadEfforts columnsData={columnsData} tableInfo={tableInfo} searchParams={searchParams} key="downloadExcel" dataType="relation"/>,
    ];
    return toolBarList;
  };


  return (
    <BasisTable
       ...
        operateToolBar={() => renderToolBar()}
      />
  )

  

但是这个方法只是在return处简化了代码,其实并没有真的实现组件的拆分。

当代码行数超过300这个阈值,就要进行组件进一步拆分。

 

②拆分为组件

  • 纯UI渲染拆分为组件
  • 纯逻辑内容拆分到hooks中 || 拆分到高阶组件中
  • 两者皆有:将相关视图&逻辑都抽离,形成一个独立组件

 

7、组件文档化

 

https://storybook.js.org/

推荐用storybook.js进行文档化,用过,还是挺好上手的,就不赘述了。

 

posted @ 2021-06-07 17:26  芝麻小仙女  阅读(73)  评论(0编辑  收藏  举报