MVVM模式的理解
时间:2022-07-24
本文章向大家介绍MVVM模式的理解,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
MVVM模式的理解
MVVM
全称Model-View-ViewModel
是基于MVC
和MVP
体系结构模式的改进,MVVM
就是MVC
模式中的View
的状态和行为抽象化,将视图UI
和业务逻辑分开,更清楚地将用户界面UI
的开发与应用程序中业务逻辑和行为的开发区分开来。
描述
MVVM
模式简化了界面与业务的依赖,有助于将图形用户界面的开发与业务逻辑或数据模型的开发分离开来。在MVVM
中的ViewModel
作为绑定器将视图层UI
与数据层Model
链接起来,在Model
更新时,ViewModel
通过绑定器将数据更新到View
,在View
触发指令时,会通过ViewModel
传递消息到Model
,ViewModel
像是一个黑盒,在开发过程中只需要关注于呈现UI
的视图层以及抽象模型的数据层Model
,而不需要过多关注ViewModel
是如何传递的数据以及消息。
组成
Model
- 以面向对象来对对事物进行抽象的结果,是代表真实状态内容的领域模型。
- 也可以将
Model
称为数据层,其作为数据中心仅关注数据本身,不关注任何行为。
View
-
View
是用户在屏幕上看到的结构、布局和外观,即视图UI
。 - 当
Model
进行更新的时候,ViewModel
会通过数据绑定更新到View
。
ViewModel
-
ViewModel
是暴露公共属性和命令的视图的抽象。 -
ViewModel
中的绑定器在视图和数据绑定器之间进行通信。 - 在
Model
更新时,ViewModel
通过绑定器将数据更新到View
,在View
触发指令时,会通过ViewModel
传递消息到Model
。
优点
- 低耦合: 视图
View
可以独立于Model
变化和修改,一个ViewModel
可以绑定到不同的View
上,当View
变化的时候Model
可以不变,当Model
变化的时候View
也可以不变。 - 可重用性: 可以把一些视图逻辑放在一个
ViewModel
里面,让很多View
重用这段视图逻辑。 - 独立开发: 开发人员可以专注于业务逻辑和数据的开发
Model
,设计人员可以专注于页面设计。 - 可测试: 界面素来是比较难于测试的,测试行为可以通过
ViewModel
来进行。
不足
- 对于过大的项目,数据绑定需要花费更多的内存。
- 数据绑定使得
Bug
较难被调试,当界面异常,可能是View
的代码有问题,也可能是Model
的代码有问题,数据绑定使得一个位置的Bug
可能被快速传递到别的位置,要定位原始出问题的地方就变得不那么容易了。
实例
下面是参照Vue
实现的简单的数据绑定实例。
<!DOCTYPE html>
<html>
<head>
<title>数据绑定</title>
</head>
<body>
<div id="app">
<div>{{msg}}</div>
<div>{{date}}</div>
<button onclick="update()">update</button>
</div>
</body>
<script type="text/javascript">
///////////////////////////////////////////////////////////////////////////////
var Mvvm = function(config) {
this.$el = config.el;
this.__root = document.querySelector(this.$el);
this.__originHTML = this.__root.innerHTML;
function __dep(){
this.subscribers = [];
this.addSub = function(watcher){
if(__dep.target && !this.subscribers.includes(__dep.target) ) this.subscribers.push(watcher);
}
this.notifyAll = function(){
this.subscribers.forEach( watcher => watcher.update());
}
}
function __observe(obj){
for(let item in obj){
let dep = new __dep();
let value = obj[item];
if (Object.prototype.toString.call(value) === "[object Object]") __observe(value);
Object.defineProperty(obj, item, {
configurable: true,
enumerable: true,
get: function reactiveGetter() {
if(__dep.target) dep.addSub(__dep.target);
return value;
},
set: function reactiveSetter(newVal) {
if (value === newVal) return value;
value = newVal;
dep.notifyAll();
}
});
}
return obj;
}
this.$data = __observe(config.data);
function __proxy (target) {
for(let item in target){
Object.defineProperty(this, item, {
configurable: true,
enumerable: true,
get: function proxyGetter() {
return this.$data[item];
},
set: function proxySetter(newVal) {
this.$data[item] = newVal;
}
});
}
}
__proxy.call(this, config.data);
function __watcher(fn){
this.update = function(){
fn();
}
this.activeRun = function(){
__dep.target = this;
fn();
__dep.target = null;
}
this.activeRun();
}
new __watcher(() => {
console.log(this.msg, this.date);
})
new __watcher(() => {
var html = String(this.__originHTML||'').replace(/"/g,'\"').replace(/s+|r|t|n/g, ' ')
.replace(/{{(.)*?}}/g, function(value){
return value.replace("{{",'"+(').replace("}}",')+"');
})
html = `var targetHTML = "${html}";return targetHTML;`;
var parsedHTML = new Function(...Object.keys(this.$data), html)(...Object.values(this.$data));
this.__root.innerHTML = parsedHTML;
})
}
///////////////////////////////////////////////////////////////////////////////
var vm = new Mvvm({
el: "#app",
data: {
msg: "1",
date: new Date(),
obj: {
a: 1,
b: 11
}
}
})
function update(){
vm.msg = "updated";
}
///////////////////////////////////////////////////////////////////////////////
</script>
</html>
每日一题
https://github.com/WindrunnerMax/EveryDay
参考
https://zhuanlan.zhihu.com/p/38296857
https://baike.baidu.com/item/MVVM/96310
https://www.liaoxuefeng.com/wiki/1022910821149312/1108898947791072
- 剑指 offer——面试题8求旋转数组的最小值
- MYSQL INNODB表压缩
- 剑指offer——年龄排序问题
- Mysql Group Replication介绍
- 剑指offer——快速排序
- 架构高性能网站秘笈(四)——反向代理缓存
- 架构高性能网站秘笈(一)——了解衡量网站性能的指标
- MYSQL5.6&5.7编译安装
- 架构高性能网站秘笈(三)——浏览器缓存
- 剑指 offer代码解析——面试题39判断平衡二叉树(高效方法)
- 跟着柴毛毛学Spring(4)——面向切面编程![这里写图片描述](http://img.blog.csdn.net/20171031111402095)
- MYSQL数据闪回方式
- 剑指 offer代码解析——面试题39判断平衡二叉树
- 跟着柴毛毛学Spring(3)——简化Bean的配置
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 状态管理之Vuex (二) 异步管理
- Spring中AOP相关的API及源码解析,原来AOP是这样子的
- 状态管理之Vuex (一) 基操勿六
- 形式化分析工具(五)使用CAS +语法轻松编写HLPSL规范
- 你知道Spring是怎么将AOP应用到Bean的生命周期中的吗?
- 太实用了!自己动手写软件——密码验证器的界面实现
- 【TBase开源版测评】深度测评TBase的shard分片和冷热分离存储特性
- Python爬虫练手,一个简单的Python资讯采集案例
- 直播带货软件开发过程中,如何实现图片上传
- 太实用了!自己动手写软件——邮件用户名密码验证
- 太实用了!自己动手写软件——SSH、FTP和SQL server的密码破解
- Kaggle Tweet Sentiment Extraction 第七名复盘
- 【翻译】.NET 5中的性能改进
- 腾讯云实时语音识别-iOS SDK
- JointPoint用法及与ProceedingJoinPoint 的关系