Bobril - II - Bobflux 应用程序架构
对 bobril 的 bobflux 应用程序架构的解释
引言
在上一篇文章中,我们学习了如何使用 bobril 创建一个页面(Web 应用程序的视图部分)。在本文中,我们将学习如何通过 bobflux 添加应用程序逻辑。
- Bobril - I - 入门
- Bobril - II - Bobflux 应用程序架构
- Bobril - III - 本地化和格式化
- Bobril - IV - 路由
- Bobril - V - Bobril-build
- Bobril - VI - BobX 应用程序商店管理
- Bobril - VII - 组件和 TSX
背景
Bobflux 是一个纯函数式前端应用架构,它基于 flux,并受到 reflux 和 redux 的启发。它由 Karel Steinmetz(GMC Software Technology 的软件开发人员)编写。
Bobflux 的生命周期可以用下图描述:
- 应用程序具有一些 状态
- 视图 根据此状态进行渲染
- 视图调用由 action creator 创建的 actions 的处理程序
- Actions 改变状态
有关更多信息,请参阅 项目页面。
开始吧
我们将创建一个简单的 TODO 应用程序。首先,我们需要在计算机上准备好 bobril-build。请按照上一篇文章中的步骤执行 bobril-build
的安装。
现在,您可以重新开始一个新项目,或者使用 bobril-build github 仓库中预定义的骨架 simpleApp
:bobril-build github repository。
以下示例将使用它。要获取最终代码,请下载完整的示例。
将 bobflux 添加到应用程序
在应用程序文件夹的根目录下运行以下命令:
npm i
npm i bobflux --save
bb
状态
首先,我们需要准备应用程序的 状态、它的 cursor 以及用于创建默认状态的函数。
要定义状态,请添加文件 src/state.ts,其内容如下:
import * as flux from 'bobflux';
export interface ITodoAppState extends flux.IState {
todos: string[];
todoName: string;
}
export const todoAppCursor: flux.ICursor<ITodoAppState> = {
key: ''
};
export function createDefaultTodoAppState(): ITodoAppState {
return {
todos: [],
todoName: ''
};
}
State 是存储应用程序数据的地方。状态与上下文的区别在于,上下文存储仅供组件自身使用的数据,而应用程序的其余部分不关心它们。例如,一个部分组件是否折叠的信息(当您不需要从外部管理它时)。
在我们的 todo 应用程序中,我们需要存储 todos 列表以及当前输入的 todo 名称,因此我们必须为这些数据定义 ITodoAppState
。
Cursor 定义了应用程序状态中状态的路径(键)。在我们的示例中,我们将仅使用根应用程序光标,因此 todoAppCursor.key
中的路径是空的。
在实际应用中,建议定义尽可能多的特定光标,以获得最佳的 bobflux 性能优势。这意味着,例如,创建一个光标,如:
export const todoNameCursor: flux.ICursor<ITodoAppState> = {
key: 'todoName'
};
为了简单起见,我们的示例仅使用根 todoAppCursor
。
现在,我们需要初始化 bobflux 并向其提供应用程序状态,因此请将 src/app.ts 更改为如下所示:
import * as b from 'bobril';
import * as flux from 'bobflux';
import * as todoState from './state';
import { mainPage } from './mainPage';
flux.bootstrap(todoState.createDefaultTodoAppState());
b.routes(
b.route({ handler: mainPage })
);
现在,我们已经准备好了可以通过调用 actions 来修改的应用程序状态。
Actions
Actions 通过在由光标定义的特定子状态上运行的处理程序来更改状态。在我们的 todo 示例中,我们将需要执行两个 actions:
- 根据
textbox
的值更改当前 todo 的名称 - 将输入的 todo 添加到 todo 列表中
因此,我们将添加文件 src/actions/changeTodoName.ts
import * as flux from 'bobflux';
import { ITodoAppState, todoAppCursor } from '../state';
export const changeTodoName =
flux.createAction(todoAppCursor,
(state: ITodoAppState, todoName: string): ITodoAppState => {
if (todoName === state.todoName)
return state;
return flux.shallowCopy(state, copy => { copy.todoName = todoName; });
});
export default changeTodoName
和 src/actions/addTodo.ts
import * as flux from 'bobflux';
import { todoAppCursor } from '../state';
export const addTodo = flux.createParamLessAction(todoAppCursor, state => {
if (!state.todoName || state.todoName.trim().length === 0)
return state;
return flux.shallowCopy(state, copy => {
copy.todos = [...state.todos, state.todoName];
copy.todoName = '';
});
});
export default addTodo;
changeTodoName
action 由 bobflux 中的 createAction
函数定义,该函数接受将要更改其状态的光标以及用于更改的那个处理程序。
在处理程序的开头,会检查我们是否想要更改任何内容。
- 如果不是,则返回原始状态实例。
- 如果是,则创建状态的浅拷贝并返回修改后的副本。
Bobflux 遵循不可变性原则,以保持最佳性能。它将输入状态与输出状态进行比较,如果不同,则调用 b.invalidate
来重新渲染视图。必须注意复制的对象属性。如果存在像 addTodo
action 中的 todos 数组这样的引用对象,也必须将其复制。
使用 bobflux 组合页面
现在,我们已经准备好在 todo 应用程序的页面上使用所有内容了。因此,让我们将 src/mainPage.ts 更改为如下所示:
import * as b from 'bobril';
import * as flux from 'bobflux';
import { todoAppCursor } from './state';
import { changeTodoName } from './actions/changeTodoName';
import { addTodo } from './actions/addTodo';
import { button } from './components/button';
import { textbox } from './components/textbox';
import { p } from './components/paragraph';
import { h1 } from './components/header';
export const mainPage = b.createComponent({
render(_ctx: b.IBobrilCtx, me: b.IBobrilNode): void {
const state = flux.getState(todoAppCursor);
me.children = [
h1({}, 'TODO'),
p({}, [
textbox({ value: state.todoName,
onChange: newValue => changeTodoName(newValue) }),
button({ title: 'ADD', onClick: () => addTodo() })
]),
state.todos.map(item => p({}, item)),
p({}, `Count: ${state.todos.length}`)
];
}
});
export default mainPage;
组件定义不是本文的主题,因此您可以使用附加源代码中的定义。
您可以看到,页面通过 todoAppCursor
定义的 getState
函数解析当前应用程序状态。可以这样完成,因为 bobflux 在状态的每次更改时都会初始化页面的渲染。
textbox
和 button
组件在其 onChange
和 onClick
回调中使用定义的 actions,因此来自视图的用户交互会触发 actions 调用。
最后,在 render
函数的末尾,将 todos 数组映射到带有 todo 名称的 'p'
标签。
现在,我们可以在浏览器中打开应用程序并查看其工作原理。是的,它确实就是这么简单。
为了调试状态历史,您也可以尝试 bobflux-monitor(请参阅项目页面或示例)。
Bobflux 还包含一些性能辅助工具,例如 createRouteComponent
,用于优化组件渲染或在上下文中提供状态等。
要获取更多信息,请参阅项目 github 页面
历史
- 2017-07-30:修订(bobril-build@0.71.0, boril@7.3.2, TS 2.4.2)
- 2017-02-01:修订(bobril-build@0.59.2, bobril@5.2.1)
- 2016-11-04:修订
- 2016-05-29:添加了新的编码标准
- 2015-12-16:更改为基于 bobril-build 的 simpleApp
- 2015-11-15:为 bobflux 1.0.0 版本创建了文章