傳統(tǒng)MVC框架的缺陷
什么是MVC?
Request->Controller->Model
···························->View
MAC的全名是Model View Controller,是模型(Model)、視圖(View)、控制器(controller)的縮寫,是一種軟件設(shè)計典范。
V即View視圖是指用戶看到并與之交互界面。
M即Model模型是管理數(shù)據(jù),很多業(yè)務(wù)邏輯都在模型中完成。在MVC中三個部件中,模型擁有最多的處理人物。
C即Controller控制器是指控制器接收用戶的輸入并調(diào)用模型和視圖去完成用戶的需求,控制器本身不輸出任何東西和做任何處理。他只是接受請求并決定調(diào)用哪個模型構(gòu)件去處理請求,然后在確定用哪個視圖來顯示返回的數(shù)據(jù)。
MVC只是看起來很美
MVC框架的數(shù)據(jù)流很立項,請求先到Controller,由Controller調(diào)用Model中的數(shù)據(jù)交給View進(jìn)行渲染,但是在實際的項目中,又是允許Model和View直接通信的。
Flux
在2013年,Facebook讓react兩項的同事退出了Flux架構(gòu)思想,React的初衷實際上是用來代替Jquery的,Flux實際上就可以用來代替Backbone.js、Ember.js等一系列MVC架構(gòu)和前端JS框架。
其實在Flux在react里的應(yīng)用就類似于vue中的vuex的作用,但是在vue中,vue是完整的MVVM框架,而vuex只是一個全局的插件。
react只是一個MVC中的V(視圖層),只管頁面的渲染,一旦有數(shù)據(jù)管理的時候,react本身的能力就不足以支撐復(fù)雜組件結(jié)構(gòu)的項目,在傳統(tǒng)的MVC中,就需要用到Model和Controller。Facebook對于當(dāng)時市面上的MVC框架并不滿意,于是就有了Flux,但Flux并不是一個MVC框架。
Action->Dispatcher->Store->View->Action->Dispatcher...
Flux
- View:視圖層
- ActionCreator(動作創(chuàng)造者):視圖層發(fā)出的消息(例如:onClick)
- Dispatcher(派發(fā)器):用來接收Actions、執(zhí)行回調(diào)函數(shù)
- Store(數(shù)據(jù)層):用來存放應(yīng)用的狀態(tài),一旦發(fā)生變動,就提醒Views要更新頁面
Flux的流程
- 組件獲取到store中保存的數(shù)據(jù)掛載在自己的狀態(tài)上
- 用戶產(chǎn)生了操作,調(diào)用actions的方法
- actions接收到了用戶的操作,進(jìn)行一系列的邏輯代碼、異步操作
- 然后actions回創(chuàng)建出對應(yīng)的action,action帶有標(biāo)識性的屬性
- actions調(diào)用dispatcher的dispatch方法將action傳遞給dispatcher
- dispatcher接收到action并根據(jù)標(biāo)識信息判斷之后,調(diào)用store的更改數(shù)據(jù)的方法
- store的方法被調(diào)用后,更改狀態(tài),并觸發(fā)自己的某一件事
- store更改狀態(tài)后事件被觸發(fā),該事件的處理程序會通知view去獲取最新的內(nèi)容
Redux
React只是DOM的一個抽象層,并不是Web應(yīng)用的完整解決方案。有兩個反面它沒有涉及到:
- 代碼結(jié)構(gòu)
- 組件之間的通信
2013年Facebook提出了Flux架構(gòu)的思想,引發(fā)了很多的實現(xiàn)。2015年,Redux出現(xiàn),將Flux于函數(shù)式編程結(jié)合起來,很短時間內(nèi)就成為了最熱門的前端架構(gòu)。
如果你不知道是否需要Redux,那就是不需要它。
還有遇到React實在解決不了的問題,你才需要Redux
簡單來說,如果你的UI層非常簡單,沒有很多互動,Redux就是不必要的,用了反而增加復(fù)雜性。
- 用戶的使用方式非常簡單
- 用戶之間沒有協(xié)作
- 不需要與服務(wù)器大量交互,也沒有使用WebSocket
- 視圖層(view)只從單一來源獲取數(shù)據(jù)
需要使用Redux的項目
- 用戶的使用方式復(fù)雜
- 不同身份的用戶有不同的使用方式(例如:普通用戶和管理員)
- 多個用戶之間可以協(xié)作
- 與服務(wù)器大量交互,或者使用了WebSoket
- view要從多個來源獲取數(shù)據(jù)
Redux的設(shè)計思想
- web應(yīng)用是一個狀態(tài)機,視圖與狀態(tài)是一一對應(yīng)。
- 所有的狀態(tài),保存在一個對象里(唯一數(shù)據(jù)源)
注意:flux、redux都不是必須和react搭配使用的,因為flux和redux是完整的架構(gòu),在學(xué)react的時候,只是將react的組件作為redux中的視圖層去使用了
Redux的使用的三大原則
- Single Source of Truth (唯一數(shù)據(jù)源)
- State is read-only (狀態(tài)是只讀的)
- Changes are made with pure function (數(shù)據(jù)的改變必須通過純函數(shù)完成)
自己實現(xiàn)一個Redux
基礎(chǔ)代碼
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>redux原理</title>
</head>
<body>
<div>
<button onclick="dispatch({type:'JIAN',n:2})">-</button>
<span id="countDisplay">10</span>
<button onclick="dispatch({type:'JIA',n:3})">+</button>
</div>
<script>
const countDisplay = document.querySelector("#countDisplay");
//保存數(shù)值
const countState = {
count: 5,
};
const renderCount = (state) => {
countDisplay.innerHTML = state.count;
};
renderCount(countState);
// const jian=()=>{
// countState.count=countState.count-1;
// renderCount(countState)
// }
/**
* 中控 來調(diào)用修改state和渲染頁面
* @param action 參數(shù)
* @param action.type 類型
* @param action.n 跳階
*/
const dispatch = (action) => {
changeState(action);
renderCount(countState)
};
const changeState=(action)=>{
switch (action.type) {
case "JIAN":
countState.count-=action.n;
break;
case "JIA":
countState.count+=action.n;
break;
default:
break;
}
};
</script>
</body>
</html>
以上這種方式countState變量是可以進(jìn)行改變的,但是我們想要的效果是countState中的所有的值不允許進(jìn)行改變,卻又想讓頁面上渲染的數(shù)值是正確的,即如下:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>redux原理</title>
</head>
<body>
<div>
<button onclick="store.dispatch({type:'JIAN',n:2})">-</button>
<span id="countDisplay">10</span>
<button onclick="store.dispatch({type:'JIA',n:3})">+</button>
</div>
<script>
const countDisplay = document.querySelector("#countDisplay");
//保存數(shù)值
const countState = {
count: 5,
};
//修改state的值
const changeState = (state, action) => {
if(!state){
return countState;
}
switch (action.type) {
case "JIAN":
return {
...state, //解構(gòu)出state中所有的屬性 并做返回 在此 return 后 changeState就為新的state就不再指向countState了
count: state.count - action.n
};
case "JIA":
return {
...state,
count: state.count + action.n
};
default:
return state;
}
};
/**
* 創(chuàng)建一個私有管理域
*/
const createStore = () => {
let state=null;
//返回全局的countState
const getState = () => state;
let listens = [];//保存事件
//
const dispatch = (action) => {
state = changeState(state, action);
listens.forEach(item => item());//執(zhí)行監(jiān)聽中的所有函數(shù)
};
const setListens = (listen) => {
if (Array.isArray(listen)) {
listen.forEach(item => {
listens.push(item)
})
} else {
listens.push(listen);
}
};
dispatch({});//默認(rèn)執(zhí)行一次空對象
return {
getState,
dispatch,
setListens
}
};
const store = createStore(changeState());
const renderCount = () => {
countDisplay.innerHTML = store.getState().count;
};
renderCount();
//首次加載的時候把renderCount監(jiān)聽存放到每次數(shù)值改變事件中 (觀察者模式)
store.setListens(renderCount)
</script>
</body>
</html>