學習 React.js:瞭解 Flux,React.js 的架構

Getting To Know Flux, the React.js Architecture

Ken Wheeler (@ken_wheeler)

簡介

歡迎來到學習 React 的第三章。今天我們將會學習臉書的 Flux 架構的工作方式,以及我們怎麼把它應該用到我們的工程中。

如果你沒有準備好,我強烈建議你回去看看這個系列的第一第二章,Getting Started & ConceptsBuilding a Real Time Twitter Stream with Node and React。當然我不強迫你們,不過如果你不熟悉 React.js 的化,這兩篇文章肯定對你很有用。

什麼是 Flux?

Flux 是臉書內部用來配合 React 工作的一個架構。它不是框架或者庫。它只是一個配合 React 的新的體系結構,以及單向數據流的概念。

也就是說,臉書提供了了一個包含了 Dispatcher 庫的 repo。這個庫是用來專門處理那些全局的,廣播消息到註冊事件上的發佈/訂閱模式的。

一個典型的 Flux 架構用例是,配合 NodeJS 的 EventEmitter 模塊來創建一個事件系統,用於管理應用的狀態。

解釋 Flux 的最好方式我想應該就是把它的組件給挨個介紹一下:

  • Actions – 將數據傳遞給 Dispatcher 的輔助方法
  • Dispatcher – 接收動作然後廣播到已註冊的囘調事件
  • Stores – 應用 state & logic 的容器,存放註冊囘調事件的地方
  • Controller Views – React 組件,用來從 Stores 接收狀態,然後把它通過 props 傳給子組件。

讓我們來看看示意圖:

API 是幹嘛的?

當你需要用到從外面(或者發送出去)的數據的時候,我發現用 Actions 引入數據到 Flux 流,然後傳給 Stores,是最無痛的方式。

Dispatcher

那 Dispatcher 到底是什麼

Dispatcher 是管理整個流程的基礎。它是你的應用的樞紐核心。dispatcher 接收 actions ,然後把 actions 和數據推送給註冊的囘調。

那本質就是 發佈/訂閱 咯?

也不完全就是這樣。despatcher 廣播負載給所有的註冊囘調,並且可以允許你按照一定順序來調用囘調,甚至還可以在執行之前做暫停等待更新。在你的應用中,只有一個 despatcher,它的職責就是扮演你的核心樞紐。

看起來應該像這樣:

<!-- lang: js -->
var Dispatcher = require(‘flux‘).Dispatcher;
var AppDispatcher = new Dispatcher();

AppDispatcher.handleViewAction = function(action) {
  this.dispatch({
    source: ‘VIEW_ACTION‘,
    action: action
  });
}

module.exports = AppDispatcher;

上面的例子中,我們創建了一個 Dispatcher 的實例並且創建了一個 handleViewAction 方法。如果你希望找出視圖觸發動作 vs 服務/API 觸發動作的區別的化,那麽這種抽象是很有用的。

我們的方法調用dispatch 方法,它會廣播 action 給所有的註冊囘調。這個 action 可以在 Stores 裏面被調用,然後會觸發 state 的更新。

用圖來描述看起來應該是這樣:

Dispatcher 模塊有一個很酷的功能,就是可以在 Stores 中定義依賴和管理囘調。所以如果你的應用中某一部分的更新,需要另一部分先更新的話,Dispatcher 的 waitFor 方法將非常有用。

為了用這個功能,我們需要在 Store 裏面保存 Dispatcher 註冊方法的返回值,以dispatcherIndex 形式,如下所示:

<!-- lang: js -->
ShoeStore.dispatcherIndex = AppDispatcher.register(function(payload) {

});

然後在我們的 Store 中,當我們要處理某個動作時,我們可以用 Dispatcher 的 waitFor 方法來確保我們的鞋店是否被更新:

<!-- lang: js -->
case ‘BUY_SHOES‘:
  AppDispatcher.waitFor([
    ShoeStore.dispatcherIndex
  ], function() {
    CheckoutStore.purchaseShoes(ShoeStore.getSelectedShoes());
  });
  break;

Stores

在 Flux 中,Stores 管理著你的應用中特殊部分的 state。或者通俗點講,包括有應用的每個部分,保存管理數據,數據查詢和調用囘調之類。

我們來看一個最基本的 Store:

<!-- lang: js -->
var AppDispatcher = require(‘../dispatcher/AppDispatcher‘);
var ShoeConstants = require(‘../constants/ShoeConstants‘);
var EventEmitter = require(‘events‘).EventEmitter;
var merge = require(‘react/lib/merge‘);

// Internal object of shoes
var _shoes = {};

// Method to load shoes from action data
function loadShoes(data) {
  _shoes = data.shoes;
}

// Merge our store with Node‘s Event Emitter
var ShoeStore = merge(EventEmitter.prototype, {

  // Returns all shoes
  getShoes: function() {
    return _shoes;
  },

  emitChange: function() {
    this.emit(‘change‘);
  },

  addChangeListener: function(callback) {
    this.on(‘change‘, callback);
  },

  removeChangeListener: function(callback) {
    this.removeListener(‘change‘, callback);
  }

});

// Register dispatcher callback
AppDispatcher.register(function(payload) {
  var action = payload.action;
  var text;
  // Define what to do for certain actions
  switch(action.actionType) {
    case ShoeConstants.LOAD_SHOES:
      // Call internal method based upon dispatched action
      loadShoes(action.data);
      break;

    default:
      return true;
  }

  // If action was acted upon, emit change event
  ShoeStore.emitChange();

  return true;

});

module.exports = ShoeStore;

上面的代碼中我們做的最重要的事情就是把我們的 store 用 NodeJS 的 EventEmitter 擴展了。這允許我們的 store 來監聽/廣播 事件。允許我們的 視圖/組件 基於這些事件來更新。因為我們的 Controller View 監聽著 Store,利用這些事件的變化,可以讓 Controller View 知道我們的應用 state 是否發生變化,以及是否應該刷新來保持顯示一致。

我們還用它的 register 註冊了一個囘調到我們的 AppDispatcher。這意味著我們的 Store 現在監聽著 AppDispatcher 廣播。我們的 switch 段用來確定,當有事件發生的時候,是否發送廣播。如果一個關聯的動作發生,那麽一個更新事件就會被推送,然後監聽著這個事件的視圖就會更新它們的狀態。

我們的 public 方法 getShoes 是提供給我們的 Controller View 來查詢用的,它用來查詢 _shoes 對象中所有的鞋子,然後把數據提供給我們的組件狀態。雖然這只是一個簡單的例子,複雜的邏輯也可以放在這裏,以便我們的視圖和幫助類保持整潔。

Action Creator 和 Actions

動作創建者是動作集合,用來在 View (或者要幹事的任何地方),用來把發送給 Dispatcher 的。動作會被通過 dispatcher 推送。

臉書是這樣用它的,action 的類型常量用來決定應該觸發何種 action,應該配合何種 action 數據。在註冊囘調中,這些動作可以通過動作類型來處理,並且可以作為參數,配合動作數據來調用這些動作。

來看看常量的定義:

<!-- lang: js -->
var keyMirror = require(‘react/lib/keyMirror‘);

module.exports = keyMirror({
  LOAD_SHOES: null
});

上面我們用了 React 的keyMirror 庫,沒錯,你猜對了,就是用來匹配我們的關鍵字的。來看看這個文件,我們可以告訴我們的應用加載鞋子。用這個靜態的幫助類,可以保持所有的事情都有組織,並且可以從較高層次來查看應用到底在做什麽。

現在來看看對應的 Action Creator 的定義:

<!-- lang: js -->
var AppDispatcher = require(‘../dispatcher/AppDispatcher‘);
var ShoeStoreConstants = require(‘../constants/ShoeStoreConstants‘);

var ShoeStoreActions = {

  loadShoes: function(data) {
    AppDispatcher.handleAction({
      actionType: ShoeStoreConstants.LOAD_SHOES,
      data: data
    })
  }

};

module.exports = ShoeStoreActions;

在我們上面的例子中,我們在 ShoeStoreActions 對象裏面創建了一個方法,然後拿我們提供的數據來調用推送動作。我們現在可以把這個動作文件導入到我們的視圖或者 API,然後調用 ShoeStoreActions.loadShoes(數據) 來推送到 Dispatcher,然後會被廣播出去。之後 ShoeStore 將會 ‘聽到‘ 事件發生了,然後加載鞋子的方法!

Controller Views

Controller Views 沒什麽好說,它們就是 React 組件,用來監聽變更事件,然後處理從 Stores 過來的應用狀態的。它們會通過 props 把數據傳播下去給子組件。

看起來應該像這樣:

<!-- lang: js -->
/** @jsx React.DOM */

var React = require(‘react‘);
var ShoesStore = require(‘../stores/ShoeStore‘);

// Method to retrieve application state from store
function getAppState() {
  return {
    shoes: ShoeStore.getShoes()
  };
}

// Create our component class
var ShoeStoreApp = React.createClass({

  // Use getAppState method to set initial state
  getInitialState: function() {
    return getAppState();
  },

  // Listen for changes
  componentDidMount: function() {
    ShoeStore.addChangeListener(this._onChange);
  },

  // Unbind change listener
  componentWillUnmount: function() {
    ShoesStore.removeChangeListener(this._onChange);
  },

  render: function() {
    return (
      <ShoeStore shoes={this.state.shoes} />
    );
  },

  // Update view state when change event is received
  _onChange: function() {
    this.setState(getAppState());
  }

});

module.exports = ShoeStoreApp;

上面的例子中,我們通過 addChangeListener 來監聽事件變更,當接收到事件的時候更新我們的應用狀態。

我們的應用狀態數據被保存在 Stores ,所以我們用 Stores 的 public 方法來接收和設置應用狀態。

放一塊

現在我們把 Flux 架構的各部分獨立的過了一遍,我們應該比較好的理解了這個架構實際是怎樣工作的了。還記得我們之前看過的那個圖片麽?讓我們更深入的來看看它,現在我們明白了這個流程裏面的每一步是做什麼的了:

組裝

讀完這篇文章之後,我希望你如果之前沒有"認識"臉書的 Flux 架構的話,現在你可以說你知道了。只有實際用它來做些東西之後,你才會知道 React.js 是怎樣一回事。

當你用過 Flux 一次之後,不用 Flux 寫 React 的感覺就像操作 DOM 不用 jQuery 一樣抓狂。當然你也能做到,但是總覺得不優雅,不夠結構化。

如果你希望用 Flux 架構,不過你不想用 React,來看看 Delorean ,一個 Flux 框架,你可以用 Ractive.js 或者 Flight。還有另外一些值得看看的庫,比如說 Fluxxor,它提供了一種不同的 Flux 方式,以 Flux 實例為中心提供了一套緊耦合的 Flux 組件。

好了,我相信你已經真正的掌握了 Flux 了,你也實際有用到它了,所以我們在學習 React 的第四章將用 Reach.js 和 Flux 架構來創建一個購物車站點。

时间: 2024-10-11 20:49:02

學習 React.js:瞭解 Flux,React.js 的架構的相关文章

學習 React.js:用 React.js 和 Flux 創建一個簡單的購物車

Creating A Simple Shopping Cart with React.js and Flux Ken Wheeler (@ken_wheeler) 簡介 歡迎來到學習 React 的第四章這也是最後一章!到現在,我們已經學習了怎樣利用 React 的 API 來創建狀態型組件,如何應用它們,以及如何運用臉書的 Flux 架構來工作的 今天我們將把所有的這一切放到一塊,來創建一個簡單的購物車應用.在現在的電商網站上,產品的詳細頁面相互依賴,而 React 有助於簡化並有效的組織它們

學習 React.js:用 Node 和 React.js 創建一個實時的 Twitter 流

Build A Real-Time Twitter Stream with Node and React.js By Ken Wheeler (@ken_wheeler) 簡介 歡迎來到學習 React 的第二章,該系列文章將集中在怎麼熟練並且有效的使用臉書的 React 庫上.如果你沒有看過第一章,概念和起步,我非常建議你繼續看下去之前,回去看看. 今天我們準備創建用 React 來創建一個應用,通過 Isomorphic Javascript. Iso-啥? Isomorphic. Java

學習 React.js:用 ReactJS 30 分鐘打造一個移動應用

Make a Mobile App with ReactJS in 30 Minutes Ken Wheeler (@ken_wheeler) React 能讓前端開發者以前所未有的方式來構建應用.它有許多好處:比如單向數據流,簡單的組件生命週期,聲明組件之類的. Reapp 是最近發佈的基於 React 的一個框架.它是一個專為性能和生產效率而生的移動應用平台.你可以把它看做是一個精心優化過的 UI 組件,並且帶有一套很好的編譯系統,以及許多有用工具,能讓你輕松構建你的應用. Reapp 給我

[转] Immutable 详解及 React 中实践

https://zhuanlan.zhihu.com/p/20295971 作者:camsong链接:https://zhuanlan.zhihu.com/p/20295971来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. Shared mutable state is the root of all evil(共享的可变状态是万恶之源) -- Pete Hunt 有人说 Immutable 可以给 React 应用带来数十倍的提升,也有人说 Immutable

Immutable 详解及 React 中实践

本文转自:https://github.com/camsong/blog/issues/3 Shared mutable state is the root of all evil(共享的可变状态是万恶之源) -- Pete Hunt 有人说 Immutable 可以给 React 应用带来数十倍的提升,也有人说 Immutable 的引入是近期 JavaScript 中伟大的发明,因为同期 React 太火,它的光芒被掩盖了.这些至少说明 Immutable 是很有价值的,下面我们来一探究竟.

vue.js、angularJS、react.js框架比较

下载链接:https://www.yinxiangit.com angularJS特性 模板功能强大丰富(数据绑定大大减少了代码量) 比较完善的前端MVC框架(只要学习这个框架,按照规定往里面填东西就可以完成前端几乎所有的的问题) 引入了Java的一些概念 angularJS的一些问题 性能问题 (脏检查机制)在angular中被废除了,整体性能被提升 路由问题 (使用的是第三方模块)在angular中路由器是主要的机制 作用域问题(不能用原生的事件,想用就要调用一个方法)在angular中任何

node.js学习笔记之React

React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站.做出来以后,发现这套东西很好用,就在2013年5月开源了.由于 React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单.所以,越来越多的人开始关注和使用,认为它可能是将来 Web 开发的主流工具. ReactJS官网地址:http://facebook.github.io/react/ Github地

大二上學期學習生活總結

暑假的時候沒有回家,一直呆在學校學習.雖然沒能在外打工賺錢,不過卻學到了不少知識.對自己未來的付出.比賺生活費更有意義. 大一一年的學習之後,我對自身存在的不足有了進一步認識,也开始了新一波的奮鬥! [專業課] 這個學期幾乎全是專業課,包含C#.網頁設計. 操作系統.Linux.數據庫和計算機網絡. [計算機網絡]之前學過,後來期末考試的時候,感覺又忘得几乎相同了,雖然沒掛科,不過卻應該驚醒,閑了再翻翻看看. [C#]也是之前學過,在學期末的課程設計中使用SQLserver數據庫做了一個學生管理

深入浅出React(一):React的设计哲学 - 简单之美

编者按:自2013年Facebook发布以来,React吸引了越来越多的开发者,基于它的衍生技术,如React Native.React Canvas等也层出不穷.InfoQ精心策划"深入浅出React"系列文章,为读者剖析React开发的技术细节. React最初来自Facebook内部的广告系统项目,项目实施过程中前端开发遇到了巨大挑战,代码变得越来越臃肿且混乱不堪,难以维护.于是痛定思痛,他们决定抛开很多所谓的"最佳实践",重新思考前端界面的构建方式,于是就有