说说MVVM的组件设计

这种组件设计的特点是,组件的所有state和action都来自props,组件自身没有状态,只负责展示UI层。model层完全交给全局状态管理库比如redux或mobx。不推荐这种组件设计,因为后期不好维护。这是典型的滥用全局状态管理库的现象。

什么叫滥用全局状态管理库?

就是没有认识到状态管理库的作用,或者说我们什么时候需要状态管理库?

拿 react 来说,react 是有组内状态的,状态可以通过 props 传递。但是,但当 app 比较庞大的时候,兄弟组件,远亲组件这些的交流就变得困难起来, 它们必须依赖相同的父组件来完成信息的传递。这时,就是我们使用状态管理库的时候。

但是,很多人把所有状态都往 redux 里面丢,虽然这方便了开发,但缺点却很明显:

  1. 组件很难复用:因为状态只有一份。
  2. 耦合度高:根据高内聚低耦合的设计原则,一个模块应该有独立的功能,不依赖外部,在内部实现复杂度,只暴露接口来与外界交流。但如果把组内的一些状态放在全局 model,就提供了让其他组件修改的能力,并且代码没有内聚。

非受控组件

划分好状态的等级,尽量把状态放在组件内。当遇到共享组内状态困难的场景时,提升状态到全局状态管理库。

这种组件,有view层、model层、services层。因为它是有独立功能的,然后通过向外界暴露api来提供自己的能力,同时把复杂度隐藏在内部。

例如一个列表组件:

// 方案一
// ListDemo.jsx
import React,{useEffect} from 'react';
import {getData} from 'services/api';

export default function ListDemo({requestId}){
  // model
  const [data,setData] = useState([]);
  const [visible,setVisible] = useState(false);

  useEffect(()=>{
    // services 层
    getData().then(data=>{
      setData(data)
    });
    /**
     * 当requestId变化时,列表会重新请求
     * 这里的requestId是组件向外界暴露的一个api
     **/
  },[requestId])

  useEffect(()=>{
    if(visible===true){
      // clearState
      setVisible(false);
    }
  },[requestId])

  return (
    // view
    <div>
      {
        data.map(item=><li>{item}</li>
      }
      {
        visible && (
          <div>
            this is a modal
          </div>
        )
      }
    </div>
    )
  )
}

// app.jsx
<ListDemo />
复制代码

这种组件设计的特点是,组件可以重置自身状态的时机是由自身控制的。如果你觉得这样麻烦,你可以把重置自身状态的时机交给外部,通过key来 “销毁组件”=>“重新渲染组件”。上面的代码可以简化成:

// 方案二
// ListDemo.jsx
import React,{useEffect} from 'react';
import {getData} from 'services/api';

export default function ListDemo({requestId}){
  // model
  const [data,setData] = useState([]);
  const [visible,setVisible] = useState(false);

  useEffect(()=>{
    // services 层
    getData().then(data=>{
      setData(data)
    });
  },[])

  return (
    // view
    <div>
      {
        data.map(item=><li>{item}</li>
      }
      {
        visible && (
          <div>
            this is a modal
          </div>
        )
      }
    </div>
    )
  )
}

// app.jsx
/*
*当requestId变化时,ListDemo会重新渲染
*/
<ListDemo key={requestId} />
复制代码

方案二的代码比较整洁,且出错率比方案一低,但是方案二存在重新渲染组件的一个环节,性能开支会比方案一多一点点(大部分情况你都可以忽略不计)。

很多情况下,我们应该采用方案二。

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章