Flux架构(重点在于思想)

一、简介

  • 由于React的核心是组件,而且它负责的就是view的处理。但当应用的负责程度增加的时候,state中数据就会越来越复杂,只用reac开发就会变得力不从心,需要新的工具处理(不仅仅是view层级的内容,还有其他层级的内容,比如说数据流向、state管理、路由解决方案等)。
  • react的开发者推出了Flux架构及官方实现来解决这些问题。
  • 业内推出了很多其他的Flux实现,其中以Redux这个最为瞩目。
  • Flux是Facebook官方提出的一套前端应用框架模式。它的核心概念就是单向数据流
  • 更像是一种软件开发模式,而不是具体的一个框架,所以基于Flux存在很多实现方式。其实,使用Flux框架开发程序不需要引入很多代码,关键是它内在的思想。

二、单向数据流

单向数据流是Flux的核心。Flux的单向数据流是怎么具体运作的呢?

(一)Action

Action就是用来描述一个行为的对象,里面有相关的信息,比如说一个创建文章的Action可以是:

{
actionName:'creat-post',
data:{
	content:'new stuff'
	}
}

(二)Dispatcher

Dispather是一个信息的分发中心,它是Action和Store的连接中心。Dispatch可以使用dispatch方法执行一个Action,并且可以用register方法注册并回调,在回调方法中处理Store中的内容。

(三)Store

Store处理完毕之后,它可以使用emit方法向其他发送名为‘change’的广播,告知他们Store已经发生变更。

(三)View

View层监听change事件,一旦change事件被触发,那么该层可以调用setState来更新整个UI。

三、具体分析

1.Dispatcher & Action

//dispatcher/AppDispatcher.js
//实例化一个Dispatcher并且返回
import {Dispatcher} from 'flux';
export default new Dispatcher();

当产生一个Action(新建或者删除)时,

//.components/Todo.jsx
import TodoAction from '../actions/TodoAction;
...
class Todo extends React.Component {
	//数据
	construstor(props){
		super(props);
		this.createTodo=this.createTodo.bind(this);
		this.deleteTodo=this.deleteTodo.bind(this);
	}
	//方法:新建todo
	createTodo(){
		TodoAction.create({id:uuid.v4(),content:'3rd stuff'});
	}
	//方法:删除todo
	deleteTodo(){
		TodoAction.delete(id);
	}
	//模板
	render(){
		return(<div>
			<List items={this.state.todos} onDelete={this.deleteTodo}>
			<CreateButton onClick={this.createTodo}>
		</div>)
	}
}
export default Todo;

当点击按钮是,一个特殊的action会被触发,并且交给Dispatcher处理。

//./actions/TodoAction
import AppDispatcher from '../dispatcher/AppDispatcher';
const TodoAction  = {
	creat(todo){
		AppDispatcher.dispatch({
			actionType:'CREATE_TODO',
			todo
		});
	},
	delete(id){
		AppDispatcher.dispatch({
			actionType:'DELETE_TODO',
			id
		}); 
	}
};

export defalut TodoAction;

只是一个普通的JavaScript Object,用一个actionType字段来表明这个action的用途,用另一个字段来表明它所传递的信息。
在这里,dispatch的是一个对象,但是当应用的复杂程度不断增加的时候,就可能在不同的view中dispatch相同的
对象,而且必须用着相同的actionType,还要记牢数据的格式,这都不利于代码复用。所以Flux提出了一个新的概念,称为action creator,其实就是将这些数据抽象到一个函数中。就像在ToDoAction里面写的一样。

//在TodoAction中定义的Action Creators
const TodoAction ={
	//用一个函数包裹Dispatcher.dispatch方法 actionCreator
	create(todo){
		AppDispatcher.dispatch({
			actionType:'CREATE_TODO',
			todo
		});
	}
	...
}
//那么现在调用action就可以像Todo.jsx中写的一样。
TodoAction.create({id:uuid.v4(),content:'3rd stuff'});

2.Store & Dispatcher

store:是整个程序所需要的数据。store是单例模式的,在整个程序中,每种store都仅有一个实例。例:现在来创建TodoStore,它存放了所有的文章列表。不同类型的数据应该创建多个store,假如程序里面存在用户信息,那么还可以新建一个UserStore.js。

//./store/TodoStore.js
//单件类型的一个JavaScript Object
const TodoStore={
 //存放所有的文章列表,里面有两条默认的数据
	todos:[
		{id:uuid.v4(),
		content:'first one'},
		{id:uuid.v4(),
		content:'2nd two'},
	],
	getAll(){return this.todos;},
	addTodo(todo){
		this.todos.push(todo);
	},
	deleteTodo(id){
		this.todos=this.todos.filter(item=>item.id!==id);
	}
}

Dispatcher的另外一个API方法是register,它可以注册不同事件的处理回调,并且在回调中对store进行处理。

//./store/TodoStore.js
...
AppDispatcher.register(action=>{
	switch(action.actionType){
		case 'CREATE_TODO':
		 TodoStore.addTodo(action.todo);
		 break;
		case 'DELETE_TODO':
		 TodoStore.deleteTodo(action.id);
		 break;
		default:
			//默认操作
	}
})
...

每个action对应dispatcher传过来的action,包含actionType和对应的数据,store是更新数据的唯一场所,这是Flux的重要理念。
action和Dispatcher并不和数据打交道,无法做数据操作。
Dispatcher的两个重要的方法:

  • register方法:负责注册各种actionType的回调,并且在回调中操作store;
  • dispatch方法:这个方法用来触发对应类型的回调。

Pub-Sub(订阅-发布)模式的代码如下:

var EventEmitter = require('events');
var myEmitter = new EventEmitter();
myEmitter.on('event',function(name){
	console.log('my name is '+name);
})
myEmitter.emit('event','张三');
//打印:my name is 张三

  • Dispatcher与Pub-Sub(订阅-发布)模式的区别:
    • Dispatcher的回调函数未订阅到一个特定的事件或者频道中,register只接受一个函数作为回调。所有的动作都会发送到这个回调中。
    • Dispatcher的回调可以被延迟执行,直到其他的回调函数执行完毕。

3.Store & View

Store发生了改变,有它来通知View,然后由View来展示新的数据了。

现在的代码无法完成这个功能,上面提到的Pub-Sub(订阅-发布)模式,如果给store添加上这个特性,就能很方便的把store和view联系在一起。在这里借助Node.js标准库EventEmitter在浏览器中实现。

npm install events -save

//使用Object.assign方法把EventEmitter.prototype挂载到TodoStore上
const TodoStore=Object.assign({},EventEmitter.prototype,{
...
	emitChange(){
		this.emit('change');
	},
	addChangeListener(callback){
		this.on('change',callback);
	},
	removeChangeListener(callback){
		this.removeListener('change',callback);
	},
...
});
AppDispatcher.register((action)>{
	switch(action.actionType){
		case 'CREATE_TODO':
		 TodoStore.addTodo(action.todo);
		 //TodoStore已经更改,发送了一个广播
		 TodoStore.emitChange();
		 break;
		case 'DELETE_TODO':
		 TodoStore.deleteTodo(action.id);
		 TodoStore.emitChange();
		 break;
		default:
			//默认操作
	}
})
export defalut TodoAction;

store的变化已经使用emit方法广播出去,需要view层接受这个变化的信号,同时更新UI。
第一步:在组件刚初始化完毕的时候监听store的change事件,这样在store触发这个事件的时候,就会触发回调。
第二步:在生命周期函数componentDidMount中注册这个事件,在componentWillUnmount中清除这个事件绑定。

//Todo.jsx
class Todo extends React.Component{
	construstor(props){
		super(props);
		this.state ={
			todos:TodoStore.getAll()
		};
		this.createTodo=this.createTodo.bind(this);
		this.deleteTodo=this.deleteTodo.bind(this);
		this.onChange=this.onChange.bind(this);
	}
	//初始化时在store中注册这个事件
	componentDidMount(){
		TosoStore.addChangeListener(this.onChange);
	}
	//组件卸载时清除这个事件绑定
	componentWillUnmount(){
		TosoStore.removeChangeListener(this.onChange);
	}
	//store改变后触发的回调函数,用setState来更新整个UI
	onChange(){
		this.setState({
			todos:TodoStore.getAll()
		})
	}
}

四、Flux的整个的流程

  • 当用户在view上有一个交互动作时,
  • Dispatcher广播(dispatch方法)一个action(就是一个Object对象,里面包含action类型和要传递的数据);
  • 在总控台Dispatcher中注册了各种类型的action类型,在对应的类型中,store(也是一个对象类型)对这个action进行响应,对数据进行相应的处理,然后触发一个自定义事件,
  • 同时,在view上注册这个store的事件回调,响应这个事件并且重新渲染整个页面。

五、Flux的优缺点

优点:
Flux能够合理将数据和组件的state分离,保持清晰的数据流,提 供了可预测的状态,避免的了多向数据流带来的混乱和维护困难的问题。适用于比较复杂的多人项目。

缺点:
实现Flux架构会增加代码量,应用简单时没有必要使用Flux。

你可能感兴趣的:(#,flux架构,flux,react)