该项目旨在通过实现一种自定义模式来减轻 SPA 开发中的痛苦,该模式以与框架无关、易于测试且对开发人员友好的方式对应用程序状态进行建模。 Sparix 允许您将状态封装在类型安全的存储中,并定义可以在所述状态上发生的转换。更新永远不会改变状态,而是创建一个新的状态转换实例(就像 redux 减速器一样)。转换后的状态序列作为 RxJS Observable<State>
公开提供。
Sparix 是用 TypeScript 编写的,代码示例也是如此。它作为 JavaScript 库进行分发,类型定义嵌入在 NPM 模块中,就像 RxJS 一样。
$ npm i -S sparix rxjs
import { Store } from 'sparix'
import { add } from 'ramda'
export interface CounterState {
count : number
}
const initialState : CounterState = {
count : 0
}
export class Counter extends Store < CounterState > {
constructor ( ) {
super ( initialState )
}
increment ( ) {
// ALL THESE ARE EQUIVALENT
this . update ( state => { count : state . count + 1 } )
this . updateState ( { count : val => val + 1 } )
this . updateState ( { count : add ( 1 ) } ) // Using Ramda's automatically curryied functions
}
}
import { Counter , CounterState } from './counter'
const counter = new Counter ( )
// Recommended way
const state$ : Observable < CounterState > = counter . state$
const count$ : Observable < number > = counter . map ( state => state . count )
// Alternative way (useful for testing)
expect ( counter . currentState . count ) . toEqual ( 0 )
counter . increment ( )
expect ( counter . currentState . count ) . toEqual ( 1 )
首先,它是一种模式。其次,它是基于RxJS的实现。实现非常简单,用另一个反应式库重写它只需要几个小时。然而,由于 SPA 世界将由 React 和 Angular2 主导,而且后者附带 RxJS,因此使用该库作为 sparix 的参考实现是有意义的。
在 sparix 中,状态被建模为Observable<State>
,即状态转换的不可变流。
Store 的 API 保持简单,所有复杂的逻辑都被封装并从外部隐藏,就像使用古老的面向对象编程一样。要测试 Store,您所需要做的就是模拟输入(通过调用其公共方法之一)并检查其输出(状态)。
Sparix 完全遵循 redux 原则(或者更确切地说,Elm 架构原则),其中状态转换被定义为不会改变先前状态的纯函数。
在 redux 中,当您需要更新状态时,您可以调度一个操作。但如果仔细观察,您可能会发现操作可以分为两类:
我的主张是,当目标只是更新单个 Store 的状态(在大多数情况下)时,操作是一种过于繁重的机制。在 sparix 中,Store 可以直接更新其状态,无需更多仪式:
// Increment counter
this . update ( state => ( {
counter : state . counter + 1
} ) )
有一种更细粒度、更具声明性的方式来编写这些状态更新器:
this . updateState ( {
counter : prevCounter => prevCounter + 1
} )
或者甚至更好:
const increment = value => value + 1
this . updateState ( {
counter : increment
} )
好吧,实际上你应该利用 Ramda 的自动柯里化:
import { add } from 'ramda'
this . updateState ( {
counter : add ( 1 )
} )
我喜欢将这些状态更新程序视为匿名操作。在 redux 中,这就像调度一个减速器。但动作创作者呢?好吧,我们真的不需要它们:
const increment = val => val + 1
class SomeStore extends Store < SomeState > {
// constructor
incrementCounter ( ) {
this . updateState ( {
counter : increment
} )
}
}
这里, incrementCounter()
方法是Store公共API的一部分。您不再需要分派由操作创建者创建的全局操作。只需调用方法即可!
Sparix 致力于对应用程序的核心进行建模。但什么是核心呢?或者更确切地说,什么不是核心?
应用程序核心应该是不可知的。与框架和数据库无关,与表示层无关,与数据获取机制和协议无关。它应该对一切都是不可知的。
应用程序核心不知道 HTML、DOM、Angular 或 React、本地存储、HTTP 或 WebSockets.. 它甚至不知道它存在于 Web 浏览器中!相同的应用程序核心应该可以在 Cordova、NativeScript 或 Electron 应用程序中重复使用,而无需更改一行代码!
那么你在核心中放了什么?答案很简单:其他一切!如果它可以成为核心的一部分,那么它就应该成为核心的一部分。所有的业务逻辑、数据转换、交互逻辑都应该建模为应用程序核心。除了用于建模的编程语言之外,所有这些都不应该依赖于其他任何东西。
回到sparix。它将帮助您建模不依赖于第三方库和框架的应用程序核心,但 RxJS 和 sparix 本身除外。但这不是什么大问题。 Observables 正在成为标准 ECMAScript 功能,而 sparix 是一个非侵入式库,这使得使用它可以轻松地仅对应用程序核心的子集进行建模。