Flux 架構入門教程
過去一年中,前端技術大發展,最耀眼的明星就是React。
React 本身只涉及UI層,如果搭建大型應用,必須搭配一個前端框架。也就是說,你至少要學兩樣東西,才能基本滿足需要:React + 前端框架。
Facebook官方使用的是 Flux 框架。本文就介紹如何在 React 的基礎上,使用 Flux 組織程式碼和安排內部邏輯,使得你的應用更易於開發和維護。
閱讀本文之前,我假設你已經掌握了 React 。如果還沒有,可以先看我寫的《React入門教程》。與以前一樣,本文的目標是使用最簡單的語言、最好懂的例子,讓你一看就會。
一、Flux 是什麼?
簡單說,Flux 是一種架構思想,專門解決軟體的結構問題。它跟MVC 架構是同一類東西,但是更加簡單和清晰。
Flux存在多種實現(至少15種),本文采用的是Facebook官方實現。
二、安裝 Demo
為了便於講解,我寫了一個Demo。
請先安裝一下。
$ git clone https://github.com/ruanyf/extremely-simple-flux-demo.git $ cd extremely-simple-flux-demo && npm install $ npm start
然後,訪問 http://127.0.0.1:8080 。
你會看到一個按鈕。這就是我們的Demo。
三、基本概念
講解程式碼之前,你需要知道一些 Flux 的基本概念。
首先,Flux將一個應用分成四個部分。
- View: 檢視層
- Action(動作):檢視層發出的訊息(比如mouseClick)
- Dispatcher(派發器):用來接收Actions、執行回撥函式
- Store(資料層):用來存放應用的狀態,一旦發生變動,就提醒Views要更新頁面
Flux 的最大特點,就是資料的”單向流動”。
- 使用者訪問 View
- View 發出使用者的 Action
- Dispatcher 收到 Action,要求 Store 進行相應的更新
- Store 更新後,發出一個”change”事件
- View 收到”change”事件後,更新頁面
上面過程中,資料總是”單向流動”,任何相鄰的部分都不會發生資料的”雙向流動”。這保證了流程的清晰。
讀到這裡,你可能感到一頭霧水,OK,這是正常的。接下來,我會詳細講解每一步。
四、View(第一部分)
請開啟 Demo 的首頁index.jsx
,你會看到只載入了一個元件。
// index.jsx var React = require('react'); var ReactDOM = require('react-dom'); var MyButtonController = require('./components/MyButtonController'); ReactDOM.render( <MyButtonController/>, document.querySelector('#example') );
上面程式碼中,你可能注意到了,元件的名字不是 MyButton
,而是 MyButtonController
。這是為什麼?
這裡,我採用的是 React 的 controller view 模式。”controller view”元件只用來儲存狀態,然後將其轉發給子元件。MyButtonController
的原始碼很簡單。
// components/MyButtonController.jsx var React = require('react'); var ButtonActions = require('../actions/ButtonActions'); var MyButton = require('./MyButton'); var MyButtonController = React.createClass({ createNewItem: function (event) { ButtonActions.addNewItem('new item'); }, render: function() { return <MyButton onClick={this.createNewItem} />; } }); module.exports = MyButtonController;
上面程式碼中,MyButtonController
將引數傳給子元件MyButton
。後者的原始碼甚至更簡單。
// components/MyButton.jsx var React = require('react'); var MyButton = function(props) { return <div> <button onClick={props.onClick}>New Item</button> </div>; }; module.exports = MyButton;
上面程式碼中,你可以看到MyButton
是一個純元件(即不含有任何狀態),從而方便了測試和複用。這就是”controll view”模式的最大優點。
MyButton
只有一個邏輯,就是一旦使用者點選,就呼叫this.createNewItem
方法,向Dispatcher發出一個Action。
// components/MyButtonController.jsx // ... createNewItem: function (event) { ButtonActions.addNewItem('new item'); }
上面程式碼中,呼叫createNewItem
方法,會觸發名為addNewItem
的Action。
五、Action
每個Action都是一個物件,包含一個actionType
屬性(說明動作的型別)和一些其他屬性(用來傳遞資料)。
在這個Demo裡面,ButtonActions
物件用於存放所有的Action。
// actions/ButtonActions.js var AppDispatcher = require('../dispatcher/AppDispatcher'); var ButtonActions = { addNewItem: function (text) { AppDispatcher.dispatch({ actionType: 'ADD_NEW_ITEM', text: text }); }, };
上面程式碼中,ButtonActions.addNewItem
方法使用AppDispatcher
,把動作ADD_NEW_ITEM
派發到Store。
六、Dispatcher
Dispatcher 的作用是將 Action 派發到 Store、。你可以把它看作一個路由器,負責在 View 和 Store 之間,建立 Action 的正確傳遞路線。注意,Dispatcher 只能有一個,而且是全域性的。
Facebook官方的 Dispatcher 實現輸出一個類,你要寫一個AppDispatcher.js
,生成 Dispatcher 例項。
// dispatcher/AppDispatcher.js var Dispatcher = require('flux').Dispatcher; module.exports = new Dispatcher();
AppDispatcher.register()
方法用來登記各種Action的回撥函式。
// dispatcher/AppDispatcher.js var ListStore = require('../stores/ListStore'); AppDispatcher.register(function (action) { switch(action.actionType) { case 'ADD_NEW_ITEM': ListStore.addNewItemHandler(action.text); ListStore.emitChange(); break; default: // no op } })
上面程式碼中,Dispatcher收到ADD_NEW_ITEM
動作,就會執行回撥函式,對ListStore
進行操作。
記住,Dispatcher 只用來派發 Action,不應該有其他邏輯。
七、Store
Store 儲存整個應用的狀態。它的角色有點像 MVC 架構之中的Model 。
在我們的 Demo 中,有一個ListStore
,所有資料都存放在那裡。
// stores/ListStore.js var ListStore = { items: [], getAll: function() { return this.items; }, addNewItemHandler: function (text) { this.items.push(text); }, emitChange: function () { this.emit('change'); } }; module.exports = ListStore;
上面程式碼中,ListStore.items
用來儲存條目,ListStore.getAll()
用來讀取所有條目,ListStore.emitChange()
用來發出一個”change”事件。
由於 Store 需要在變動後向 View 傳送”change”事件,因此它必須實現事件介面。
// stores/ListStore.js var EventEmitter = require('events').EventEmitter; var assign = require('object-assign'); var ListStore = assign({}, EventEmitter.prototype, { items: [], getAll: function () { return this.items; }, addNewItemHandler: function (text) { this.items.push(text); }, emitChange: function () { this.emit('change'); }, addChangeListener: function(callback) { this.on('change', callback); }, removeChangeListener: function(callback) { this.removeListener('change', callback); } });
上面程式碼中,ListStore
繼承了EventEmitter.prototype
,因此就能使用ListStore.on()
和ListStore.emit()
,來監聽和觸發事件了。
Store 更新後(this.addNewItemHandler()
)發出事件(this.emitChange()
),表明狀態已經改變。 View 監聽到這個事件,就可以查詢新的狀態,更新頁面了。
八、View (第二部分)
現在,我們再回過頭來修改 View ,讓它監聽 Store 的 change
事件。
// components/MyButtonController.jsx var React = require('react'); var ListStore = require('../stores/ListStore'); var ButtonActions = require('../actions/ButtonActions'); var MyButton = require('./MyButton'); var MyButtonController = React.createClass({ getInitialState: function () { return { items: ListStore.getAll() }; }, componentDidMount: function() { ListStore.addChangeListener(this._onChange); }, componentWillUnmount: function() { ListStore.removeChangeListener(this._onChange); }, _onChange: function () { this.setState({ items: ListStore.getAll() }); }, createNewItem: function (event) { ButtonActions.addNewItem('new item'); }, render: function() { return <MyButton items={this.state.items} onClick={this.createNewItem} />; } });
上面程式碼中,你可以看到當MyButtonController
發現 Store 發出 change
事件,就會呼叫 this._onChange
更新元件狀態,從而觸發重新渲染。
// components/MyButton.jsx var React = require('react'); var MyButton = function(props) { var items = props.items; var itemHtml = items.map(function (listItem, i) { return <li key={i}>{listItem}</li>; }); return <div> <ul>{itemHtml}</ul> <button onClick={props.onClick}>New Item</button> </div>; }; module.exports = MyButton;
九、致謝
本文受到了Andrew Ray 的文章《Flux For Stupid People》的啟發。
相關文章
- 【譯】Flux入門UX
- Android Flux架構初探AndroidUX架構
- 入門MySQL——架構篇MySql架構
- Hadoop的HDFS架構入門Hadoop架構
- 詳解安卓架構入門安卓架構
- webpack自動化架構入門Web架構
- 六邊形架構入門 - levelup架構
- LangGraph入門:構建ReACT架構的智慧AgentReact架構
- Apache Kylin 入門 2 - 原理與架構Apache架構
- UML 架構圖入門介紹 starUML架構
- iOS架構入門 - MVC模式例項演示iOS架構MVC模式
- 函式式DDD架構入門 - SCOTT WLASCHIN函式架構
- 從零入門 Serverless | 架構的演進Server架構
- Pulsar 入門實戰(2)--特性及架構架構
- [雲原生微服務架構](九)入門HELM微服務架構
- 整合spring cloud雲架構 - Gateway的基本入門SpringCloud架構Gateway
- 大資料架構師從入門到精通大資料架構
- elastic-job-lite 入門以及架構原理分析AST架構
- Netty-架構設計及入門程式-3Netty架構
- Android入門教程 | RecyclerView使用入門AndroidView
- C#快速入門教程(12)—— if語句結構C#
- Termux 入門教程:架設手機 Server 下載檔案UXServer
- spring cloud微服務分散式雲架構-Gateway入門SpringCloud微服務分散式架構Gateway
- 系統架構基礎知識入門指南-下架構
- 系統架構基礎知識入門指南-上架構
- 微服務架構學習Day01-SpringBoot入門微服務架構Spring Boot
- AudioKit 入門教程
- awk 入門教程
- Maven入門教程Maven
- Aseprite入門教程
- Electron入門教程
- HBase入門教程
- RabbitMQ入門教程MQ
- CMake入門教程
- SnapKit入門教程APK
- Linux入門教程Linux
- Springboot入門教程Spring Boot
- Vuex入門教程Vue
- Webpack 入門教程Web