MVC软件架构
MVC的全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的
缩写,是一种软件设计典范。
V即View视图是指用户看到并与之交互的界面。
M即Model模型是管理数据 ,很多业务逻辑都在模型中完成。在MVC的三个部件中,模
型拥有最多的处理任务。
C即Controller控制器是指控制器接受用户的输入并调用模型和视图去完成用户的需求,
控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去
处理请求,然后再确定用哪个视图来显示返回的数据。
MVC架构的缺陷
MVC框架的数据流很理想,请求先到Controller, 由Controller调用Model中的数据交给
View进行渲染,但是在实际的项目中,又是允许Model和View直接通信的。然后就出现
了这样的结果:
(1)controller view的任务过轻,而主要的业务处理都压在Model身上
(2)Controller 和 view之间没有任何的联系
facebook 开发团队就看不上MVC ,所以导致React没有采用MVC架构思想,React单纯
只能看做是MVC中V;作为一个完整的软件架构,那么需要包含另外两个部分,所以
React需要补全这两个部分,在React发布的同时,facebook团队发布了一个软件架构思
维: Flux。
在这Flux软件架构思维中,React只是其中一部分,充当V,后期Flux发展改进,诞生了
一个新的架构: Redux,Redux也存在不便利性,为了弥补Redux的缺陷,之后又开发
了mobx。
Flux Redux 都是和MVC MVP MVVM 一样是架构层级。
错误的说法: Flux Redux是React的状态管理工具
正确说法: Flux Redux是一种架构思维,是可以进行状态管理的一个工具,也是一个集
中式的存储仓库
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框架,他是一种新的思想( 新的架构思想 )。
Flux 组成部分:
View: 视图层 ( 用React的组件来代替 )
ActionCreators(动作创造者【事件】):视图层发出的消息(比如mouseClick)
Dispatcher(派发器):用来接收Actions、执行回调函数
Store(数据层):用来存放应用的状态,一旦发生变动,就提醒Views要更新页面
Flux的流程:
(1)组件获取到store中保存的数据挂载在自己的状态上
(2)用户产生了操作,调用actions的方法
(3)actions接收到了用户的操作,进行一系列的逻辑代码、异步操作,然后actions会
创建出对应的action,action带有标识性的属性,actions调用dispatcher的dispatch方法将
action传递给dispatcher
(4)dispatcher接收到action并根据标识信息判断之后,调用store的更改数据的方法
(5)store的方法被调用后,更改状态,并触发自己的某一个事件
(6)store更改状态后事件被触发,该事件的处理程序会通知view去获取最新的数据
Flux在项目中的具体操作:
以一个计数案例说明Flux的具体使用。
要想使用FLux架构思维,需要通过一个工具进行使用, 这个工具就是flux
(1)安装 flux
$ yarn add flux
(2)在src目录下新建store目录,里面新建index.js
store有两个功能
● 存储数据
当数据发生改变时,视图要进行更新 ( 当前组件中的state发生了改变,从新从store中
获取数据,要想重新复制,那么要通过事件的发布,订阅【通过node中的events模块获
取事件的发布on和订阅emit】 )
const EventEmitter = require( 'events' ).EventEmitter//拿到node中的事件
const store = {
...EventEmitter.prototype,//解构事件对象原型上面的方法,主要是为了获取on()和emit()
//这样store上面就有on和emit方法【具备事件的订阅和发布的能力】
//最终的目的是更新视图
state: {//定义一条数据,用来存储管理数据
count: 0
},
getState () {//相当于一个辅助工具,用于获取store中的数据
return this.state
}
}
//store.on('aa',function(){//事件的订阅和发布的过程,自定义事件
//console.log(1)
//})
//store.emit('aa')
export default store
● 将store中的数据显示在组件(视图)中
import store from './store'//引入store
console.log(store)//输出store我们发现该对象下面有很多的方法,就包括定义在store文件夹中的index.js中的getStore()方法
class xxx extends React.Component{
constructor () {
super()
this.state = {
count: store.getState().count//在该组件定义一条数据,用来存放从store获取的数据
}
}
render () {
return (
<div>
<p> { this.state.count } </p>//展示数据
</div>
)
}
}
(3)用户操作,用户点击按钮,执行当前组件中的方法,这个方法的逻辑实际上是
actionCreators中的方法
(4)在store文件夹下创建actionCreators.js
● 用于用户交互,也就是所谓的事件
● 创建一个动作,然后发送给dispatcher
import * as type from './type'
import dispatcher from './dispatcher';
const actionCreators = {
increment () {
// 创建动作
let actions = {//有类型type和负载数据
type: type.INCRMENT
}
// 发送动作,必须由dispatcher亲自发送,dispatcher来通过dispatch 发送actions
dispatcher.dispatch( actions )
}
}
export default actionCreators
(5)在store文件夹下创建dispatcher.js
动作的触发者,用来修改数据
import { Dispatcher } from 'flux';
import * as type from './type'
import store from './index'
const dispatcher = new Dispatcher()
//dispatcher必须先注册,然后才能使用
// dispatcher.register( callback )
dispatcher.register( ( actions) => {
switch ( actions.type ) {
case type.INCRMENT:
// 用户操作了,数据修改
store.state.count++
break;
default:
break;
}
})
export default dispatcher
(5)通过store的事件的发布和订阅,在需要的组件中触发actionCreators.js中的动作
import React from 'react';
import logo from './logo.svg';
import './App.css';
import store from './store'
import actionCreators from './store/actionCreators';
class App extends React.Component {
constructor () {
super()
this.state = {
count: store.getState().count
}
}
increment () {
actionCreators.increment()//执行actionCreator中的动作
store.emit('count')//事件事件订阅,视图改变
}
componentDidMount () {
store.on('count', () => {//事件的发布
this.setState({//更新视图
count: store.getState().count
})
})
}
render () {
return (
<div>
<h3> flux </h3>
<button onClick = { this.increment }> + </button>
<p> count: { this.state.count } </p>
</div>
)
}
}
export default App;