再见Flux, 欢迎Bacon/Rx?

Facebook的Flux和React.js刚刚变得很火,大有剿灭MVC之势,现在又有人提出再见Flux,新的Bacon/Rx有哪些优势呢?

Facebook一年前引入Flux架构,它是客户端建立Web应用的最新革新,是对Angular.js的前端MVC的提升与革命,如今已经成为Web开发者最火热的技术。

Flux是使用dispathcer将业务逻辑从用户界面中分离出来,核心思想是单向数据流,这意味着响应用户动作的事件的传播可以单向传遍整个系统,不需要绑定任何内部数据模型:

20231905_lpny.png
这种单向数据流与React的虚拟DOM绑定,使得Flux实现更加简洁,因为没有必要进行状态区分(虚拟DOM框架实现这个一功能),但是Flux是重量的,它引入了多个模板(如 事件发射器、监听器等等),有一些企业软件的繁琐酸味,如按约定编程与分层结构。

函数响应式编程Functional Reactive Programming (FRP) 是一种新的编程范式,事件建模为事件流,事件流是类似一个不变的数组,它们能够被map 过滤 combine和merge,数组和事件流的区别是事件流中的事件是异步的,每次当事件发生时,它会被通过流传播,最终被订阅者消费使用。

顾名思义,Reactive响应式编程是React的本质,发生的动作通过事件流传播,这些事件流结合起来形成应用的状态,当事件通过系统传播以后,新的应用状态对象导致了状态的订阅者,它会通过根级别的React组件重新渲染:

整个状态广播的逻辑能使用下面代码实现,使用的是Bacon.js作为FRP库:

// 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与业务逻辑进行通讯:

// 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去调用业务逻辑:

// 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虽然繁琐一些,但是一板一眼,对于前端程序员比较容易理解。

转载于:https://my.oschina.net/quicker/blog/469170

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值