1、傳統MVC框架的缺陷
模型(model)-視圖(view)-控制器(controller)的縮寫
V
即View視圖:用戶看到並與之交互的界面。
M
即Model模型是管理數據:很多業務邏輯都在模型中完成。在MVC的三個部件中,模型擁有最多的處理任務。
C
即Controller控制器:接受用戶的輸入並調用模型和視圖去完成用戶的需求,控制器本身不輸出任何東西和做任何處理。它只是接收請求並決定調用哪個模型構件去處理請求,然后再確定用哪個視圖來顯示返回的數據。
缺點
MVC框架的數據流很理想,請求先到Controller, 由Controller調用Model中的數據交給View進行渲染.
但是在實際的項目中,又是允許Model和View直接通信的。
2、Flux
在2013年,Facebook讓React
亮相的同時推出了Flux框架,React
的初衷實際上是用來替代jQuery
的,Flux
實際上就可以用來替代Backbone.js
,Ember.js
等一系列MVC
架構的前端JS框架。
其實Flux
在React
里的應用就類似於Vue
中的Vuex
的作用,但是在Vue
中,Vue
是完整的mvvm
框架,而Vuex
只是一個全局的插件。
React
只是一個MVC中的V(視圖層),只管頁面中的渲染,一旦有數據管理的時候,React
本身的能力就不足以支撐復雜組件結構的項目,在傳統的MVC
中,就需要用到Model和Controller。Facebook對於當時世面上的MVC
框架並不滿意,於是就有了Flux
, 但Flux
並不是一個MVC
框架,他是一種新的思想
- View: 視圖層
- ActionCreator(動作創造者):視圖層發出的消息(比如mouseClick)
- Dispatcher(派發器):用來接收Actions、執行回調函數
- Store(數據層):用來存放應用的狀態,一旦發生變動,就提醒Views要更新頁面
Flux的流程:
- 組件獲取到store中保存的數據掛載在自己的狀態上
- 用戶產生了操作,調用actions的方法
- actions接收到了用戶的操作,進行一系列的邏輯代碼、異步操作
- 然后actions會創建出對應的action,action帶有標識性的屬性
- actions調用dispatcher的dispatch方法將action傳遞給dispatcher
- dispatcher接收到action並根據標識信息判斷之后,調用store的更改數據的方法
- store的方法被調用后,更改狀態,並觸發自己的某一個事件
- store更改狀態后事件被觸發,該事件的處理程序會通知view去獲取最新的數據
3、Redux
注意:flux、redux都不是必須和react搭配使用的,因為flux和redux是完整的架構,在學習react的時候,只是將react的組件作為redux中的視圖層去使用了。
React 只是 DOM 的一個抽象層,並不是 Web 應用的完整解決方案。
有兩個方面,它沒涉及:
- 代碼結構
- 組件之間的通信
2013年 Facebook 提出了 Flux 架構的思想,引發了很多的實現。2015年,Redux 出現,將 Flux 與函數式編程結合一起,很短時間內就成為了最熱門的前端架構。
使用場景設計思路
不需要redux情況
- 用戶的使用方式非常簡單
- 用戶之間沒有協作
- 不需要與服務器大量交互,也沒有使用 WebSocket
- 視圖層(View)只從單一來源獲取數據
需要redux情況
- 用戶的使用方式復雜
- 不同身份的用戶有不同的使用方式(比如普通用戶和管理員)
- 多個用戶之間可以協作
- 與服務器大量交互,或者使用了WebSocket
- View要從多個來源獲取數據
從組件層面考慮,什么樣子的需要Redux:
- 某個組件的狀態,需要共享
- 某個狀態需要在任何地方都可以拿到
- 一個組件需要改變全局狀態
- 一個組件需要改變另一個組件的狀態
Redux的設計思想:
- Web 應用是一個狀態機,視圖與狀態是一一對應的。
- 所有的狀態,保存在一個對象里面(唯一數據源)。
Redux的使用的三大原則:
- store:單一數據源
整個應用的 state 被儲存在一棵對象結構中,並且這個對象結構只存在於唯一一個 store 中
不能直接去修改此數據源中的數據(數據深拷貝) - State:是只讀的
state redux中的state
唯一改變 state 的方法就是觸發 action,action 是一個用於描述已發生事件的普通對象。
action只能是一個對象 - reducer:使用純函數(reducer)來執行修改
為了描述 action 如何改變 state tree ,你需要編寫 reducer,reducer只是一些純函數,它接收先前的 state 和 action,並返回新的 state
Redux框架的使用
Reducer必須是一個純函數
純函數遵守以下一些約束。
- 不得改寫參數
- 不能調用系統 I/O 的API
- 不能調用Date.now()或者Math.random()等不純的方法,因為每次會得到不一樣的結果
步驟1:創建統一數據源
- 引入redux 生成createStore
- 創建默認數據 defaultState
- 創建 reduces(state,action){}
- createStore(reduces)創建數據源
//引入創建倉庫方法
import {createStore} from 'redux'
//默認數據源數據,不能直接修改
const defaultStore={
count:1
}
//reducer出函數
function reducers(state=defaultStore,action){
if(action.type=='incr'){
return{
count:state.count+1
}
}
return state
}
//創建唯一倉庫
const store = createStore(
reducers,
)
export default store
步驟2:組件中獲取數據並設置數據
- 獲取數據 store.getState()
- 訂閱數據 store.subscribe(()=>{this.setState(state=>store.getState())})
- 派發任務 store.dispatch(actionCreator)
import React,{Component} from 'react'
import store from './store'
export default class App extends Component{
constructor(props){
super(props)
this.state = store.getState()
store.subscribe(()=>{
this.setState(state=>store.getState())
})
}
render(){
return(
<div>
<button onClick={this.incr}>++</button>
</div>
)
}
incr=()=>{
const actionCreator=>{
type:'incr',
payLoad:1
}
store.dispatch(actionCreator)
}
}
划分reducer
原因:一個應用只有一個state,單個reducer對數據操作很臃腫,so需要按照不同功能去拆分
注意:
- 分離reducer的時候,每一個reducer維護的狀態都應該不同
- 通過store.getState獲取到的數據也是會按照reducers去划分的
- 划分多個reducer的時候,默認狀態只能創建在reducer中,因為划分reducer的目的,就是為了讓每一個reducer都去獨立管理一部分狀態
react-redux(redux擴展庫)
React-Redux是Redux的官方針對React開發的擴展庫。
你可以理解為react-redux就是redux給我們提供一些高階組件
安裝
npm i -S redux react-redux
兩個核心的api
- Provider: 提供store
根據單一store原則 ,一般只會出現在整個應用程序的最頂層。 - connect: 用於連接容器組件和展示組件
語法格式為:
connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?)(component)
一般來說只會用到前面兩個,它的作用是:- 把
store.getState()
轉化為展示組件的props
- 把
actionCreators
轉化為展示組件props
上的方法
- 把
使用
步驟1:定義Provider
- 主程序index.js中定義Provider
- 讓全局的組件共享store中的數據
import React from 'react' import ReactDOM from 'react-dom' import { Provider } from 'react-redux' import store from './store' import App from './App' ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') )
步驟2:子程序中使用connect
- 把
store.getState()
轉化為展示組件的props
- 把
actionCreators
轉化為展示組件props
上的方法
- 傳統使用方式
- 裝飾器使用方式(推薦使用)
傳統使用方式
import React ,{Component} from 'react'
import {connect} from 'react-redux'
import * as actions frm './countAction'
class App exteds Component{
render(){
return (
<div>
{this.props.count}
<button onClick={this.incr}>++</button>
</div>
)
}
incr=()=>{
this.props.incr()
}
}
const mapStateProps=state=>{
return {count:state.count}
}
const mapPropsToDIspatch=dispatch=>{
return{
incr(){
dispatch(actions.incr())
}
}
}
export default connect(mapStateProps,mapPropsToDIspatch)(App)
裝飾器使用方式(推薦使用)
import React ,{Component} from 'react'
import {connect} from 'react-redux'
import * as actions frm './countAction'
const mapStateProps=state=>{
return {count:state.count}
}
const mapPropsToDIspatch=dispatch=>{
return{
incr(){
dispatch(actions.incr())
}
}
}
@connect(mapStateProps,mapPropsToDIspatch)
class App exteds Component{
render(){
return (
<div>
{this.props.count}
<button onClick={this.incr}>++</button>
</div>
)
}
incr=()=>{
this.props.incr()
}
}
Redux異步操作(redux-thunk)
通常情況下,action只是一個對象,不能包含異步操作,這導致了很多創建action的邏輯只能寫在組件中,代碼量較多也不便於復用,同時對該部分代碼測試的時候也比較困難.
常見的異步庫:
- Redux-thunk
- Redux-saga
- Redux-effects
- Redux-side-effects
- Redux-loop
- Redux-observable
基於Promise的異步庫:
- Redux-promise
- Redux-promises
- Redux-simple-promise
- Redux-promise-middleware
安裝
npm i -S redux-thunk
使用
在createStore實例store中使用
import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
import reducer from './countReducer'
const store = createStore(
reducer,
applyMiddleware(thunk)
)
export default store
countReducer.js
const incrAction = num=>({
type:'incr',
payload:num
})
export const incr=>90=>dispatch=>{
setTimeout(()=>{
let num=10
dispatch(incrAction(num))
},1000)
}