單頁應用開發總結

本文想經過本身這一年的單頁應用開發經驗,來對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,後期難以維護,這無疑是本末倒置的。寫這篇總結也是對我最近寫項目的一些反思,但願能有更多的人一塊兒討論,謝謝。

相關文章
相關標籤/搜索