再見Flux, 歡迎Bacon/Rx?

Facebook的Flux和React.js剛剛變得很火,大有剿滅MVC之勢,如今又有人提出再見Flux,新的Bacon/Rx有哪些優點呢?

Facebook一年前引入Flux架構,它是客戶端創建Web應用的最新革新,是對Angular.js的前端MVC的提高與革命,現在已經成爲Web開發者最火熱的技術。

Flux是使用dispathcer將業務邏輯從用戶界面中分離出來,核心思想是單向數據流,這意味着響應用戶動做的事件的傳播能夠單向傳遍整個系統,不須要綁定任何內部數據模型:前端


這種單向數據流與React的虛擬DOM綁定,使得Flux實現更加簡潔,由於沒有必要進行狀態區分(虛擬DOM框架實現這個一功能),可是Flux是重量的,它引入了多個模板(如 事件發射器、監聽器等等),有一些企業軟件的繁瑣酸味,如按約定編程與分層結構。

函數響應式編程Functional Reactive Programming (FRP) 是一種新的編程範式,事件建模爲事件流,事件流是相似一個不變的數組,它們可以被map 過濾 combine和merge,數組和事件流的區別是事件流中的事件是異步的,每次當事件發生時,它會被經過流傳播,最終被訂閱者消費使用。

顧名思義,Reactive響應式編程是React的本質,發生的動做經過事件流傳播,這些事件流結合起來造成應用的狀態,當事件經過系統傳播之後,新的應用狀態對象致使了狀態的訂閱者,它會經過根級別的React組件從新渲染:

整個狀態廣播的邏輯能使用下面代碼實現,使用的是Bacon.js做爲FRP庫:react

// app.js
const React   = require('react'),
     Bacon   = require('baconjs'),
     TodoApp = require('./todoApp'),
     todos   = require('./todos'),
     filter  = require('./filter')

const filterP = filter.toProperty(<initial filter>),
     itemsP  = todos.toItemsProperty(<initial items>, filterP)

const appState = Bacon.combineTemplate({
 items: itemsP,
 filter: filterP
})

相比Flux,這樣作的好處是再也不須要分離動做和存儲,事件流很是簡單,取而代之是一個業務組件,它有一個公共API經過本地dispatcher與業務邏輯進行通信:git

// todos.js
const Bacon       = require('baconjs'),
     Dispatcher  = require('./dispatcher')

const d = new Dispatcher()

module.exports = {
 toItemsProperty: function(initialItems, filterP) {
   
// "business logic"    
   const itemsP = Bacon.update(initialItems,
     [d.stream('remove')], removeItem,
     [d.stream('create')], createItem,
     ...
   )
   return Bacon
     .combineAsArray([itemsP, filterP])
     .map(setItemsDisplayStatusBasedOnFilter)

   function createItem(items, newItemTitle) {
     return items.concat([{<new item data>}])
   }

   function removeItem(items, itemIdToRemove) {
     return items.filter(it => it.id !== itemIdToRemove)
   }
   ...
 },

 
// "public API"

 createItem: function(title) {
   d.push('create', title)
 },
 removeItem: function(itemId) {
   d.push('remove', itemId)
 },

 ...
}

dispatcher.js是一個可發佈的事件流,事件流可以被訂閱和消費:程序員

// dispatcher.js
const Bacon = require('baconjs')

module.exports = function() {
 const busCache = {}  
// Bus == Subject in Rx

 this.stream = function(name) {
   return bus(name)
 }
 this.push = function(name, value) {
   bus(name).push(value)
 }
 this.plug = function(name, value) {
   bus(name).plug(value)
 }

 function bus(name) {
   return busCache[name] = busCache[name] || new Bacon.Bus()
 }
}

視圖代碼以下,視圖會變得簡單,沒有回調,監聽者,React的虛擬DOM會作剩餘的工做,咱們只要同步公共API去調用業務邏輯:github

// todoItem.jsx
const React = require('react'),
     todos = require('./todos')

module.exports = React.createClass({
 render: function() {
   const item = this.props.item
   return (
     <li className={item.states.join(' ')}>
       <div className=
"view">
         <label>{item.name}</label>
         ...
         <button
           className=
"destroy"
           onClick={() => todos.removeItem(item.id)}
           />
       </div>
     </li>
   )
 }
})

以上代碼演示見:TodoMVC project

Good bye Flux, welcome Bacon/Rx? — Medium

點評:Bacon/Rx的事件流相似事件總線,雖然很簡單,可是理解起來有必定難度,而Flux雖然繁瑣一些,可是一板一眼,對於前端程序員比較容易理解。
編程

相關文章
相關標籤/搜索