Flux 简介

发表于: 2016-12-27分类于:前端开发者笔记

Flux

在学习 flux 的时候,官方文档看的一头雾水。后来想想,其实自己在脑子里把整个过程过一遍,把它的核心思想搞清楚了以后,发现它还是很简单的。下面就看看 flux 是怎样的一种思想。

基本概念

flux 的特点是单向数据流,即各个操作都是单向的,如下图所示:

image

下面对每个过程进行论述:

View

视图层,和用户进行交互,针对不同的操作,View 层面可以触发不同的 action。

Action

触发了 action 之后,该 action 会使用 dispatcher 来分发该 action。

Dispatcher

dispatcher 接受 action 并将其分发给 store。

Store

store 要想收到 dispatcher 分发的 action,需要在 dispatcher 上面注册一些函数,这些函数会收到 action,然后可以根据不同的 action 对 store 做一些处理。每当 store 被改变后,都会触发 "change" 事件。

View

view 中应该订阅了 store 的 "change" 事件,这样 store 改变后,view 能够感知到,并读取 store 得到新的内容,刷新 View。

以上过程是个环。

实例

举个例子来说明以上过程,并给出一个简单的示例:

这个示例很简单,就是点击按钮,就会在列表中添加一项。你需要有 React 的基础,不需要有任何 Flux 的基础。以下是示例的目录结构:

├── actions
│   └── ListActions.js
├── components
│   └── List.js
├── dispatcher.js
├── index.js
└── stores
    └── ListStore.js

List.js

这是 React 的一个组件,在 render 函数中,它使用 store 提供的 API 来读取数据完成渲染。在点击事件中调用 ListActions 中的方法来触发一个 action。

componentDidMount 方法中订阅了 store 的 "change" 事件,当 store 更新后读取新的数据,并重新渲染页面。

import React, {Component} from 'react';
import ListStore from '../stores/ListStore';
import ListActions from '../actions/ListActions';

class List extends Component{
    constructor(props){
        super(props);
        this.state = {
            items: ListStore.getItems()
        }
    }
    addItem(){
        ListActions.addItem('new item');
    }
    componentDidMount(){
        ListStore.on('change', ()=>{
            this.setState({
                items: ListStore.getItems()
            });
        })
    }
    render(){
        return (
            <div>
                <p onClick={this.addItem}>add</p>
                <ul>
                    {
                        this.state.items.map((item, index) => {
                            return <li key={index}>{item}</li>
                        })
                    }
                </ul>
            </div>
        )
    }
}

export default List;

ListActions.js

action 中包含一些方法,这些函数会在 View 上的事件处理函数中调用,比如在在一个按钮的 click 事件中调用 ListActions.addItem('new item')

action 中不做什么处理,只是简单地使用一个 actionType 来标记该 action,并使用 dispatcher 分发出去。这里分发的是一个对象,因此可以在对象中带上其他数据,比如这里需要添加一个 item,于是这里就带了一个需要添加的 item。

import dispatcher from '../dispatcher';

let ListActions = {
    addItem(item){
        dispatcher.dispatch({
            type: 'ADD_ITEM',
            item: item
        })
    }
};

export default ListActions;

dispatcher.js

这里的 dispatcher 是使用 flux 的实现,flux 提供了一个 Dispatcher 类,在使用的时候应该创建一个实例。

这里使用到了 dispatcher 的 dispatch 方法和 register 方法,前者用来分发 action,而后者用来订阅 action。

在 action 中调用了 dispatcher.dispatch 之后,dispatcher 就会执行在它上面注册的回调。因此需要为 store 在 dispatcher 上注册回调才能接受到 action。

import {Dispatcher} from 'flux';

var dispatcher = new Dispatcher();

export default dispatcher;

ListStore.js

store 里面存储了应用的数据,另外 store 还提供了一些用来操作其中数据的 API,这些 API 有的是修改数据的,有的是读取数据的。修改数据的 API 应该是给 dispatcher 调用的,而获取数据的 API 应该是给 view 调用的。

另外为了能够让 view 感知到 store 的改变,store 上混入了实现 sub/pub 模式的 API。

随后在 dispatcher 上注册了对 action 的处理函数,这个函数接受一个 action,在函数中通过 action.type 来判断当前的 action 是要进行什么操作,然后根据不同的操作来修改 store,当 store 修改后需要触发 emit 方法来通知 view 自己已经改变了。

import dispatcher from '../dispatcher';
var EventEmitter = require('events').EventEmitter;

var ListStore = Object.assign({}, EventEmitter.prototype, {
    items: [],
    addItem: function(item) {
        this.items.push(item);
    },
    getItems: function(){
        return this.items;
    },
    deleteItem: function (index) {
        if(this.items.length > index && index >= 0){
            this.items.splice(index, 1);
        }
    }
});

dispatcher.register(function (action) {
    switch (action.type){
        case 'ADD_ITEM':
            ListStore.addItem(action.item);
            ListStore.emit('change');
            break;
        default:
            break;
    }
});

module.exports = ListStore;

Ok,以上就是 flux 的核心思想了。