React開發簡書總結

王大錘_code發表於2020-11-29

React 簡書網開發

github地址

技術棧

whatway
React建立元件
Redux管理資料
react-redux方便使用 redux
redux-thunk讓store有能力接收函式,用來做非同步資料獲取與複雜邏輯
immutable.js保證 redux 的 state 不被修改
react-router路由管理
react-loadable實現非同步元件,而不是所有程式碼都打包在一起
styled-components模組化 CSS
react-transition-group動態改變 class 屬性值實現 react 動畫

執行本專案

注意:這裡預設給您寫的是cnpm哦,如果不支援請修改第三行為 npm install

git clone https://github.com/Wangdachui110/jianshu.git
cd jianshu
cnpm install
npm start

我做了哪些功能?

首頁的樣式與佈局,沒有做響應式
頁面頭部搜尋欄的動畫與推薦
頁面頭部:登陸與退出的跳轉
頁面頭部:寫文章的許可權驗證,沒有登陸會跳到登陸頁面
登陸頁的簡單佈局。沒有驗證哈
文章詳情的跳轉,雖然點每一個跳過去都是同一篇文章,但傳送的ajax請求不同
注: 所有的mock資料放在puclic/api

專案是如何拆分的?

按照頁面進行拆分。 src/pages 目錄下的每一個資料夾代表一個頁面
每一個頁面的資料夾下管理它自己的資料與狀態 通過 combine-reducer ,可以將龐大的state分割到多個檔案中,在每一個代表頁面的資料夾中各自管理各自的資料,可以極大的降低合作成本,並讓程式碼清晰
將通用元件或一個不屬於某一個路由的元件如header部分放到src/common資料夾中,儘可能提高服用度
在src/App.js中管理路由
在src/store/reducer中合併所有的reducer

專案結構

│  App.js                         根元件
│  index.js                       專案入口
│  style.js                       注入到全域性的樣式, 重置預設樣式
├─store   
|      index.js                   唯一的store
|      reducer.js                 合併各元件的reducer
|
├─common                          通用元件資料夾
│  ├─header                       頭部元件,它獨立於頁面之外,卻又較為複雜,所以單獨抽出
│  │  │  index.js                 元件入口,佈局與邏輯
│  │  │  style.js                 元件使用標籤以及樣式
│  │  │
│  │  └─stroe                     header區域所有的資料
│  │          actionCreators.js   統一管理action
│  │          constants.js        用常量替代所有的action的type值
│  │          index.js            匯出actionCreators與reducer
│  │          reducer.js          根據不同的action修改state
│  │
│  └─tools                        存放所有的小元件
│      │  index.js                匯出所有的工具元件
│      │
│      ├─components               放置通用元件
│      │  └─backTop               回到頂部元件
│      │          index.js        
│      │          style.js        
│      │
│      └─store                    這裡打算用一個資料夾來管理所有通用元件的資料,↓
│              actionCreators.js  因為通用元件主要是樣式,邏輯是少數
│              contants.js
│              index.js
│              reducer.js
│
├─pages                           所有的頁面
│  │
│  ├─home                         主頁
│  │  │  index.js                 主頁入口
│  │  │  style.js                 主頁樣式
│  │  │
│  │  ├─components                首頁被劃分為下列元件
│  │  │      List.js              列表元件
│  │  │      Recommed.js          推薦元件
│  │  │      Topic.js             話題元件
│  │  │      Writer.js            作者元件
│  │  │
│  │  └─store
│  │          actionCreators.js   統一管理action
│  │          constains.js        用常量替代所有的action的type值
│  │          index.js            匯出actionCreators與reducer
│  │          reducer.js          根據不同的action修改state
│  │
│  ├─detail                       文章詳情頁面
│  │  │  index.js                 
│  │  │  loadable.js              使用loadable包裝元件,讓其可非同步載入程式碼
│  │  │  style.js
│  │  │
│  │  └─store
│  │          actionCreators.js
│  │          constains.js
│  │          index.js
│  │          reducer.js
│  │
│  ├─login                        登入頁
│  │  │  index.js
│  │  │  style.js
│  │  │
│  │  └─store
│  │          actionCreators.js
│  │          constains.js
│  │          index.js
│  │          reducer.js
│  │
│  └─write                        寫文章頁
│          index.js               沒有再往下寫了
│
└─statics                         靜態資原始檔
   │  logo.png                    
   │
   └─iconfont                     iconfont檔案,全是美美的圖示
           demo.css
           iconfont.css
           iconfont.eot
           iconfont.js
           iconfont.svg
           iconfont.ttf
           iconfont.woff

styled-components

使用 styled-components 有以下幾個好處:

  1. 零件化。每一個匯出的元件其實都是一個有樣式的標籤,可以方便的被元件使用。
  2. 可以像 sass 等一樣巢狀標籤
  3. 樣式程式碼與佈局程式碼分離
  4. 避免樣式衝突,他不會影響其他元件的樣式
  5. 方便的為標籤新增屬性

在 styled-components 中定義有 CSS 樣式的標籤、標籤屬性

export const HeaderWrapper = styled.div.attrs({
  class: 'header'
})`
  height: 58px;
  .somePart {
    color: red;
  }
`

在 styled-components 中使用圖片

圖片必須先使用 import 匯入,因為 webpack 會幫我們把專案打包 在使用的時候使用字串插槽,即 ${ } 的方式

import logoPic from '../../statics/logo.png'

export const Logo = styled.a`
  width: 100px;
  height: 56px;
  background: url(${logoPic});
`

React Transition Group

react-transition-group 會在動畫執行的不同時間點動態的新增一些 class 入場時: fade-enter fade-enter-active 離場時: slide-exit slide-exit-active

使用 combineReducer 對資料拆分管理

如果我們將專案中所有的 reducer 函式都放在一個檔案中的話有兩個很大的問題: 第一,reducer 函式會變得非常長; 第二,所有的 reducer 放在一起那麼合作的成本就會特別高,因為大家都在頻繁修改同一檔案。

使用 combineReducer 可以合併多個 reducer, 所以與 store 相連的 reducer 可以被簡化為下面這個樣子。

import { combineReducers } from 'redux'
import {reducer as headerReducer} from '../common/header/stroe'

export default combineReducers({
  header: headerReducer
})

combineReducer 能夠使得我們可以將 reducer 拆分,我們可以把 reducer 檔案放到元件對應資料夾下,讓一個元件資料夾就包含了整個元件所有資訊。 現在,我們就有能力在一個資料夾內就將元件的全都資訊描述完整

│  index.js   //容器元件
│  style.js   //CSS樣式 (這裡使用了styled-components,描述了標籤型別、標籤的樣式、標籤原生屬性)
│
└─stroe   //管理資料的資料夾
        index.js            //將actionCreator、reducer、constants匯出
        actionCreators.js   //管理action的產生
        constants.js        //管理action型別,把action的type值變成常量
        reducer.js          //管理整個元件涉及到的reducer

定義一個事件需要做那幾步?

  1. 在元件上掛載某事件的回撥函式(在 render 函式入口處使用解構運算子結構出這個函式)
  2. 在 mapDispatchToProps 中定義對應的函式
  3. 在 constants.js 中定義一個變數作為 aciton 的 type
  4. 在 actionCreators 中定義一個的 action。
  5. 在 mapDispatchToProps 中的函式中 dispatch 這個 action (這一步熟練後課合併到第二步)
  6. 在 reducer 的 switch 語句中新增對這個 action 的處理

immutable.js

immutable.js 是一個第三方的庫,它可以幫助我們生成一個 immutable 物件,這個物件是不可以被改變的。如果將 store 向 reducer 傳遞的 state 包裝成一個 immutable 物件的話就可以避免直接修改 state 這種情況的發生。

fromJS 可以把物件包裝為 immutable物件

const defaultState = fromJS({
  focused: false
})

set 方法 immutable 物件的 set 方法會拷貝之前 immutable 物件的值和設定的新值,返回一個全新的物件,他不會去真的修改原物件。

state.set('focused', true);

get 方法 immutable 物件不可以直接使用 “.” 或者 [] 來直接獲取物件值,必須通過 get 方法

let focused = header.get('focused')

getIn 方法

state.getIn(['header','focused'])
//等價於以下程式碼
state.get('header').get('focused')

redux-immutable

雖然使用了 immutable.js 讓 combineReducer 拆出來的元件的 reducer 中的 state 變成了 immutable 物件,但是整個專案的 store 還是一個普通的 JS 物件,使用 redux-immutable 就可以讓整個 store 變成一個 immutable 物件。 我們在直接連線 store 的 reducer 中用 redux-immutable 生成一個 combineReducers,用它替換 redux 自帶的 combineReducers

import { combineReducers } from 'redux-immutable'
import {reducer as headerReducer} from '../common/header/stroe'

export default combineReducers({
  header: headerReducer
})

immutable 中將資料設定為非同步資料 使用 fromJS 函式處理過的 state 物件是 immutable 物件,而非同步請求獲得的資料為普通的 JS 物件。 所以直接在把非同步資料提交到 reducer 中希望建立新的 state 的時候就會出問題。 所以,我們需要將非同步資料也先用 formJS 把它轉為 immutable 物件後在交給 reduer 函式。這一點需要注意

immutable 向陣列中新增元素 使用 concat 方法

state.set('articleList', state.get('articleList').concat(action.value))

merge 方法 merge 方法可以更方便的一次性合併多個屬性,並且合併的內容可以是原生的 JS 物件,而 set 方法在修改物件的時候必須要求心傳入的值是 immutable 物件

react-router-dom

使用 react-router-dom 來配置路由 文件地址:https://reacttraining.com/react-router/web/example/basic

import { BrowserRouter, Route } from 'react-router-dom'
class App extends Component {
  render() {
    return (
      <Provider store={store}>
        <div>
          <Header /> {/*這裡是一直出現在頁面中的header部分*/}
          <BrowserRouter>
            <div>
             {/*這裡是根據路由顯示的router部分*/}
              {/*exact表示完全匹配,不加這個的話 /detail 中也能匹配到/,就會讓其他頁面都展示出header */}
              <Route path="/" exact component={Home} />
              <Route path="/detail" exact component={Detail} />
            </div>
          </BrowserRouter>
        </div>
      </Provider>
    )
  }
}

PureComponent

為了提升頁面效能,我們每次都在元件中使用 shouldComponentUpdata 來判斷元件是否需要更新,但是這樣的程式碼是很重複的,所以我們引入 PureCompoent 來簡化這個操作

react-loadable 實現非同步元件

腳手架工具幫我們把一個專案的所有的程式碼都打包到一個 bundle.js 中,這導致在首頁載入的時候其實把所有的頁面都載入進來了,這樣的話第一次訪問網站的時候會顯得很卡,而且很多頁面雖然被載入進來了。 Loadable 是一個高階元件(簡單來說,就是把元件作為輸入的元件。高階函式就是把函式作為輸入的函式。在 React 裡,函式和元件有時是一回事),一個可以構建元件的函式(函式可以是元件),它可以很容易的在元件層面分割程式碼包。

簡單的使用方法: 注意: 在使用的時候就需要引入 loadble 包裝過的元件了

//用Loadable來包裝元件,讓頁面可以被非同步載入
import React from 'react' //用來解析JSX語法
import Loadable from 'react-loadable'

const LoadableBar = Loadable({
  loader: () => import('./'),
  loading() {
    return <div>Loading...</div>
  }
})

export default () => <LoadableBar />

當一個作為頁面的元件被 loadabel 包裝過之後,可能會出現路由相關的問題,因為在這個時候直接放在 Route 下面的這個元件已近從原來的元件變成了 loadable 包裝過的元件了,它不能直接獲取到路由裡面的資訊了。

<Route path="/detail/:id" exact component={Detail} />

所以我們需要在原來的元件中做一些處理讓他有能力獲得路由資訊。 使用 withRouter 包裝原來的這個元件,他就可以正常的執行了

import { withRouter } from 'react-router-dom'

export default withRouter(Detail)

相關文章