You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
如果遇到更复杂的业务逻辑,上面的 MVC 分层是远远不够的,部分逻辑可以从 Controller 和 Model 层中剥离出来,也许你还需要下面的这些分层。
Service
Service 层一般是封装了和请求数据相关的操作,比如 Ajax 请求、localStorage、indexDB,甚至是 JS Bridge 等等,这里类似于后端里面的 Model 概念。
比如向后端请求数据我们可以这么来写:
class Service {
async getList() {
const {
data = {}
} = await http({
method: 'post',
url: '/getList',
data: {}
});
return data.list;
}
}
在需要用到这个接口的地方(一般是 Controller 层)导入对应的 Service 文件,将接口请求的操作放到 Service 中统一处理,方便后期维护。
format
在将数据请求回来后,往往会出现接口的数据和前端要展示的数据结构不一致的情况。这个时候,从 api -> model 就需要一层额外的转化,这就是 format 层存在的意义。
理论上,format 函数全都应该是纯函数,接收从接口获取的数据,返回需要传给 model 的数据。
下面举个例子,这是一个将接口返回的时间戳转换为 YYYY-MM-DD 格式日期的 format 函数。
const formatDate = (datespan) => {
let date = new Date(datespan),
year = date.getFullYear(),
month = date.getMonth() ,
day = date.getDate();
const addPrefix = (num) => {
return num > 9 ? num : `0${num}`;
}
month = addPrefix(month + 1);
day = addPrefix(day);
return `${year}-${month}-${day}`
}
formatDate(1572781090844); // "2019-11-03"
service 到底是放到每个页面下面维护还是放到全局维护,这个主要看你的接口是否会经常在多个页面使用,这样的话更推荐你放到全局。
最后,一个完整的流程应当是这样,Controller 层调用 Service 层的方法去获取数据,将拿到的数据传给 format 和 utils 等函数进行转换,最后将转换的数据存入到 Model 中。
前言
前面我们讲了 JavaScript 面向对象编程,这篇文章我们会介绍一下面向对象中的经典编程模式 —— MVC。
MVC、MVVM、MVP 这三个概念在前端领域是老生常谈了,但这节课不会只在概念层面讲述三者的区别,而是更偏向实践、从编写业务代码层面讨论一下 MVC 模式在前端开发中的意思。
并未过时的 MVC
在 Angualr、Vue 等 MVVM 框架出现前,最火的前端框架当属 Backbone,这是一个典型的 MVC 框架。也许你会说 Backbone 不是过时了吗?那还在前端中讨论 MVC 还有什么意义?
Backbone 的确过时了,但是过时是因为 Backbone 的 MVC 实现方式过时了,并非是 MVC 思想过时了。如果以 Vue/React 作为 View 层,Vuex/Redux 作为 Model 层,那么就可以实现新的 MVC 框架。
工业聚大佬在此基础上实现了一个
react-imvc
的框架,也为此写过一篇文章:IMVC(同构 MVC)的前端实践MVC 分层有助于管理复杂的应用程序,因为你可以在一个时间内只关注于某一层。例如,你可以在不依赖业务逻辑的情况下专注于视图设计。同时也让应用程序的测试更加容易。
即使是不使用 Angular/React/Vue 这些框架,我们依然能用 MVC 对代码进行组织管理。
MVC 的由来
MVC 是一种架构模式,最早由施乐的 Trygve Reenskaug 在1978年提出,原本是为了给程序语言 Smalltalk 提供架构,大大提高了程序的后期可维护性。
那么什么 MVC 呢?相信大家都对 MVC 比较熟悉了,不管是 Java 中的 spring mvc,还是前端里面的 Backbone,这些都是应用很广泛的 MVC 框架。
我这里引用一下维基百科的解释:
因此,我们可以知道 MVC 是由 Controller、Model和 View 三部分组成,这三部分各司其职。
不同版本的 MVC
对于 MVC,业界有不同的定义,比如就有微软的版本、苹果的版本等等。
苹果版本
苹果版本的 MVC:
整理一下苹果版本的 MVC,是这样的:
这个版本的 MVC 模式比较符合我们前端开发的直觉,因此本文也以苹果的版本来讲解。
微软版本
微软的版本(图片来自微软 ASP.NET core 官网):
微软的版本以 ASP.NET core MVC 为实现,可以这样理解:
可以看得出来,微软版本的 MVC 比较难理解,因此这里不做太多介绍。
阮一峰版
阮一峰的版本:
阮一峰版本的 MVC 可以这样理解:
原生 JS 实现 MVC
我们要实现的 MVC 模式如下,类似于 Backbone 的设计模式:
这里我们要用原生 JS 来实现一个 MVC 模式,在业务代码中也可以使用这样的形式来组织项目。
首先,对于项目来说,我们应该以 app.js 为入口,这是一个业务模块。
Model
model.js 对应 Model,这里定义了应用的数据模型,只做提供数据存储和修改。
在这里可以实现了一个 发布-订阅 功能,在 view 中会订阅当前状态的变化,一旦状态发生变化,就去通知所有订阅的 view 重新渲染。
View
View.js 对应 View 层,提供数据进行视图渲染,一般是 html 模板文件,也可以是 React/View 等现代化 UI 框架。这里以 underscore template 来举例子。
当然,如果你觉得无法理解这个模板语法,你也可以用 es6 模板字符串来实现。
然后将 model 中的数据传给这个模板来渲染。
Controller
controller.js 对应 Controller,主要是 Model 和 View 之间的纽带,进行一系列事件绑定等功能。
如果你有开发过大型的 jQuery 项目,就会遇到这种问题。当绑定的事件越来越多的时候,在代码里面很难看出来 DOM 和事件之间的绑定关系,维护起来也越来越吃力。
所以我们可以将 DOM 和事件绑定集中在 Controller 中来处理。在 events 属性中,我们将需要绑定的 DOM 元素、事件以及回调函数用键值对的形式进行映射,在 delegateEvents 中解析后进行事件绑定。这样的好处就是可以很清晰地看到项目中所有的绑定事件,后期维护起来更容易。
同时,在初始化以及每次操作之后,我们可以选择手动通知(执行 notify 方法)相关视图进行渲染。
最终的代码实现如下:
这样我们就实现了一个完整的 MVC 骨架。这种形式可以适用于很多项目,甚至不需要依赖框架,后续还可以将 underscore template 替换成 React/Vue 等框架,增加了项目的可维护性,层次结构更加清晰。
React 中的 MVC
其他分层
如果遇到更复杂的业务逻辑,上面的 MVC 分层是远远不够的,部分逻辑可以从 Controller 和 Model 层中剥离出来,也许你还需要下面的这些分层。
Service
Service 层一般是封装了和请求数据相关的操作,比如 Ajax 请求、localStorage、indexDB,甚至是 JS Bridge 等等,这里类似于后端里面的 Model 概念。
比如向后端请求数据我们可以这么来写:
在需要用到这个接口的地方(一般是 Controller 层)导入对应的 Service 文件,将接口请求的操作放到 Service 中统一处理,方便后期维护。
format
在将数据请求回来后,往往会出现接口的数据和前端要展示的数据结构不一致的情况。这个时候,从
api -> model
就需要一层额外的转化,这就是 format 层存在的意义。理论上,format 函数全都应该是纯函数,接收从接口获取的数据,返回需要传给 model 的数据。
下面举个例子,这是一个将接口返回的时间戳转换为 YYYY-MM-DD 格式日期的 format 函数。
utils
除了数据请求和格式化之外,往往项目中也会出现一些共用的工具函数,比如日期格式化、埋点、DOM 相关的方法等等。
目录
因此,一个完整的项目目录结构应当是这样的(加号代表文件夹,减号代表文件):
service 到底是放到每个页面下面维护还是放到全局维护,这个主要看你的接口是否会经常在多个页面使用,这样的话更推荐你放到全局。
最后,一个完整的流程应当是这样,Controller 层调用 Service 层的方法去获取数据,将拿到的数据传给 format 和 utils 等函数进行转换,最后将转换的数据存入到 Model 中。
总结
虽历经几十年的洗礼,MVC 架构依然没有过时,在各种语言中我们也经常能看到对应的 MVC 框架。
在前端开发中,我们也可以借鉴 MVC 的思想来对项目进行重新组织和解耦,这样可以大大提高项目的灵活性。
The text was updated successfully, but these errors were encountered: