博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
探索从 MVC 到 MVVM + Flux 架构模式的转变
阅读量:4676 次
发布时间:2019-06-09

本文共 3660 字,大约阅读时间需要 12 分钟。

本文首发于

在业务中一般 MVVM 框架一般都会配合上数据状态库(redux, mobx 等)一起使用,本文会通过一个小 demo 来讲述为什么会引人数据状态库。

从 MVC 到 MVVM 模式说起

传统 MVC 架构(如 JSP)在当今移动端流量寸土寸金的年代一个比较头疼的问题就是会进行大量的全局重复渲染。但是 MVC 架构是好东西,其对数据、视图、逻辑有了清晰的分工,于是前端 MVC 框架(比如 backbone.js) 出来了,对于很多业务规模不大的场景,前端 MVC 框架已经够用了,它也能做到前后端分离开发单页面应用,那么它的缺陷在哪呢?

拿 backbone.js 说,它的 Model 对外暴露了 set 方法,也就是说可以在不止一个 View 里修改同个 Model 的数据,然后一个 Model 的数据同时对应多个 View 的呈现,如下图所示。当业务逻辑过多时,多个 Model 和多个 View 就会耦合到一块,可以想到排查 bug 的时候会比较痛苦。

16183c3fae895d43

针对传统 MVC 架构性能低(多次全局渲染)以及前端 MVC 框架耦合度高(Model 和 View) 的痛处,MVVM 框架完美地解决了以上两点。可以参阅之前写的

only MVVM

假设有这么一个场景,在输入框中查询条件,点击查询,然后在列表中返回相应内容。如下图所示:

16183c40b99c5093?w=318&h=200&f=jpeg&s=8090

假设用 react 实现,思路大体是先调用查询接口,调用成功后将获取到的数据通过 setState 存进 list 中,列表显示部分代码如下:

const Decorate = (ListComponent) => class extends Component {  constructor() {    super()    this.state = { list: [] }  }  componentDidMount() {    fetch('./list.json')      .then((res) => res.json())      .then(result => this.setState({ list: result.data }))  }  render() {    return (      
) }}

接着往封装的 Decorate 组件里,传入无状态函数构建的 List 组件用来展示列表数据,代码如下:

function List(props) {  return (    
{props.data.map(r =>

{r.content}

)}
)}

可以看到 List 组件相当于是 View 层,而封装的 Decorate 组件相当于是 Model 层。但是这么做还是把业务逻辑写进了组件当中。而我们期望的是能得到一个纯粹的 Model 层和 View 层。接着一起看看 Flux 架构模式是如何解决这个问题的。

引人 Flux 架构模式

16183c3fade4c600?w=468&h=200&f=jpeg&s=9305

Flux 架构模式的 4 个重要组成部分以及它们的关系如上图所示,下文会根据 dispatch,store, action, view 的顺序逐步揭开 Flux 架构模式的面纱。

从 中可以看出 Dispacher.js 是其的核心文件,其核心是基于事件的发布/订阅模式完成的,核心源码如下:

class Dispatcher {  ...  // 注册回调函数,  register(callback) {    var id = _prefix + this._lastID++;    this._callbacks[id] = callback;  }  // 当调用 dispatch 的时候会调用 register 中注册的回调函数  dispatch(payload) {    this._startDispatching(payload);    for (var id in this._callbacks) {      this._invokeCallback(id);    }  }}

回顾下之前的目的:让 Store 层变得纯粹。于是定义了一个变量 comments 用来专门存放列表数据,在了解 Dispatcher 的核心原理之后,当调用 dispatch(obj) 方法时,就可以把参数传递到事先注册的 register 函数中,代码如下:

// commentStore.jslet comments = []const CommentStore = {  getComment() {    return comments  }}dispathcer.register((action) => { // 调用 Dispatcher 实例上的 register 函数  switch (action.type) {    case 'GET_LIST_SUCCESS': {      comments = action.comment    }  }})

以及 action 中的函数如下:

// commentAction.jsconst commentAction = {  getList() {    fetch('./list.json')      .then((res) => res.json())      .then(result =>        dispathcer.dispatch({ // 调用 Dispatcher 实例上的 dispatch 函数          type: 'GET_LIST_SUCCESS',          comment: result.data        }))  }}

但是似乎少了点什么,当 GET_LIST_SUCCESS 成功后,发现还缺少通知到页面再次调用 CommentStore.getComment() 的能力,所以再次引用事件发布/订阅模式,这次使用了 Node.js 提供的 events 模块,对 commentStore.js 文件进行修改,修改后代码如下:

let comments = []const CommentStore = Object.assign({}, EventEmitter.prototype, {  getComment() {    return comments  },  emitChange() {    this.emit('change')  },  addListener(callback) { // 提供给页面组件使用    this.on('change', callback)  }})appDispathcer.register((action) => {  switch (action.type) {    case 'GET_LIST_SUCCESS': {      comments = action.comment      CommentStore.emitChange() // 有了这行代码,也就有了通知页面再次进行调用 CommentStore.getComment 的能力    }  }})

剩下最后一步了,就是整合 store 和 action 进页面中,代码如下:

class ComponentList extends Component {  constructor() {    super()    this.state = {      comment: commentStore.getComment()    }  }  componentDidMount() {    commentStore.addListener(() => this.setState({ // 注册函数,上面已经提过,供 store 使用      comment: commentStore.getComment()    }))  }  render() {    return (      
{this.state.comment.map(r =>

{r.content}

)}
) }}

小结

单纯以 mvvm 构建应用会发现业务逻辑以及数据都耦合在组件之中,引入了 Flux 架构模式后数据和业务逻辑得到较好的分离。但是使用 Flux 有什么缺点呢?在下篇 《聊聊 Redux 架构模式》中会进行分析,下回见。

本文实践案例已上传至

,欢迎 Star

转载于:https://www.cnblogs.com/MuYunyun/p/8442179.html

你可能感兴趣的文章
iframe的滚动栏问题:显示/隐藏滚动栏
查看>>
reactor模式:单线程的reactor模式
查看>>
Pair_Work Project
查看>>
单例设计模式
查看>>
Django Class Based View
查看>>
tcp 拥塞控制机制
查看>>
python学习笔记(十五)-异常处理
查看>>
路径+DataRow+SqlPara防止sql注入
查看>>
Internet History, Technology and Security (Week5.1)
查看>>
MySQL查询in操作 查询结果按in集合顺序显示_Mysql_脚本之家
查看>>
解释型语言与编译型语言
查看>>
redis主从复制
查看>>
SQLite之登录注册
查看>>
Linux就该这么学(3)-管道符、重定向与环境变量(学习笔记)
查看>>
asm335x系列adc和触摸屏驱动(转)
查看>>
菜鸟nginx源代码剖析数据结构篇(八) 缓冲区链表ngx_chain_t
查看>>
Nginx基础教程
查看>>
good
查看>>
JavaScript之isNaN()函数讲解
查看>>
如何培养自己的管理才能?
查看>>