react flux架構(gòu)學習

概述

Flux 是用來構(gòu)建用戶端 Web 應用的架構(gòu),大體分為四個部分:Views, Stores 和 Dispatcher命爬,Actions曹傀。

  • Views: 負責渲染應用中所有與用戶直接交互的界面,并從Store獲取數(shù)據(jù)遇骑。

  • Stores: 負責管理應用的數(shù)據(jù)卖毁。 可以按照業(yè)務邏輯劃分多個Store, 例如UserStore用來管理用戶信息。

  • Dispatcher: 負責分發(fā)從Action過來的事件落萎,傳遞給store亥啦,整個應用的調(diào)度中心,只有一個练链。

  • Actions: Actions是傳遞給Dispatcher的動作翔脱,包好Action物件和Action Creators.

    • Action物件是指Action里包含的信息,主要包括ActionType (在Dispatcher中用來判斷如何分發(fā))媒鼓,以及其他該動作所攜帶的主要信息
    • Action Creators 負責創(chuàng)建Actions届吁,并傳遞給Dispatcher

flux應用的數(shù)據(jù)流向是一個單向數(shù)據(jù)流,由Action creators創(chuàng)建的Actions統(tǒng)一被傳遞到Dispacther绿鸣,Dispacther在自己被注冊的方法中按照Action物件來下發(fā)事件到對應的Store, Store處理完邏輯 發(fā)送一個變化的通知疚沐,然后所有監(jiān)聽了這個Store變化的頁面更新響應。

flux data flow.png

創(chuàng)建項目

npm install -g create-react-app
create-react-app my-app
cd my-app/
npm start

啟動~~
最終項目放在github上面 下載后

  $ npm install
  $ npm start

項目結(jié)構(gòu)如下圖


項目結(jié)構(gòu).png

Actions

篇幅有限潮模,貼出部分Action代碼亮蛔。

import Constants from '../Constants/Constants'
import TodoDispatcher from '../Dispatcher/TodoDispatcher'
let TodoAction = {
    toggleItem(id) {
        TodoDispatcher.dispatch({
            id,
            type: Constants.TOGGLEITEM
        })
    },
    deleteItem(id) {
        TodoDispatcher.dispatch({
            id,
            type: Constants.DELETEITEM
        })
    },
    loadData() {
        fetch('todos.json')
            .then((data)=> data.json())
            .then((todos)=>{
                TodoDispatcher.dispatch({
                    todos,
                    type: Constants.LOADDATA
                })
            })
    }
}
module.exports = TodoAction;
  • Actions包含了所有的動作,所有的動作被傳遞給Dispatcher來做分發(fā)擎厢。
  • Action物件究流,字典{ id, type: Constants.TOGGLEITEM }就是一個Action物件辣吃,包含了Action的所有信息,其中type就是dispatch分發(fā)時該Action的標識符芬探。
  • Action Creators神得,toggleItem就是一個Action Creators,用來創(chuàng)建Action偷仿,在我們的例子當中不能體現(xiàn)它的價值哩簿,復雜一點的項目對傳遞進來的參數(shù)可以再這個地方進行處理,這是一個很常見的場景炎疆。

Dispatcher

Flux 庫的 Dispatcher 提供了一個 dispatch 函數(shù)卡骂,將接收到的 Actions 傳遞給所有注冊的回調(diào)函數(shù),回調(diào)函數(shù)由 Stores 提供形入。 flux源碼

import { Dispatcher } from 'flux'
let TodoDispatcher = new Dispatcher()
module.exports = TodoDispatcher;

Store

import Constants from '../Constants/Constants'
import TodoDispatcher from '../Dispatcher/TodoDispatcher'
const CHANGE_TODOS = 'CHANGE_TODOS'
import EventEmitter from 'events'
let _emitter = new EventEmitter()
let todos = [];

let toggleItemList = (todos, id) => {
    let newTodos = [...todos];
    let target = newTodos.find((todo)=>{
        return todo.id === id
    })
    if (target) {
        target.checked = !target.checked
    }
    return newTodos
}
let deleteItemList = (todos, id) => {
    let newTodos = [...todos];
    //找到對應的索引
    let idx = newTodos.findIndex((todo)=>{
        return todo.id === id
    })
    //按照索引刪除
    newTodos.splice(idx,1)
    return newTodos
}
let todoStore = {
    getTodos() {
        return todos;
    },
    addObserver(callback) {
        _emitter.on(CHANGE_TODOS, callback)
        return ()=> _emitter.removeListener(CHANGE_TODOS, callback)
    },
    dispatchToken: TodoDispatcher.register((action)=>{
        console.log(action)
        switch(action.type) {
            case Constants.TOGGLEITEM:
                todos = toggleItemList(todos, action.id);
                break;
            case Constants.DELETEITEM:
                todos = deleteItemList(todos, action.id);
                break;
            case Constants.CREATEITEM:
                todos = createItem(todos, action.title);
                break;
            case Constants.EDITITEM:
                todos = editItem(todos, action.id, action.title);
                break;
            case Constants.LOADDATA:
                todos = action.todos;
                break;
            default:
                break;
        }
        _emitter.emit(CHANGE_TODOS)
    })
}

module.exports = TodoDispatcher;

Store部分代碼由以下幾部分組成

  • 業(yè)務邏輯 toggleItemList 、deleteItemList都是具體的業(yè)務邏輯處理缝左。
  • 創(chuàng)建Store亿遂,TodoDispatcher.register注冊所有需要分發(fā)的方法,在事件中按照被傳遞過來的action.type區(qū)分具體的業(yè)務實現(xiàn)渺杉,該方法返回值是一個字符串為該Store的唯一標示蛇数,當一個Action觸發(fā)多個store的執(zhí)行,并且有先后順序是越,可以用這個標識排先后順序耳舅,見waitFor方法。
  • 實現(xiàn) 添加觀察者的方法addObserver倚评,在每一個業(yè)務邏輯處理完成之后浦徊,通過_emitter.emit(CHANGE_TODOS)來發(fā)送事件,刷新頁面天梧。

Views

舉一個TodoListContainer組件代碼為例

import React, { Component } from 'react'
import TodoList from './TodoList'
import TodoStore from '../Store/TodoStore'
import TodoAction from '../Action/TodoAction'
class TodoListContainer extends Component {
    constructor(props) {
      super(props);
      this.state = {
         todos: TodoStore. getTodos(),
      };
    }
    componentDidMount() {
        this.addObserver = TodoStore. addObserver(()=>{
            this.setState({
                todos: TodoStore.getTodos()
            }) 
        })
    }
    componentWillUnmount() {
        this.addObserver()
    }
    render () {
        return (
            <TodoList 
                todos={this.state.todos} 
                toggleItem={TodoAction.toggleItem}
                deleteItem={TodoAction.deleteItem}
                editItem={TodoAction.editItem}
            />
        )
    }
}

module.exports = TodoListContainer;

這個組件中

  • 從TodoStore處獲取數(shù)據(jù)盔性,在狀態(tài)機中接收,渲染界面呢岗。
  • 在生命周期componentDidMount中監(jiān)聽TodoStore冕香,一旦Store當中數(shù)據(jù)發(fā)生變化,就會通過發(fā)送這個事件來更新View后豫,addListner是一個添加監(jiān)聽者的方法悉尾,返回值是移除監(jiān)聽者的一個方法, 所以在該頁面卸載時componentWillUnmount通過調(diào)用返回的方法就可以移除監(jiān)聽者挫酿。
  • 所有組件當中用到的事件构眯,都來自TodoAction,用戶的交互信息皆由Action采集傳遞給Dispatcher饭豹。

優(yōu)化

flux提供了Flux Utils來優(yōu)化現(xiàn)有的flux實現(xiàn)鸵赖。

ReduceStore
import {ReduceStore} from 'flux/utils'; 
class TodoStore extends ReduceStore {
  getInitialState() {
    return [];
  }
  reduce(todos, action) {
    switch (action.type) {
        case Constants.TOGGLEITEM:
            return toggleItemList(todos, action.id);
        case Constants.DELETEITEM:
            return deleteItemList(todos, action.id);
        case Constants.CREATEITEM:
            return createItem(todos, action.title);
        case Constants.EDITITEM:
            return editItem(todos, action.id, action.title);
        case Constants.LOADDATA:
            return action.todos;
        default:
            return todos;
    }
  }
}
//關(guān)聯(lián)dispatcher
module.exports = new TodoStore(TodoDispatcher);

以上代碼可以完全代替todoStore那整個字典务漩,ReduceStore幫我們

  • 處理了addObserver和發(fā)送頁面刷新這樣的通知。
  • 去掉了todos這樣的數(shù)據(jù)集合它褪,我們只需要在外面使用TodoStore.getState()就可以獲取到todos這樣的數(shù)據(jù)集合饵骨。所以相對應的我們需要在頁面接收數(shù)據(jù)時做一些改變。
  • 導出的時候注意綁定茫打。
    constructor(props) {
      super(props);
      this.state = {
         todos: TodoStore.getState(),
      };
    }
    componentDidMount() {
        this.addObserver = TodoStore.addListener(()=>{
            this.setState({
                todos: TodoStore.getState()
            }) 
        })
    }
    componentWillUnmount() {
        this.addObserver()
    }

其中我們將自定義的的getTodosaddObserver替換成ReduceStore的方法居触。

Container
   import {Container} from 'flux/utils';
    static getStores() {
       return [TodoStore];
    }
    static calculateState(prevState) {
        return {
            todos: TodoStore.getState(),
        };
    }
    module.exports = Container.create(TodoListContainer);

用這些代碼可以完全替換View里面的constructorcomponentDidMount老赤、componentWillUnmount里面的三個方法轮洋。Container實際上幫我們處理了這些事情

  • 去除添加移除監(jiān)聽者的這些操作,全部轉(zhuǎn)移到了calculateState方法中抬旺。
  • 去除狀態(tài)機里面的todos弊予,在calculateState方法中字典的key就是我們狀態(tài)機里面的值,依舊可以用this.state,todos來調(diào)用
  • 注意導出的時候Container.create(TodoListContainer)

參考鏈接

http://facebook.github.io/flux/docs/flux-utils.html#content
http://kongyixueyuan.com/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末开财,一起剝皮案震驚了整個濱河市汉柒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌责鳍,老刑警劉巖碾褂,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異历葛,居然都是意外死亡正塌,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門恤溶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來乓诽,“玉大人,你說我怎么就攤上這事宏娄∥试#” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵孵坚,是天一觀的道長粮宛。 經(jīng)常有香客問我,道長卖宠,這世上最難降的妖魔是什么巍杈? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮扛伍,結(jié)果婚禮上筷畦,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好鳖宾,可當我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布吼砂。 她就那樣靜靜地躺著,像睡著了一般鼎文。 火紅的嫁衣襯著肌膚如雪渔肩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天拇惋,我揣著相機與錄音周偎,去河邊找鬼。 笑死撑帖,一個胖子當著我的面吹牛蓉坎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播胡嘿,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼蛉艾,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了灶平?” 一聲冷哼從身側(cè)響起伺通,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎逢享,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吴藻,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡瞒爬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了沟堡。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侧但。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖航罗,靈堂內(nèi)的尸體忽然破棺而出禀横,到底是詐尸還是另有隱情,我是刑警寧澤粥血,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布柏锄,位于F島的核電站,受9級特大地震影響复亏,放射性物質(zhì)發(fā)生泄漏趾娃。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一缔御、第九天 我趴在偏房一處隱蔽的房頂上張望抬闷。 院中可真熱鬧,春花似錦耕突、人聲如沸笤成。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽炕泳。三九已至纵诞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間喊崖,已是汗流浹背挣磨。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留荤懂,地道東北人茁裙。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像节仿,于是被迫代替她去往敵國和親晤锥。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 去年翻譯的flux官方文檔對flux架構(gòu)的描述廊宪,覺得最近很多朋友開始react編程了矾瘾,所以我覺得有必要拿出來這篇水...
    余歌_非魚閱讀 2,537評論 0 9
  • 看到這篇文章build an image gallery using redux saga,覺得寫的不錯箭启,長短也適...
    smartphp閱讀 6,166評論 1 29
  • ##Flux與面向組件化開發(fā)首先要明確的是壕翩,F(xiàn)lux并不是一個前端框架,而是前端的一個設(shè)計模式傅寡,其把前端的一個交互...
    吳小蛆閱讀 316評論 0 0
  • 前言 強烈推薦的學習資源:《深入淺出React和Redux》 此篇學習筆記記錄了對書中第三章節(jié)的學習心得在學...
    ccminn閱讀 7,458評論 3 5
  • 近期我想要企業(yè)資金流暢放妈,財務富足,月銷售額突破800萬荐操,月利潤突破100萬芜抒,所有眾生擁有無邊無量的財富,擁有像氧氣...
    lindacheng2017閱讀 229評論 0 0