ffux, RxJS和 Bacon.js的功能反应通量

分享于 

15分钟阅读

GitHub

  繁體 雙語
lolbal flux
  • 源代码名称:ffux
  • 源代码网址:http://www.github.com/milankinen/ffux
  • ffux源代码文档
  • ffux源代码下载
  • Git URL:
    git://www.github.com/milankinen/ffux.git
    Git Clone代码到本地:
    git clone http://www.github.com/milankinen/ffux
    Subversion代码到本地:
    $ svn co --depth empty http://www.github.com/milankinen/ffux
    Checked out revision 1.
    $ cd repo
    $ svn up trunk
    
    ffux

    具有 Bacon.js 或者 RxJS功能React程序的通量实现。

    npm versionBuild Statuscodecov.io

    Gitter

    动机

    流量是当今React圈中最热门的关键词。 尽管最新的流量实现已经转移到功能范例,但是采用功能性无功编程的模板。复杂性和潜在 Bug。

    本项目的目的是去除所有的复杂性,提供一种简单的方法来利用玻璃的力量。

    为什么选择 ffux?

    以下是在其他通量实现中选择 ffux的一些点:

    • Expressive Expressive FRP FRP FRP
    • 非常简单的 - 只需要两个功能: createStoreffux
    • 如果需要,库/视图不可知 DropReact并与jQuery一起使用;)
    • 显式 - 查看你的应用程序的结构
    • 轻量级 - 整个库位于 ES6 200 LOC。 :- )

    嗯。: 你一定要自己看看

    constReact=require("react"),
     ffux =require("ffux")const {Listener} =require("ffux/react")const {createStore} = ffuxconstCounter=createStore({
     actions: ["incrementN", "decrementOne"],
     state: (initialState, actionStreams) => {
     const {incrementN, decrementOne} = actionStreams
     // All Bacon.js tricks are permitted here!return incrementN
    . merge(decrementOne.map(-1))
    . scan(initialState, (state, delta) => state + delta)
     }
    })constCounterApp=React.createClass({
     render() {
     // ffux model contains two properties:// *"state" contains the current state of the application// *"actions" contains the action creators that can be invokedconst {counter} =this.props.state// action creators are just functions that can be invoked with arguments normallyconst {counter: {incrementN, decrementOne}} =this.props.actionsreturn (
     <div><div>Counter: {counter}</div><button onClick={() =>incrementN(2)}>+2</button><button onClick={() =>decrementOne()}>-</button></div> )
     }
    })constApp=React.createClass({
     render() {
     return (
     <Listener initialState={{counter:10}}
     dispatcher={state=>ffux({counter:Counter(state.counter)})}><CounterApp /></Listener> )
     }
    })React.render(<App />, document.getElementById("app"))

    :如何使用

    使用npm安装依赖项并开始编码。 对于 Bacon.js 用户:

    
    npm i --save ffux baconjs
    
    
    
    

    对于RxJS用户:

    
    npm i --save ffux rx
    
    
    
    

    API

    ffux 设计用于 ES6,但它也可以与ES5一起使用。 为了使用 fflux,你必须将它要求到你的项目中:

    如果你使用的是 Bacon.js:

    constffux=require("ffux")

    如果你使用的是 RxJS:

    constffux=require("ffux/rx")

    createStore({[actions,] state}) -> StoreDef

    在给定的动作和状态流中创建新的存储定义 Having。

    函数获取可以具有两个字段的对象:

    • state: 返回 Bacon.Property 或者 Rx.Observable的强制字段
    • actions: 包含存储操作的可选 array。 这些动作被作为 eventstreams/observables传递给 state 函数
    // Bacon.jsconstCounter=ffux.createStore({
     actions: ["increment", "resetAsync"],
     // Parameters in state initialization function are:// 1. initial state// 2. action streams of this store (Bacon.EventStreams) mapped behind their names// 3. dependencies if any (see below)state: (counter, {increment, resetAsync}) => {
     // Here comes all the business logic of the store!// You are free to implement the data flow by using whatever FRP means you wantconstresetS=resetAsync.delay(1000)
     constcounterP=Bacon.update(counter,
     [increment], (state, delta) => state + delta,
     [resetS], _=>0 )
     // the only restriction of the store is that it must return Bacon.propertyreturn counterP
     }
    })// RxJSconstCounter=ffux.createStore({
     actions: ["increment"],
     // same parameters as Bacon.js but now actions Rx.Observable instancesstate: (counter, {increment}) => {
     // state function must return an Rx.Observablereturn increment
    . scan(counter, (state, delta) => state + delta)
    . startWith(counter)
     }
    })

    ffux({<prop1>: Store, <prop2>: Store,.. .}) -> Dispatcher

    通过使用 StoreDef 函数,你可以实例化实际的存储实例。 为了实例化一个存储实例,你必须使用存储状态的dinitial调用 StoreState 函数:

    constcounter=Counter(10)constFilter=Filter("") // another store

    创建了存储实例之后,你可以使用它们来形成你的应用程序状态模型。 状态模型只是一个包含存储实例作为值的平面JavaScript对象。 但是,这个状态模型应该反映你的状态模型。

    conststateModel= {counter:Counter(10), filter:Filter("")}

    你可以使用状态模型创建 ffux 调度程序。 创建的dispacher有一个方法: .listen(callback) 它可以用来监听你的状态变化。 当应用程序状态更改时,包含当前状态( 模式反映状态模型) 和动作创建者的{state, actions} 对象将传递给回调函数。

    constdispatcher=ffux({counter})dispatcher.listen(({state, actions}) => {
     // state == {counter: 10}// actions == {counter: {increment: <function>, resetAsync: <function>}}React.render(<MyApp state={state} actions={actions} />, ...)
    })

    操作创建者

    创建dispatcher实例并开始侦听状态模型更改后,可以使用存储的操作创建者ffux 根据你的StoreDef 操作自动创建这些操作创建者。 这些操作创建者只是可以从 inside 访问 dispacher.listen 回调的普通函数:

    constFilter=ffux.createStore({
     actions: ["resetFilter"],
     state: (initialState, {resetFilter}) => {
     ... }
    })ffux({filter:Filter("")}).listen(({state, actions}) => {
     constresetFilter=actions.filter.resetFilterconsole.log(resetFilter) // =>"function"})

    动作创建者可以用零。一个或者多个参数来调用。 使用零或者一个参数调用操作创建者时,该参数将传递给动作事件流,方法如下:

    constFilter=ffux.createStore({
     actions: ["resetFilter"],
     state: (initialState, {resetFilter}) => {
     consttrimmed=resetFilter.map(value=>value.trim())
     ... }
    })// and usage inside your appresetFilter("tsers")

    由于事件流发出单个事件,因这里将多个参数转换为传递给事件流的array:

    constFilter=ffux.createStore({
     actions: ["resetFilter"],
     state: (initialState, {resetFilter}) => {
     consttrimmedAsync=resetFilter.flatMap(([value, timeout]) =>...)
     ... })
    })// and usage inside your appresetFilter("tsers", 1000)
    拼合动作创建者

    默认情况下,操作创建者使用与状态模型相同的模式传递。 但是,你可以通过将 flatActions = true 选项传递给dispatcher来将它们展平为 actions 对象。 在这种情况下,如果操作创建者名称冲突,则在调度程序初始化期间引发错误:

    constdispatcher=ffux({counter, filter}, {flatActions:true})dispatcher.listen(({state, actions}) => {
     // actions == {increment: <function>, resetAsync: <function>, resetFilter: <function>}React.render(<MyApp state={state} actions={actions} />, ...)
    })

    声明存储间的依赖关系

    在复杂的应用中,依赖是不可避免的。 正常流量实现使用信号并发布订阅来解决这里问题。 这里方法提供了非常解耦的组件,但它有一个主要缺点: 当依赖变得更加复杂时,它们的管理变得混乱和不可预测,因为原因不可见,因此引入 比如 循环依赖关系是很有可能的。

    ffux 采用了另一种方法:存储间的依赖被显式声明。 这确保你考虑存储的职责,并减少循环依赖性和类似的Bug的可以能性。

    ffux 中,通过将依赖项作为第二个参数传递到存储区,可以在存储实例。 它们可以是任何- 其他存储,函数或者常量。

    consttodos=Todos([], {...here comes the dependencies...})
    存储是事件流 !

    在定义依赖关系时,这非常重要。 由于这些存储是事件流,所以可以将它们视为操作。 假设你必须执行依赖于当前过滤器值(。为了检测要显示的项目)的项。 由于 Filter 存储是事件流,因此可以将它的作为依赖项传递到 Todos 存储,并直接获取筛选器更改:

    constTodos=createStore({
     state: (items, {}, {filter}) => {
     // every time when filter changes, it will run the filtering again, thus// causing a state change with the new displayed itemsreturnBacon.combineTemplate({items, filter})
    . map(({items, filter}) =>items.filter(it=>it.indexOf(filter) !==-1))
     }
    })// pass filter like any other dependencyconstfilter=Filter("")consttodos=Todos([], {filter})ffux({todos, filter}).listen(...)

    使用响应 helper-组件

    ffux 提供了一个用于响应开发的helper 组件: <Listener>Listener 具有两个属性: initialStatedispatcher。第一个是表示应用程序初始状态的JavaScript对象。 第二个是函数 (state) => Dispatcher,它从状态对象构建一个。

    将应用程序组件与侦听器环绕: 状态和动作自动传播到应用程序组件。

    const {Listener} =require("ffux/react")constApp=React.createClass({
     render() {
     return (
     <Listener initialState={{counter:10}}
     dispatcher={state=>ffux({counter:Counter(state.counter)})}><MyAppComponent /></Listener> )
     }
    })React.render(<App />, document.getElementById("app"))
    热( re ) 加载

    是。Listener 组件在默认情况下是热加载的。

    基于的同构应用开发

    ffux 对同构应用程序开发具有原生支持。 创建 ffux dispatcher后,可以使用 .take(callback) 方法获取初始状态,并使用获取的模型在服务器中呈现你的应用程序:

    // appState.jsexportdefaultfunctionappState({filter: initFilter ="", todos: initTodos = []}) {
     constfilter=Filter(initFilter)
     consttodos=Todos(initTodos, {filter})
     returnffux({todos, filter})
    }

    在服务器中:

    
    //server.js
    
    
    const state = loadFromDB()
    
    
    appState(state).take(model => {
    
    
     res.send(`<html>
    
    
     <head></head>
    
    
     <body>
    
    
     <div id="app">${React.renderToString(<YourApp {...model}/>)}</div>
    
    
     <script type="text/javascript">
    
    
     window.INITIAL_STATE = ${JSON.stringify(model.state)};
    
    
     </script>
    
    
     <script type="text/javascript" src="site.js"></script>
    
    
     </body>
    
    
     </html>`)
    
    
    })
    
    
    
    

    在浏览器中:

    // site.jsappState(window.INITIAL_STATE).listen(model=> {
     React.render(<YourApp {...model} />, document.getElementById("app"))
    })

    有关更多信息,请参见 examples 文件夹中的同构示例。

    停止调度程序

    调度器 .listen 方法返回可以被调用以停止事件侦听的停止函数。 调用stop方法后,不会调度任何新事件。

    constdispatcher=ffux(...)conststop=dispatcher.listen(model=> { ... })// you can stop listening events by calling the returned stop functionstop()

    通常,你不应该从应用程序调用 stop 方法,但是如果你正在实现 比如,则可以能有用。

    许可证

    MIT


    react  FUNC  函数  functional  FLUX  RXJS  
    相关文章