探索Flux架构中的视图组件与库应用
1. Flux架构中视图组件的另类选择
一般而言,Flux架构应尽可能减少存储(store)的数量。但当在视图层使用Handlebars时,可能会影响存储的设计。例如,为减少DOM结构重新插入文档,可能会对应用的整体状态进行拆分。
1.1 事件处理
在现代Web框架出现之前,jQuery就致力于解决跨浏览器的事件处理问题。尽管其API多年来有所变化,但强大的事件处理能力始终未变。在使用jQuery和Handlebars构建视图时,这一能力尤为重要。
事件处理的主要挑战在于,每次Handlebars模板更新时都要重新渲染元素。我们不希望每次将DOM元素插入DOM时都重新附加事件处理程序。ReactJS采用的策略是不直接将事件处理程序绑定到要监听的元素上,而是绑定到body元素,当事件冒泡时调用相应的处理程序。这种方法具有性能优势,避免了反复将相同的处理函数绑定到同一元素上。
以下是使用jQuery实现类似效果的示例。首先来看Handlebars模板文件,以了解所处理的UI类型。增加了一个反转按钮和选择功能,新的项目视图模板如下:
<a href="#{{@index}}" style="font-weight: {{fontWeight}}">
<span style="text-transform: capitalize">{{first}}</span>
<span style="text-transform: capitalize">{{last}}</span>
</a>
项目现在是一个链接,可使用
@index
Handlebars语法访问当前项在集合中的索引。主列表视图Handlebars模板如下:
<button>Reverse</button>
<ul>
{{#each users}}
<li>{{> item-view}}</li>
{{/each}}
</ul>
现在有一个新按钮用于反转列表的排序顺序。视图组件的事件处理能力代码如下:
import template from './list-view.hbs';
import { reverse } from '../actions/reverse';
import { select } from '../actions/select';
import myStore from '../stores/my-store';
export default class ListView {
constructor(element) {
this.element = element;
myStore.on('change', (state) => {
this.render(state);
});
this.element
.on('click', 'button', reverse)
.on('click', 'a', (e) => {
e.preventDefault();
let index = +(/(\d+)$/).exec(e.currentTarget.href)[1];
select(index);
});
}
render(state = myStore.state) {
this.element.html(template(state));
return this;
}
}
这里遵循了React的模式,事件处理程序不会直接附加到频繁重新渲染的元素上。第一个处理程序用于反转按钮,使用
reverse()
动作创建函数;第二个处理程序在用户点击链接时调用,阻止默认浏览器行为并调度选择事件。
为支持新的事件行为,对存储进行了一些更改。存储的初始状态如下:
var state = {
users: [
{
first: 'first 1',
last: 'last 1',
fontWeight: 'normal'
},
{
first: 'first 2',
last: 'last 2',
fontWeight: 'normal'
},
{
first: 'first 3',
last: 'last 3',
fontWeight: 'normal'
}
]
};
存储类
MyStore
的代码如下:
import { EventEmitter } from 'events';
import dispatcher from '../dispatcher';
import { REVERSE } from '../actions/reverse';
import { SELECT } from '../actions/select';
class MyStore extends EventEmitter {
constructor() {
super();
this.id = dispatcher.register((e) => {
switch(e.type) {
case REVERSE:
this.emit('change', (state = Object.assign({}, state, { users: state.users.reverse() })));
break;
case SELECT:
this.emit('change', (state = Object.assign({}, state, { users: state.users.map((v, i) => {
if (i === e.payload) {
return Object.assign({}, v, { fontWeight: 'bold' });
} else {
return Object.assign({}, v, { fontWeight: 'normal' });
}
})})));
break;
}
});
}
get state() {
return state;
}
}
export default new MyStore();
主要有两个重要更改:一是用户数组中的每个项都新增了
fontWeight
属性,用于控制链接的显示以指示选择状态;二是增加了SELECT处理逻辑,根据有效负载索引匹配项并更改字体粗细。
1.2 使用VanillaJS
前端JavaScript渲染库生态系统的多样性并非问题,相反,可供选择的库和框架过多。在某些阶段,过早选择视图库可能会限制技术的使用,而等待太久再做决定,将基于纯JS构建的视图迁移到更具倾向性的方法会变得困难。
最佳策略是尽可能避免技术锁定,保持组件的松散耦合,使其可替换。Flux架构使这一目标易于实现,因为视图层的职责相对有限,只需监听存储的更改事件并渲染存储状态。可以尝试构建两套视图组件,一套使用React等技术,另一套使用jQuery和Handlebars等其他技术,这样既能选择最适合产品的视图技术,又能测试采用新技术的准备情况。
1.3 转向React
在Flux架构的视图组件中,可以使用jQuery和Handlebars等技术,且它们不会干扰Flux架构中的单向数据流。然而,React可能是最适合作为Flux架构一部分的视图技术。从单向数据流的角度来看,React能自然地实现这一点。即使没有Flux,无状态的功能性React组件的行为也符合Flux架构中视图的预期,即新属性传入时渲染新的HTML。
此外,由于React使用虚拟DOM来修补渲染输出,而不是替换整个内容,重新渲染大型DOM结构不再那么令人畏惧。React还能处理一些边缘情况,如在重新渲染期间保持表单控件的焦点。
虽然转向React的可能性很大,但也存在一些负面权衡,如更高的内存消耗。不过,由于视图在Flux架构中作用相对较小,如果转向React能解决视图组件中的问题,那么这是一个不错的方向。
1.4 新技术的涌现
几年前,React还是新兴技术。开发者应对新技术保持一定的怀疑态度,并非所有新的闪亮技术都能成功。例如,Google正在实现名为Incremental DOM的视图技术,它采用不同的渲染方法,内存消耗更低,还有Veu.js等。要确保视图能够适应并采用最新、最优秀的视图技术。
2. 利用Flux库
Flux本质上是一套架构指南,虽提供了极大的灵活性,但在决定如何实现特定的Flux组件时可能会让人不知所措。幸运的是,有一些优秀的Flux库提供了有倾向性的Flux组件实现,减少了大量样板代码的编写。
2.1 实现核心Flux组件
可以改变Flux架构中各种组件的实现细节,下面分别讨论调度器、存储和动作及动作创建函数。
| 组件 | 说明 |
|---|---|
| 调度器 |
可对其进行定制。Facebook的参考实现虽可用,但并非每个生产环境的Flux架构都必须使用。可以将调度器模块中的
dispatch()
和
register()
函数暴露出来,使调度器的使用更直接。通用的Flux库甚至可能完全取消调度器,但仍能实现其架构原则。
|
| 存储 | 之前对存储层次结构进行了改进,让每个存储继承自一个基类,自动完成存储在调度器上的注册。方法动作处理程序原本是调度器的功能,现在或许基类存储是实现此功能的合适位置。如果发现多个存储有通用的状态转换行为,基类存储便于提取公共代码。 |
| 动作和动作创建函数 | 在Flux架构中,常量是明确表示动作的好方法。动作模块定义常量,动作创建函数将常量传递给调度器,存储在处理动作时也使用这些常量。之前采用了不同的方法,允许存储定义方法处理程序,但这降低了常量的价值。Flux库可以简化动作的调度和处理,减少处理常量时的人为错误。此外,对于异步动作创建函数,其异步行为通常遵循类似的模式,Flux库可将其生命周期抽象为通用函数。 |
2.2 实现痛点
在实现Flux架构时,存在一些痛点。异步动作难以正确实现,将应用状态划分到存储中是一个棘手的设计问题,若处理不当,很难挽回。此外,还需考虑数据依赖的挑战。
综上所述,在Flux架构的实践中,无论是视图组件的选择还是Flux库的利用,都需要综合考虑各种因素,以构建出高效、灵活的应用架构。
探索Flux架构中的视图组件与库应用
3. 不同Flux库的实现差异
为了展示不同的Flux实现方式,下面将以两个Flux库为例进行说明。虽然目标不是追求完全合规,但要构建一个能帮助应用完成任务的坚实架构。
3.1 调度器的定制差异
不同的Flux库对调度器的实现有不同的方式。有的库可能会像之前提到的那样,将
dispatch()
和
register()
函数暴露出来,让使用更直接;而有些库可能会完全隐藏调度器的实现细节,只提供一个简单的接口供开发者使用。以下是一个简单的对比表格:
| 库特点 | 调度器暴露情况 | 实现复杂度 | 灵活性 |
|---|---|---|---|
| 库A |
暴露
dispatch()
和
register()
| 适中 | 较高 |
| 库B | 隐藏调度器细节,提供简单接口 | 较低 | 相对较低 |
从这个表格可以看出,不同库在调度器的实现上各有优劣。如果开发者需要更高的灵活性,可能会选择像库A这样的实现;而如果希望快速开发,减少复杂度,库B可能更合适。
3.2 存储的处理差异
存储在不同Flux库中的处理方式也有所不同。有些库可能会提供一个强大的基类存储,包含各种通用的状态转换方法,开发者可以直接继承使用;而另一些库可能只提供最基本的存储功能,让开发者自己去实现具体的逻辑。
以下是一个mermaid格式的流程图,展示了不同库中存储的处理流程差异:
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;
A([开始]):::startend --> B{选择库}:::decision
B -->|库C| C(继承强大基类存储):::process
B -->|库D| D(实现基本存储功能):::process
C --> E(使用通用状态转换方法):::process
D --> F(自定义状态转换逻辑):::process
E --> G([结束]):::startend
F --> G
从这个流程图可以清晰地看到,不同库在存储处理上的路径不同。选择哪种库取决于开发者对存储功能的需求和开发的便捷性。
3.3 动作处理的差异
动作处理在不同Flux库中也存在差异。有些库可能会强调使用常量和
switch
语句来明确动作的处理,而有些库可能会采用更灵活的方式,如使用方法处理程序。以下是一个列表说明:
-
库E
:
- 强调使用常量和
switch
语句,动作处理逻辑清晰,易于理解和维护。
- 示例代码:
switch(action.type) {
case 'ACTION_TYPE_1':
// 处理逻辑
break;
case 'ACTION_TYPE_2':
// 处理逻辑
break;
}
-
库F
:
- 采用方法处理程序,让存储直接定义与动作匹配的方法。
- 示例代码:
class MyStore {
handleActionType1() {
// 处理逻辑
}
handleActionType2() {
// 处理逻辑
}
}
不同的动作处理方式各有优缺点,开发者可以根据项目的需求和团队的开发习惯来选择。
4. 总结与建议
在Flux架构的实践中,视图组件的选择和Flux库的利用都至关重要。以下是一些总结和建议:
4.1 视图组件选择
- 对于初期项目,可先使用jQuery和Handlebars等熟悉的技术构建视图组件,快速实现功能。
- 随着项目的发展,如果对性能和单向数据流有更高要求,可考虑转向React。
- 保持多种视图组件的实现,以便在需要时进行切换和测试。
4.2 Flux库使用
- 根据项目的复杂度和团队的开发能力选择合适的Flux库。
- 关注库在调度器、存储和动作处理等方面的实现差异,选择最符合需求的库。
- 注意异步动作处理、状态划分和数据依赖等实现痛点,选择能有效解决这些问题的库。
总之,Flux架构提供了很大的灵活性,开发者需要根据具体情况做出合适的选择,以构建出高效、灵活且易于维护的应用架构。同时,要保持对新技术的关注,以便在合适的时候引入更好的解决方案。
Flux架构中视图与库的选择
超级会员免费看
2

被折叠的 条评论
为什么被折叠?



