前端架构

2016-12-09 fishedee 前端

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前期比较爽,后期是大坑。
  • 多交互多逻辑,像大型的电子商务网站,应该将网站按照业务进行切分,每个模块使用业务相应合适的业务架构,这样每个模块都能独立演化,并提高整体的容错性。

相关文章