React组件拆分之道

栏目: IOS · Android · 发布时间: 4年前

内容简介:提高可读性、可维护性把相关联的东西放一起(按功能、业务)只有render方法、简单的交互事件处理和state管理。比如Input/CheckBox等。

为什么要拆分组件

提高可读性、可维护性

如果不拆分

  • 代码量大,所有内容集中在一起
  • 相同组件无法复用
  • 业务开发分工不明确,开发人员要关心非业务的代码
  • 改代码时,可能会影响其他业务,牵一发动全身(耦合)
  • 任何一个操作都导致整个应用重新render

目标

  • 架构清晰
  • 相同组件能够复用
  • 业务分工明确,开发人员仅专注与自己的业务
  • 每个组件负责独立的功能,与其他组件解耦合
  • 可使用SCU、memo减少不必要渲染

如何拆分组件

把相关联的东西放一起(按功能、业务)

  • 横向(按业务、功能模块划分)
  • 纵向(应用、系统层级划分)

一个React组件的功能

  • 维护局部数据: state、ref、后台返回等
  • 获取、修改全局数据
  • 事件处理、数据监听处理(useEffect/componentDidUpdate等)
  • IO: 网络请求/本地读写
  • 数据处理
  • render

组件分类

展示组件

只有render方法、简单的交互事件处理和state管理。比如Input/CheckBox等。

划分标准: 根据UI稿,不同的展示模块分为不同的组件。比如顶部、底部、导航、列表等

容器组件

业务组件

与数据源(redux/后台/本地存储)进行数据传输操作(不止是IO)

划分标准: 根据业务功能划分。比如登录、登出、支付、表单校验等

连接组件

连接业务组件和展示组件, 主要用于处理数据后传给展示组件。

组件树结构

展示组件内可以有容器组件,容器组件内也可以有展示组件

React组件拆分之道

案例

逻辑、展示分离

把渲染和功能拆分成不同组件,提高复用性

不拆分

登录组件处理了2件事情:

  1. 渲染登录表单
  2. 记录用户输入和登录状态,向后台发送登录请求
class Login extends Component {
  constructor(props) {
    super(props)

    this.state = {
      account: '',
      password: '',
      status: 'init',
    }
  }

  handleAccountChange(e) {
    this.setState({account: e.target.value})
  }

  handlePasswordChange(e) {
    this.setState({password: e.target.value})
  }

  handleLoginClick() {
    this.setState({ status: 'ing' })
    request('/login', {
      params: {
        account: this.state.account,
        password: this.state.password,
      }
    }).then(() => {
      this.setState({status: 'succ'})
    }).catch(() => {
      this.setState({status: 'fail'})
    })
  }

  render() {
    return (
      <div>
        <input
          placeholder="账号"
          value={this.state.account}
          onChange={(...args) => this.handleAccountChange(...args)}
        />
        <input
          placeholder="密码"
          value={this.state.password}
          onChange={(...args) => this.handlePasswordChange(...args)}
        />
        <button onClick={() => this.handleLoginClick()}>登录</button>
      </div>
    )
  }
}

拆分后

容器组件负责实现登录功能,展示组件负责渲染内容。

如果要实现另一套登陆组件时,可直接复用容器组件,只需要实现新的展示组件即可。

// 业务组件 可复用性比较高
function withLogin(config) {
  const { mapStateToProps, mapDispatchToProps } = config
  return (Comp) => {
    class Container extends Component {
      constructor(props) {
        super(props)

        this.state = {
          account: '',
          password: '',
          status: 'init',
        }
      }

      handleAccountChange = (e) => {
        this.setState({account: e.target.value})
      }

      handlePasswordChange = (e) => {
        this.setState({password: e.target.value})
      }

      handleLoginClick = () => {
        this.setState({ status: 'ing' })
        request('/login', {
          params: {
            account: this.state.account,
            password: this.state.password,
          }
        }).then(() => {
          this.setState({status: 'succ'})
        }).catch(() => {
          this.setState({status: 'fail'})
        })
      }

      render() {
        const propsFromState = mapStateToProps(this.state, this.props)
        const propsFromDispatch = mapDispatchToProps({
          onAccountChange: this.handleAccountChange,
          onPasswordChange: this.handlePasswordChange,
          onSubmit: this.handleLoginClick,
        }, this.props)
        return (
          <Comp
            {...this.props}
            {...propsFromState}
            {...propsFromDispatch}
          />
        )
      }
    }
    return LoginContainer
  }
}

// 展示组件
class Login extends Component {
  render() {
    const { account, password, onAccountChange, onPasswordChange, onSubmit }
    return (
      <div>
        <input
          placeholder="账号"
          value={account}
          onChange={(...args) => onAccountChange(...args)}
        />
        <input
          placeholder="密码"
          value={password}
          onChange={(...args) => onPasswordChange(...args)}
        />
        <button onClick={() => onSubmit()}>登录</button>
      </div>
    )
  }
}

// 连接组件
const LoginContainer = withLogin({
  mapStateToProps: (state, props) => {
    return {
      account: state.account,
      password: state.password,
    }
  },
  mapDispatchToProps: (dispatch, props) => {
    return {
      onAccountChange: dispatch.onAccountChange,
      onPasswordChange: dispatch.onPasswordChange,
      onSubmit: dispatch.Submit,
    }
  }
})

渲染优化

把UI上相互独立的部分,划分成不同组件,防止渲染时相互影响。最常见的是列表组件。

拆分前

点击一个li, 其他li全都重新渲染

class List extends Component {
  state = {
    selected: null
  }

  handleClick(id) {
    this.setState({selected: id})
  }

  render() {
    const { items } = this.props
    return (
      <ul>
      {
        items.map((item, index) => {
          const {text, id} = item
          const selected = this.state.selected === id
          return (
            <li
              key={id}
              className={selected ? 'selected' : ''}
              onClick={() => this.handleClick(id)}
            >
              <span>{text}</span>
            </li>
          )
        })
      }
      </ul>
    )
  }
}

拆分后

子组件使用 PureComponentmemo ,并且click事件回调函数直接使用 this.handleClick ,而不是每次都创建新函数。

点击li,最多只会有2个子组件渲染。

// onClick时需要的参数,要传进来
class Item extends PureComponent {
  render() {
    const { id, text, selected, onClick } = this.props
    return (
      <li
        className={selected ? 'selected' : ''}
        onClick={onClick(id)}
      >
        <span>{text}</span>
      </li>
    )
  }
}

class List extends Component {
  state = {
    selected: null
  }

  handleClick(id) {
    this.setState({selected: id})
  }

  render() {
    const { items } = this.props
    return (
      <ul>
      {
        items.map((item, index) => {
          const {text, id} = item
          return (
            <Item
              key={id}
              id={id} // 传进去
              selected={this.state.selected === id}
              text={text}
              onClick={this.handleClick}
            />
          )
        })
      }
      </ul>
    )
  }
}

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

创新公司

创新公司

[美]艾德·卡特姆、埃米·华莱士 / 靳婷婷 / 中信出版社 / 2015-2 / 49.00元

●《玩具总动员》《海底总动员》《机器人瓦力》《飞屋环游记》等14部脍炙人口的动画长片, 近30次奥斯卡奖, 7部奥斯卡最佳动画长片,7次金球奖; ●几乎每一部电影一上映都位居票房榜首,所有电影都曾进入影史票房总榜前50,每一部电影都是商业与艺术的双赢。 ●即便新兴动画公司不断涌现,皮克斯始终保持动画界的王者之位,这一切背后的秘密就在于:不断推动创新的创意管理方式。 你可以从本书......一起来看看 《创新公司》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具