1 概述
前端架构汇总
2 原始
2.1 数据流
@startdot
digraph G {
rankdir=LR;
node [shape=record];
View -> View [label = "所有操作"]
}
@enddot
2.2 依赖
无
2.3 总结
优点:
- 简单暴力
缺点:
- 大型程序维护困难
3 MV
3.1 数据流
@startdot
digraph G {
rankdir=LR;
size="8,5"
node [shape=record];
View
Model
View -> Model [ label = "2.调用更新数据" ];
View -> Model [ label = "1.订阅数据更新事件" ];
Model -> View [ label = "3.发布数据更新事件" , style = dashed ];
View -> View [ label = "4.更新ui"]
}
@enddot
3.2 依赖
@startdot
digraph G {
node [shape=record];
View -> Model [ label = "依赖"]
}
@enddot
3.3 总结
优点
- 数据操作与ui展示分开,更容易测试,维护性更好
缺点
- View操作仍然承载过多的数据操作,而不是一个简单的表现层
4 MVC
4.1 数据流
@startdot
digraph G {
rankdir=LR;
size="8,5"
node [shape=record];
View
Controller
Model
View -> Controller [ label = "2.调用事件行为" ];
Controller -> Model [ label = "3.调用更新数据"]
View -> Model [ label = "1.订阅数据更新事件"];
Model -> View [ label = "4.发布数据更新事件", style = dashed];
View -> View [ label = "5.更新ui"]
}
@enddot
4.2 依赖
@startdot
digraph G {
node [shape=record];
View -> Controller [ label = "依赖"]
View -> Model [ label = "依赖"]
Controller -> Model [ label = "依赖"]
}
@enddot
4.3 总结
优点:
- 数据操作,ui展示,事件操作全部分开,更容易测试,也更容易被维护
缺点:
- View强度依赖于Controller与Model,View难以复用在其他Model上,而且作为表现层的View除了做ui展示工作外,还需要知道每个事件需要指向到哪个Controller上,每个数据需要来源于哪个Model
5 MVP
5.1 数据流
@startdot
digraph G {
rankdir=LR;
size="8,5"
node [shape=record];
View
Presenter
Model
Presenter -> View [ label = "1.订阅ui事件" ];
Presenter -> Model [ label = "2.订阅数据更新事件" ];
View -> Presenter [ label = "3.发布ui事件" , style = dashed ];
Presenter -> Model [ label = "4.调用数据更新" ];
Model -> Presenter [ label = "5.发布数据更新事件" , style = dashed ];
Presenter -> View [ label = "6.调用ui更新" ];
}
@enddot
5.2 依赖
@startdot
digraph G {
node [shape=record];
Presenter -> View [ label = "依赖"]
Presenter -> Model [ label = "依赖"]
}
@enddot
5.3 总结
优点:
- 数据操作,ui展示,事件操作全部分开,更容易测试,也更容易被维护。
- View与Model无外部依赖,能够更自由地独立演化
缺点:
- Controller的工作太多,而且每个Controller需要对应每个顶层View,Controller之间有太多冗余的修改同一个model的操作了
6 MVVM
6.1 数据流
@startdot
digraph G {
rankdir=LR;
size="8,5"
node [shape=record];
View
ViewModel
Model
ViewModel -> View [ label = "1.订阅ui事件" ];
ViewModel -> Model [ label = "2.订阅数据更新事件" ];
ViewModel -> View [ label = "3.传递绑定的bind数据" ];
View -> ViewModel [ label = "4.发布ui事件" , style = dashed ];
ViewModel -> Model [ label = "5.调用数据更新" ];
Model -> ViewModel [ label = "6.发布数据更新事件" , style = dashed ];
ViewModel -> ViewModel [ label = "7.修改bind数据" ];
View -> View [ label = "8.同步bind数据更新ui" ];
}
@enddot
6.2 依赖
@startdot
digraph G {
node [shape=record];
ViewModel -> View [ label = "依赖"]
ViewModel -> Model [ label = "依赖"]
}
@enddot
6.3 总结
优点:
- 数据操作,ui展示,事件操作全部分开,更容易测试,也更容易被维护。
- View与Model无外部依赖,能够更自由地独立演化
- ViewModel修改ui是通过修改bind数据的方式,ui变化后自动修改bind数据,这样大幅减少了很多原来Controller与View之间的调用,仅通过修改data来实现
缺点:
- ViewModel之间有太多冗余的修改同一个model的操作了
- 不是太直观
MVVM强调的two-way binding,是数据->视图的无限制单向绑定,以及视图->数据之间的简单数据的绑定,不支持数组绑定
7 Flux
7.1 数据流
@startdot
digraph G {
rankdir=LR;
size="8,5"
node [shape=record];
Component
Container
Action
Reducer
Container -> Component [ label = "1.订阅ui事件" ];
Container -> Reducer [ label = "2.订阅数据更新事件" ];
Reducer -> Action [ label = "3.订阅Action事件" ];
Component -> Container [ label = "4.发布ui事件" , style = dashed ];
Container -> Action [ label = "5.调用Action行为" ];
Action -> Reducer [ label = "6.发布Action事件" , style = dashed ];
Reducer -> Container [ label = "7.发布数据更新事件" , style = dashed ];
Container -> Component [ label = "8.调用ui更新" ];
}
@enddot
7.2 依赖
@startdot
digraph G {
node [shape=record];
Container -> Component [ label = "依赖"]
Container -> Action [ label = "依赖"]
Container -> Reducer [ label = "依赖"]
Reducer -> Action [ label = "依赖"]
}
@enddot
7.3 总结
优点:
- 数据操作(Reducer),ui展示(Component),事件操作(Action)全部分开,更容易测试,也更容易被维护。
- Container减少了冗余,因为Container只是一个薄薄的封装层,封装的是Reducer,Component,Action三者之间的关系。虽然每个顶层View都对应一个Container,但是Container中的操作很大一部分都不是冗余的,因为冗余的事件操作都放到Action里面了。
- Reducer被设计为单一无副作用store操作,所以很容易就能实现全局state的序列化,跟踪,回退等操作了。
- Reducer与Action被设计为订阅发布模式,这造成,单个Action能同时修改多个Reducer。
- Action被设计为命令模式,所以很容易能做中间件来监控记录Action的运行
- Action与Reducer都是纯函数的,很容易就做到了server render。
缺点:
- Reducer不能被独立演化了,因为它依赖的是Action。从业务的角度看,Reducer应该是底层业务,Action是高一级业务,所以Action应该依赖Reducer,而不是反过来。
- 单一store限制了Reducer在不同的Container中复用的能力
8 MyFlux
8.1 数据流
@startdot
digraph G {
rankdir=LR;
size="8,5"
node [shape=record];
Component
Container
Action
Model
Container -> Component [ label = "1.订阅ui事件" ];
Container -> Model [ label = "2.订阅数据更新事件" ];
Component -> Container [ label = "3.发布ui事件" , style = dashed ];
Container -> Action [ label = "4.调用Action行为" ];
Action -> Model [ label = "5.调用数据更新"];
Model -> Container [ label = "6.发布数据更新事件" , style = dashed ];
Container -> Component [ label = "7.调用ui更新" ];
}
@enddot
8.2 依赖
@startdot
digraph G {
node [shape=record];
Container -> Component [ label = "依赖"]
Container -> Action [ label = "依赖"]
Container -> Model [ label = "依赖"]
Action -> Model [ label = "依赖"]
}
@enddot
8.3 总结
优点:
- 数据操作(Model),ui展示(Component),事件操作(Action)全部分开,更容易测试,也更容易被维护。
- Container减少了冗余,因为Container只是一个薄薄的封装层,封装的是Model,Component,Action三者之间的关系。虽然每个顶层View都对应一个Container,但是Container中的操作很大一部分都不是冗余的,因为冗余的事件操作都放到Action里面了。
- Model与Component被设计为独立演化模块,这大大增强了他们的灵活性
缺点:
- 丢失了reflux中单一无副作用store的能力
- 丢失了reflux中单个Action能修改多个Reducer的能力
9 总结
根据不同的业务,我们需要选择不同的框架
- 少交互少逻辑,像企业官网这类纯展示的网站,应该采用MV或simple模式
- 多交互少逻辑,像后台系统这类表单超多,但是逻辑其实很简单的网站,应该采用MVVM的模式,注意,不要使用MVP,Flux等重型架构,因为如果用了这类架构,你会发现很多事件的逻辑仅仅只是为了设置一个字段的值
- 少交互多逻辑,像中小型的论坛或社区网站,使用MVP,MyFlux模式即可,数据流清晰明显,容易排错。不推荐使用Flux,单一store前期比较爽,后期是大坑。
- 多交互多逻辑,像大型的电子商务网站,应该将网站按照业务进行切分,每个模块使用业务相应合适的业务架构,这样每个模块都能独立演化,并提高整体的容错性。
- 本文作者: fishedee
- 版权声明: 本博客所有文章均采用 CC BY-NC-SA 3.0 CN 许可协议,转载必须注明出处!