单页应用开发总结

本文想经过本身这一年的单页应用开发经验,来对SPA的开发作一个总结。react

页面开发模式

一般咱们在开发页面时,都会拿到一份设计图,假设咱们拿到一份这样的设计图redux

image.png
对于页面的开发,我老是遵循自上而下的设计模式去开发。在这里首先会把页面分为两部分,头部导航,和内容主体。内容主体又分为两部分左侧关注信息以及右侧的动态列表。若是按照这样分,咱们的组件编写会像以下这样设计模式

<Container>
  <Nav />
  <Body>
    <BodyLeft />
    <BodyRight />
  </Body>
</Container>

这样写其实没什么问题,把全部的细节所有隐藏在组件内部,每一个组件只要处理好本身就OK。可是要知道,现现在页面都比较复杂,通常的单页应用都须要一个可靠的数据流去处理,不然在往后维护方面会难度巨大。这里假设咱们使用了redux,在redux中,咱们的数据都是从顶层往下传,通常以route页面为维度,去作connect,而后route页面将所需的store以及action以props的形式分发。那就至关于,整个页面只有route组件是智能组件,其余组件尽量写成木偶组件。若是按照这样的开发模式去开发左侧关注列表组件,应该是这样的less

//BodyLeft
<Container>
  <TopNav />
  <List />
</Container>

而list组件应该多是这样的dom

//List
<Container>
  {data.map(item=>(
    <Item>{item.name}</Item>
  ))}
</Container>

这时若是route页面想传递什么信息给左侧列表,每次都要层层传递,少了一层,多了可能会有两三层,这样会写不少没用的信息,而且很不利于往后的维护,由于每次有更改,都要去改许多无用的地方(那些中间层)。
而我我的更倾向一种扁平化的组件设计风格。
仍是原来的页面,若是采用扁平化的设计模式,设计出来的组件结构是这样的模块化

//ps:组件命名随便取的
<Container>
  <Nav>
    <NavLeft />
    <NavRight />
  </Nav>
  <Body>
    <BodyLeft>
      <LeftTop />
      <LeftList />
    </BodyLeft>
    <BodyRight>
      <ArticleList />
    </BodyRight>
  </Body>
</Container>

首先最外层的布局是能够复用的。这也就意味着若是以后还有页面是这样,上下布局,下面又是左右布局的时候,能够拿来用。其次是数据的传递不须要像以前那样层层传递,能够直接传给想要的组件。函数

有人说route页面为何不能这样写组件化

<div>
  <Nav />
  <div>
    <div className="left">
      <div className="leftTop">
        ...
      </div>
      <List />
    </div>
    <div className="right">
      <ArticleList />
    </div>
  </div>
</div>

也就是说把以前的那些组件换成div不就行了。可是在组件设计哲学里,一般在connect的组件,也就是智能组件里,是不处理与view有关的东西,智能组件只处理数据和逻辑。而于视图相关的东西全放在木偶组件去处理。好处是只要是逻辑处理,就会去找智能组件,而界面样式之类,就会去找木偶组件,思路清晰,更低耦合高内聚。布局

组件

不少人对组件的理解是复用。其实组件化开发还有一个好处就是模块化。模块化能够将一个复杂的问题划分为多个,简单的问题去处理。打个比方你的能力一次只能处理一个七十分的问题,如今来了一个八十分的问题,你一次性很难处理,常常须要写一写,停顿一下,思考一会,而后再写一写,直至完成;相反若是你采用模块化的方式去解决,直接将这个八十分的问题划分为四个二十分的问题,此时,你只须要先搭建一个架子,让这个四个二十分的模块加起来能等于八十分,接着再去处理每一个二十分的问题就OK了。这其实也秉承了自上而下的设计风格。
我一般将组件分为四类this

  1. 智能组件

  2. 木偶组件

  3. 容器组件

  4. 高阶组件

项目若是使用redux,智能组件一般是做为connect的那个组件,他只处理该组件下全部子组件的数据逻辑;而样式,真实的dom结构,由木偶组件来负责,他一般是stateless function,偶尔也有本身的state,但即便是state,也只处理与页面展现有关的逻辑;容器组件很简单,若是把一个页面比做一我的,容器组件就是人的头,身体,和四肢。将页面大体分类,一般的写法是这样的

//Body
<div className="body">{this.props.children}</div>

ps:为何容器组件不直接写成div上文说过了。

若是说前三类组件都在为页面服务,那么最后一个组件就是专门为组件服务的组件。高阶组件就像高阶函数同样,将被修饰的组件做为参数,同时也能够传入其余配置参数,最后返回一个被加强的组件,redux的connect就是一个高阶组件,一般写法是这样的:

//高阶组件
const Hoc = props => WrapComponent => {
  return class extends Component {
    render() {
      return <WrapComponent {...props} />;
    }
  };
};
// 使用
class WrapComponent extends Component{
  render(){
    return <div />
  }
}
export default Hoc()(WrapComponent)

redux数据流

在redux数据流管理里,行业里有不少最佳实践,我这里就当抛砖引玉。
我认为通常页面逻辑不是很复杂的项目,简单的使用redux redux-thunk redux-action就够了,若是须要处理的请求很复杂,为了不回调地狱,可使用redux-saga。一般咱们在对数据从顶部往下分发的时候,须要以一个维度为基点来作connect,这个维度通常是route页面。对于router,如今也基本达成了共识,那就是router的数据状态也是redux数据的一种,它与view无关,所以使用react-redux-router将router与redux结合在一块儿是一个比较好的作法。
在redux里,有一个比较有争议的点是,关于页面的状态,是否要放在redux里。有人认为redux就应该只放数据,即后台的数据和部分前台本身存储的数据;可是我认为,咱们页面在开发过程当中,页面的展现逻辑一般与后台的数据是很是耦合的,可能一个按钮的状态,一个icon的颜色,都与后台的数据有关,那么若是强行拆分,就会变成对于一个流程,咱们要先去redux里处理后台数据,处理好以后,再去智能组件里根据redux数据处理内部state(页面的状态),这样很麻烦,也很难维护。我本身的作法是,每个流程,只对应一个action,这个action内部再去根据不一样的参数去处理不一样的数据,直至页面正常反应这个操做为止。

总结

总的来讲,咱们中不少人包括我本身,给view层赋予了太多的职能和责任,形成redux的价值没有发挥出来,view里跑着各类state,后期难以维护,这无疑是本末倒置的。写这篇总结也是对我最近写项目的一些反思,但愿能有更多的人一块儿讨论,谢谢。

相关文章
相关标签/搜索