资讯专栏INFORMATION COLUMN

React组件拆分之道

terasum / 929人阅读

摘要:划分标准根据稿,不同的展示模块分为不同的组件。比如顶部底部导航列表等容器组件业务组件与数据源后台本地存储进行数据传输操作不止是划分标准根据业务功能划分。最常见的是列表组件。

为什么要拆分组件

提高可读性、可维护性

如果不拆分

代码量大,所有内容集中在一起

相同组件无法复用

业务开发分工不明确,开发人员要关心非业务的代码

改代码时,可能会影响其他业务,牵一发动全身(耦合)

任何一个操作都导致整个应用重新render

目标

架构清晰

相同组件能够复用

业务分工明确,开发人员仅专注与自己的业务

每个组件负责独立的功能,与其他组件解耦合

可使用SCU、memo减少不必要渲染

如何拆分组件

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

横向(按业务、功能模块划分)

纵向(应用、系统层级划分)

一个React组件的功能

维护局部数据: state、ref、后台返回等

获取、修改全局数据

事件处理、数据监听处理(useEffect/componentDidUpdate等)

IO: 网络请求/本地读写

数据处理

render

组件分类 展示组件

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

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

容器组件 业务组件

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

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

连接组件

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

组件树结构

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

案例 逻辑、展示分离

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

不拆分

登录组件处理了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 (
      
this.handleAccountChange(...args)} /> this.handlePasswordChange(...args)} />
) } }
拆分后

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

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

// 业务组件 可复用性比较高
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 (
          
        )
      }
    }
    return LoginContainer
  }
}

// 展示组件
class Login extends Component {
  render() {
    const { account, password, onAccountChange, onPasswordChange, onSubmit }
    return (
      
onAccountChange(...args)} /> onPasswordChange(...args)} />
) } } // 连接组件 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 (
      
    { items.map((item, index) => { const {text, id} = item const selected = this.state.selected === id return (
  • this.handleClick(id)} > {text}
  • ) }) }
) } }
拆分后

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

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

// onClick时需要的参数,要传进来
class Item extends PureComponent {
  render() {
    const { id, text, selected, onClick } = this.props
    return (
      
  • {text}
  • ) } } class List extends Component { state = { selected: null } handleClick(id) { this.setState({selected: id}) } render() { const { items } = this.props return (
      { items.map((item, index) => { const {text, id} = item return ( ) }) }
    ) } }

    文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

    转载请注明本文地址:https://www.ucloud.cn/yun/105142.html

    相关文章

    • React 应用设计之道 - curry 化妙用

      摘要:右侧展现对应产品。我们使用命名为的对象表示过滤条件信息,如下此数据需要在组件中进行维护。因为组件的子组件和都将依赖这项数据状态。化应用再回到之前的场景,我们设计化函数,进一步可以简化为对于的偏应用即上面提到的相信大家已经理解了这么做的好处。 showImg(https://segmentfault.com/img/remote/1460000014458612?w=1240&h=663...

      sewerganger 评论0 收藏0
    • React 应用设计之道 - curry 化妙用

      摘要:右侧展现对应产品。我们使用命名为的对象表示过滤条件信息,如下此数据需要在组件中进行维护。因为组件的子组件和都将依赖这项数据状态。化应用再回到之前的场景,我们设计化函数,进一步可以简化为对于的偏应用即上面提到的相信大家已经理解了这么做的好处。 showImg(https://segmentfault.com/img/remote/1460000014458612?w=1240&h=663...

      LinkedME2016 评论0 收藏0
    • 王下邀月熊_Chevalier的前端每周清单系列文章索引

      摘要:感谢王下邀月熊分享的前端每周清单,为方便大家阅读,特整理一份索引。王下邀月熊大大也于年月日整理了自己的前端每周清单系列,并以年月为单位进行分类,具体内容看这里前端每周清单年度总结与盘点。 感谢 王下邀月熊_Chevalier 分享的前端每周清单,为方便大家阅读,特整理一份索引。 王下邀月熊大大也于 2018 年 3 月 31 日整理了自己的前端每周清单系列,并以年/月为单位进行分类,具...

      2501207950 评论0 收藏0

    发表评论

    0条评论

    最新活动
    阅读需要支付1元查看
    <