传统MVC框架的缺陷
什么是MVC?
Request->Controller->Model
···························->View
MAC
的全名是Model View Controller
,是模型(Model
)、视图(View
)、控制器(controller
)的缩写,是一种软件设计典范。
V
即View
视图是指用户看到并与之交互界面。
M
即Model
模型是管理数据,很多业务逻辑都在模型中完成。在MVC中三个部件中,模型拥有最多的处理人物。
C
即Controller
控制器是指控制器接收用户的输入并调用模型和视图去完成用户的需求,控制器本身不输出任何东西和做任何处理。他只是接受请求并决定调用哪个模型构件去处理请求,然后在确定用哪个视图来显示返回的数据。
MVC只是看起来很美
MVC
框架的数据流很立项,请求先到Controller
,由Controller
调用Model
中的数据交给View
进行渲染,但是在实际的项目中,又是允许Model
和View
直接通信的。
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
框架。
Action->Dispatcher->Store->View->Action->Dispatcher...
Flux
- View:视图层
- ActionCreator(动作创造者):视图层发出的消息(例如:onClick)
- Dispatcher(派发器):用来接收Actions、执行回调函数
- Store(数据层):用来存放应用的状态,一旦发生变动,就提醒Views要更新页面
Flux的流程
- 组件获取到store中保存的数据挂载在自己的状态上
- 用户产生了操作,调用actions的方法
- actions接收到了用户的操作,进行一系列的逻辑代码、异步操作
- 然后actions回创建出对应的action,action带有标识性的属性
- actions调用dispatcher的dispatch方法将action传递给dispatcher
- dispatcher接收到action并根据标识信息判断之后,调用store的更改数据的方法
- store的方法被调用后,更改状态,并触发自己的某一件事
- store更改状态后事件被触发,该事件的处理程序会通知view去获取最新的内容
Redux
React只是DOM的一个抽象层,并不是Web应用的完整解决方案。有两个反面它没有涉及到:
- 代码结构
- 组件之间的通信
2013年Facebook提出了Flux架构的思想,引发了很多的实现。2015年,Redux出现,将Flux于函数式编程结合起来,很短时间内就成为了最热门的前端架构。
如果你不知道是否需要Redux,那就是不需要它。
还有遇到React实在解决不了的问题,你才需要Redux
简单来说,如果你的UI层非常简单,没有很多互动,Redux就是不必要的,用了反而增加复杂性。
- 用户的使用方式非常简单
- 用户之间没有协作
- 不需要与服务器大量交互,也没有使用WebSocket
- 视图层(view)只从单一来源获取数据
需要使用Redux的项目
- 用户的使用方式复杂
- 不同身份的用户有不同的使用方式(例如:普通用户和管理员)
- 多个用户之间可以协作
- 与服务器大量交互,或者使用了WebSoket
- view要从多个来源获取数据
Redux的设计思想
- web应用是一个状态机,视图与状态是一一对应。
- 所有的状态,保存在一个对象里(唯一数据源)
注意:flux、redux都不是必须和react搭配使用的,因为flux和redux是完整的架构,在学react的时候,只是将react的组件作为redux中的视图层去使用了
Redux的使用的三大原则
- Single Source of Truth (唯一数据源)
- State is read-only (状态是只读的)
- Changes are made with pure function (数据的改变必须通过纯函数完成)
自己实现一个Redux
基础代码
<!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");
//保存数值
const countState = {
count: 5,
};
const renderCount = (state) => {
countDisplay.innerHTML = state.count;
};
renderCount(countState);
// const jian=()=>{
// countState.count=countState.count-1;
// renderCount(countState)
// }
/**
* 中控 来调用修改state和渲染页面
* @param action 参数
* @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变量是可以进行改变的,但是我们想要的效果是countState中的所有的值不允许进行改变,却又想让页面上渲染的数值是正确的,即如下:
<!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");
//保存数值
const countState = {
count: 5,
};
//修改state的值
const changeState = (state, action) => {
if(!state){
return countState;
}
switch (action.type) {
case "JIAN":
return {
...state, //解构出state中所有的属性 并做返回 在此 return 后 changeState就为新的state就不再指向countState了
count: state.count - action.n
};
case "JIA":
return {
...state,
count: state.count + action.n
};
default:
return state;
}
};
/**
* 创建一个私有管理域
*/
const createStore = () => {
let state=null;
//返回全局的countState
const getState = () => state;
let listens = [];//保存事件
//
const dispatch = (action) => {
state = changeState(state, action);
listens.forEach(item => item());//执行监听中的所有函数
};
const setListens = (listen) => {
if (Array.isArray(listen)) {
listen.forEach(item => {
listens.push(item)
})
} else {
listens.push(listen);
}
};
dispatch({});//默认执行一次空对象
return {
getState,
dispatch,
setListens
}
};
const store = createStore(changeState());
const renderCount = () => {
countDisplay.innerHTML = store.getState().count;
};
renderCount();
//首次加载的时候把renderCount监听存放到每次数值改变事件中 (观察者模式)
store.setListens(renderCount)
</script>
</body>
</html>
网友评论