单页应用开发总结

本文想通过自己这一年的单页应用开发经验,来对SPA的开发做一个总结。

页面开发模式

通常我们在开发页面时,都会拿到一份设计图,假设我们拿到一份这样的设计图

image.png

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

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

这样写其实没什么问题,把所有的细节全部隐藏在组件内部,每个组件只要处理好自己就OK。但是要知道,现如今页面都比较复杂,一般的单页应用都需要一个可靠的数据流去处理,否则在日后维护方面会难度巨大。这里假设我们使用了redux,在redux中,我们的数据都是从顶层往下传,一般以route页面为维度,去做connect,然后route页面将所需的store以及action以props的形式分发。那就相当于,整个页面只有route组件是智能组件,其他组件尽可能写成木偶组件。如果按照这样的开发模式去开发左侧关注列表组件,应该是这样的

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

而list组件应该可能是这样的

//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了。这其实也秉承了自上而下的设计风格。
我通常将组件分为四类

  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,后期难以维护,这无疑是本末倒置的。写这篇总结也是对我最近写项目的一些反思,希望能有更多的人一起讨论,谢谢。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,504评论 4 365
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,898评论 1 300
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,218评论 0 248
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,322评论 0 214
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,693评论 3 290
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,812评论 1 223
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,010评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,747评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,476评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,700评论 2 251
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,190评论 1 262
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,541评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,206评论 3 240
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,129评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,903评论 0 199
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,894评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,748评论 2 274

推荐阅读更多精彩内容