Mvvm 前端数据流框架精讲

原文链接, 如果感兴趣可以加QQ群: 157937068, 一起交流。

本次分享是带大家了解什么是 mvvm,mvvm 的原理,以及近几年产生了哪些演变。

同时借 mvvm 这个话题拓展到对各类前端数据流方案的思考,形成对前端数据流整体认知,帮助大家在团队中更好的做技术选型。

Mvvm 的概念与发展

Mvvm & 单向数据流

Mvvm 是指双向数据流,即 View-Model 之间的双向通信,由 ViewModel 作桥接。如下图所示:

而单向数据流则去除了 View -> Model 这一步,需要由用户手动绑定。

生态 - 内置 & 解耦

许多前端框架都内置了 Mvvm 功能,比如 Knockout、Angular、Ember、Avalon、Vue、San 等等。

而就像 Redux 一样,Mvvm 框架中也出现了许多与框架解耦的库,比如 Mobx、Immer、Dob 等,这些库需要一个中间层与框架衔接,比如 mobx-react、redux-box、dob-react。解耦让框架更专注 View 层,实现了库与框架灵活搭配的能力。

解耦的数据流框架也诠释了更高抽象级别的 Mvvm 架构,即:View - 前端框架,Model - (mobx, dob),ViewModel - (mobx-react, dob-react)。

同时也实现了数据与框架分离,便于测试与维护。比如下面的例子,左边是框架无关的纯数据/数据操作定义,右边是 View + ViewModel:

运行效率 - 脏检测 & getter/setter 劫持

Angluar 早期的脏检测机制虽然开创了 mvvm 先河,但监听效率比较低,需要 N + 1 次确认数据是否有联动变化,就像下图所示:

现在几乎所有框架都改为 getter/setter 劫持实现监听,任何数据的变化都可以在一个事件循环周期内完成:

语法 - 特殊语法 & 原生语法

早期一些 Mvvm 框架需要手动触发视图刷新,现在这种做法几乎都被原生赋值语句取代。

数据变更方式 - Mutable & Immutable

下图的代码语法虽为 mutable,但产生的结果可能是 mutable,也可能是 immutable,取决于 mvvm 框架内置实现机制:

Connect 的两种写法

由于 mvvm 支持了 mutable 与 immutable 两种写法,所以对于 mutable 的底层,我们使用左图的 connect 语法,对于 immutable 的底层,需要使用右图的 conenct 语法:

[图片上传失败...(image-b7408b-1522595335875)]

对左图而言,由于 mutable 驱动,所有数据改动会自动调用视图刷新,因此不但更新可以一步到位,而且可以数据全量注入,因为没用到的变量不会导致额外渲染。

对右图,由于 immutable 驱动,本身并没有主动驱动视图刷新能力,所以当右下角节点变更时,会在整条链路产生新的对象,通过 view 更新机制一层层传导到要更新的视图。

从 TFRP 到 mvvm

讲到 mvvm 的原理,先从 TFRP 说起,详细可以参考 dob-框架实现 这里以 dob 框架为例子,一步步介绍了如何实现 mvvm。本文简单做个介绍。

autorun & reaction

autorun 是 TFRP 的函数效果,即集成了依赖收集与监听,autorun 背后由 reaction 实现。

reaction 实现 autorun

如下图所示,autorun 是 subscription 套上 track 的 reaction,并且初始化时主动 dispatch,从入口(subscription)处激活循环,完成 subscription -> track -> 监听修改 -> subscription 完成闭环。

track 的实现

每个 track 在其执行期间会监听 callback 的 getter 事件,并将 target 与 properityKey 存储在二维 Map 中,当任何 getter 触发后,从这个二维表中查询依赖关系,即可找到对应的 callback 并执行。

View-Model 的实现

由于 autorun 与 view 的 render 函数很像,我们在 render 函数初始化执行时,使其包裹在 autorun 环境中,第 2 次 render 开始遍剥离外层的 autorun,保证只绑定一遍数据。

这样 view 层在原本 props 更新机制的基础上,增加了 autorun 的功能,实现修改任何数据自动更新对应 view 的效果。

Mvvm 的缺点与解法?

Mvvm 所有已知缺点几乎都有了解决方案。

无法监听新增属性

用过 Mobx 的同学都知道,给 store 添加一个不存在的属性,需要使用 extendObservable 这个方法。这个问题在 Dob 与 Mobx4.0 中都得到了解决,解决方法就是使用 proxy 替代 Object.defineProperty

异步问题

由于 getter/setter 无法获得当前执行函数,只能通过全局变量方式解决,因此 autorun 的 callback 函数不支持异步:

嵌套问题

由于 reaction 特性,只支持同步 callback 函数,因此 autorun 发生嵌套时,很可能会打乱依赖绑定的顺序。解决方案是将嵌套的 autorun 放到执行队列尾部,如下图所示:

无数据快照

mutable 最被人诟病的一点就是无法做数据快照,不能像 redux 一样做时间回溯。有问题自然有人会解决,Mobx 作者的 Immer 库完美的解决了问题。

原理是通过 proxy 返回代理对象,在内部通过浅拷贝替代对对象的 mutable 更改。具体原理可以参考我的这篇文章:精读 Immer.js 源码

无副作用隔离

mvvm 函数的 Action 由于支持异步,许多人会在 Action 中发请求,同时修改 store,这样就无法将请求副作用隔离到 store 之外。同时对 store 的 mutable 修改,本身也是一种副作用。

虽然可以将请求函数拆分到另一个 Action 中,但人为因素无法完全避免。

自从有了 Immer.js 之后,至少从支持元编程的角度来看,mutable 并不一定会产生副作用,它可以是零副作用的:

function inc(obj) {
  return produce(obj => obj.count++)
}
复制代码

上面这种看似 mutable 的写法其实是零副作用的纯函数,和下面写法等价:

function inc(obj) {
  return {
    count: obj.count + 1,
    ...obj
  }
}
复制代码

而对副作用的隔离,也可以做出类似 dva 的封装:

Mvvm store 组织形式

Mvvm 在项目中 stores 代码结构也千变万化,这里列出 4 种常见形式。

对象形式,代表框架 – mobx

mobx 开创了最基本的 mvvm store 组织形式,基本也是各内置 mvvm 框架的 store 组织形式。

Class + 注入,代表框架 – dob

dob 在 store 组织形式下了不少功夫,通过依赖注入增强了 store 之间的关联,实现 stores -> action 多对一的网状结构。

数据结构化,代表框架 – mobx-state-tree

mobx-state-tree 是典型结构化 store 组织的代表,这种组织形式适合一体化 app 开发,比如很多页面之间细粒度数据需要联动。

约定与集成,代表框架 – 类 dva

类 dva 是一种集成模式,是针对 redux 复杂的样板代码,思考形成的简化方案,自然集成与约定是简化的方向。

另外这种方案更像一层数据 dsl,得益于此,同一套代码可以拥有不同的底层实现。

Mvvm vs Reactive programming

Mvvm 与 Reactive programming 都拥有 observable 特性,通过下面两张图可以轻松区分:

上面红线是 mvvm 的 observable 部分,这里指的是数据变化的 autorun 动作。

上面红线是 Reactive programming 的 observable 部分,指的是数据源派发流的过程。

Mvvm 与 Reactive programming 的结合

既然 redux 可以与 rxjs 结合(redux-observable),那么 mvvm 应该也可以如此。

下面是这种方案的构想:

rxjs 仅用来隔离副作用与数据处理,mvvm 拥有修改 store 的能力,并且精准更新使用的 View。

总结

根据业务场景指定数据流方案,数据流方案没有银弹,只有贴着场景走,才能找到最合适的方案。

了解到 mvvm 的发展与演进,让不同数据流方案组合,你会发现,数据流方案还有很多。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Hands-On High Performance Programming with Qt 5: Build cross-platform applications using concurrency, parallel programming, and memory management Author: Marek Krajewski Pub Date: 2019 ISBN: 978-1789531244 Pages: 384 Language: English Format: EPUB Size: 17 Mb Build efficient and fast Qt applications, target performance problems, and discover solutions to refine your code Achieving efficient code through performance tuning is one of the key challenges faced by many programmers. This book looks at Qt programming from a performance perspective. You’ll explore the performance problems encountered when using the Qt framework and means and ways to resolve them and optimize performance. The book highlights performance improvements and new features released in Qt 5.9, Qt 5.11, and 5.12 (LTE). You’ll master general computer performance best practices and tools, which can help you identify the reasons behind low performance, and the most common performance pitfalls experienced when using the Qt framework. In the following chapters, you’ll explore multithreading and asynchronous programming with C++ and Qt and learn the importance and efficient use of data structures. You’ll also get the opportunity to work through techniques such as memory management and design guidelines, which are essential to improve application performance. Comprehensive sections that cover all these concepts will prepare you for gaining hands-on experience of some of Qt’s most exciting application fields – the mobile and embedded development domains. By the end of this book, you’ll be ready to build Qt applications that are more efficient, concurrent, and performance-oriented in nature What you will learn Understand classic performance best practices Get to grips with modern hardware architecture and its performance impact Implement tools and procedures used in performance optimization Grasp Qt-specific work techniques for graphical user interface (GUI) and platform programming Make Transmission Control Protocol (TCP) and Hypertext Transfer Protocol (HTTP) performant and use the relevant Qt classes Discover the improvements Qt 5.9 (and the upcoming versions) holds in store Explore Qt’s graphic engine architecture, strengths, and weaknesses
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值