MVVM框架原理浅析
MVVM 是一个 Android MVVM 框架,基于谷歌 dataBinding 技术实现。dataBinding 实现的 V 和 VM 的关联;使用 IOC 架构实现了 M 和 V 的关联。
概述
MVVM是Model-View-ViewModel的简写。微软的WPF(Windows Presentation Foundation–微软推出的基于Windows 的用户界面框架)带来了新的技术体验, 使得软件UI层更加细节化、可定制化。
与此同时,在技术层面,WPF也带来了 诸如Binding(绑定)、Dependency Property(依赖属性)、Routed Events(路由事件)、Command(命令)、DataTemplate(数据模板)、ControlTemplate(控制模板)等新特性。
MVVM模式其实是MVP模式与WPF结合的应用方式时发展演变过来的一种新型架构模式。它立足于原有MVP框架并且把WPF的新特性糅合进去,以应对客户日益复杂的需求变化。
框架功能:
- 业务逻辑层的分离
- 封装了 android 6.0 权限申请,在申请权限时,能像 View 一样设置事件监听
- 创建 Fragment、Dialog、popupwindow 都将变得极其简单
- 占位布局实现将变得极其简单
- 具有 dataBinding 的一切功能
- 封装了 Okhttp 网络请求,实现二级缓存,实现了网络回调监听
MVVM控件
使用MVVM来开发用户控件。由于用户控件在大部分情况下不涉及到数据的持久化,所以如果将M纯粹理解为DomainModel的话,使用MVVM模式来进行自定义控件开发实际上可以省略掉M,变成了VVM。
MVVM模式的组成部分
- 模型
- 模型是指代表真实状态内容的领域模型(面向对象),或指代表内容的数据访问层(以数据为中心)。
- 视图
- 就像在MVC和MVP模式中一样,视图是用户在屏幕上看到的结构、布局和外观(UI)。
- 视图模型
- 视图模型是暴露公共属性和命令的视图的抽象。MVVM没有MVC模式的控制器,也没有MVP模式的presenter,有的是一个绑定器。在视图模型中,绑定器在视图和数据绑定器之间进行通信。
- 绑定器
- 声明性数据和命令绑定隐含在MVVM模式中。在Microsoft解决方案堆中,绑定器是一种名为XAML的标记语言。绑定器使开发人员免于被迫编写样板式逻辑来同步视图模型和视图。在微软的堆之外实现时,声明性数据绑定技术的出现是实现该模式的一个关键因素。
MVVM优点
MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model),有几大优点
1. 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
2. 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
3. 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xaml代码。
4. 可测试。界面素来是比较难于测试的,测试可以针对ViewModel来写。
MVVM缺点
Bug很难被调试: 因为使用双向绑定的模式,当看到界面异常了,有可能是View的代码有Bug,也可能是Model的代码有问题。数据绑定使得一个位置的Bug被快速传递到别的位置,要定位原始出问题的地方就变得不那么容易了。
另外,数据绑定的声明是指令式地写在View的模版当中的,这些内容是没办法去打断点debug的一个大的模块中model也会很大,虽然使用方便了也很容易保证了数据的一致性,当时长期持有,不释放内存就造成了花费更多的内存对于大型的图形应用程序,视图状态较多,ViewModel的构建和维护的成本都会比较高。
MVVM框架与MVC框架的区别
mvc 和 mvvm 其实区别并不大。都是一种设计思想,主要区别如下:
- mvc 中 Controller演变成 mvvm 中的 viewModel
- mvvm 通过数据来驱动视图层的显示而不是节点操作。
- mvc中Model和View是可以直接打交道的,造成Model层和View层之间的耦合度高。而mvvm中Model和View不直接交互,而是通过中间桥梁ViewModel来同步
- mvvm主要解决了:mvc中大量的DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验
MVVM应用场景
- 针对具有复杂交互逻辑的前端应用
- 提供基础的架构抽象
- 通过Ajax数据持久化,保证前端用户体验
MVVM的实现原理:
MVVM的实现主要是三个核心点:框架
- 响应式:vue如何监听data的属性变化
- 模板解析:vue的模板是如何被解析的
- 渲染:vue模板是如何被渲染成HTML的
响应式:
对于MVVM来讲,data通常是放在一个对象当中,就好比这样:dom
var obj = {
name: 'zhangsan',
age: 25
}
当咱们访问或修改obj的属性的时候,好比:
console.log(obj.name) //访问
obj.age = 22 //修改
可是这样的操做vue自己是没有办法感知到的,那么应该如何让vue知道咱们进行了访问或是修改的操做呢?
那就要使用Object.defineProperty
var vm = {}
var data = {
name: 'zhangsan',
age: 20
}
var key, value
for (key in data) {
(function (key) {
Object.defineProperty(vm, key, {
get: function () {
console.log('get', data[key]) // 监听
return data[key]
},
set: function (newVal) {
console.log('set', newVal) // 监听
data[key] = newVal
}
})
})(key)
}
经过Object.defineProperty将data里的每个属性的访问与修改都变成了一个函数,在函数get和set中咱们便可监听到data的属性发生了改变。
模板解析:
首先模板是什么?
模板本质上是一串字符串,它看起来和html的格式很相像,实际上有很大的区别,由于模板自己还带有逻辑运算,好比v-if,v-for等等,但它最后仍是要转换为html来显示。
<div id="app">
<div>
<input v-model="title">
<button v-on:click="add">submit</button>
</div>
<div>
<ul>
<li v-for="item in list">{{item}}</li>
</ul>
</div>
</div>
模板在vue中必须转换为JS代码,缘由在于:在前端环境下,只有JS才是一个图灵完备语言,才能实现逻辑运算,以及渲染为html页面。
这里就引出了vue中一个特别重要的函数——render
render函数中的核心就是with函数。
with函数将某个对象添加到做用域链的顶部,若是在 statement中有某个未使用命名空间的变量,跟做用域链中的某个属性同名,则这个变量将指向这个属性值。
举个例子:
var obj = {
name: 'zhangsan',
age: 20,
getAddress: function () {
alert('beijing')
}
}
function fn1() {
with(obj) {
alert(age)
alert(name)
getAddress()
}
}
fn1()
with将obj这个对象放在了本身函数的做用域链的顶部,当执行下列函数时,就会自动到obj这个对象去寻找同名的属性。
而在render函数中,with的用法是这样:
<div id="app">
<div>
<input v-model="title">
<button v-on:click="add">submit</button>
</div>
<div>
<ul>
<li v-for="item in list">{{item}}</li>
</ul>
</div>
</div>
// 对应的js文件
var data = {
title: '',
list: []
}
// 初始化 Vue 实例
var vm = new Vue({
el: '#app',
data: data,
methods: {
add: function () {
this.list.push(this.title)
this.title = ''
}
}
})
with(this){ // this 就是 vm
return _c(
'div',
{
attrs:{"id":"app"}
},
[
_c(
'div',
[
_c(
'input',
{
directives:[
{
name:"model",
rawName:"v-model",
value:(title),
expression:"title"
}
],
domProps:{
"value":(title)
},
on:{
"input":function($event){
if($event.target.composing)return;
title=$event.target.value
}
}
}
),
_v(" "),
_c(
'button',
{
on:{
"click":add
}
},
[_v("submit")]
)
]
),
_v(" "),
_c('div',
[
_c(
'ul',
_l((list),function(item){return _c('li',[_v(_s(item))])})
)
]
)
]
)
}
在一开始,由于new操做符,因此this指向了vm,经过with咱们将vm这个对象放在做用域链的顶部,由于在函数内部咱们会屡次调用vm内部的属性,因此使用with能够缩短变量长度,提供系统运行效率。
其中的_c函数表示的是建立一个新的html元素,其基本用法为:
_c(element,{attrs},[children...])
其中的element表示所要建立的html元素类型,attrs表示所要建立的元素的属性,children表示该html元素的子元素。
_v函数表示建立一个文本节点,_l函数表示建立一个数组。
最终render函数返回的是一个虚拟DOM。
如何将模板渲染为html
模板渲染为html分为两种状况,第一种是初次渲染的时候,第二种是渲染以后数据发生改变的时候,它们都须要调用updateComponent,其形式以下:
vm._update(vnode){
const prevVnode = vm._vnode
vm._vnode = vnode
if (!prevVnode){
vm.$el = vm.__patch__(vm.$el,vnode)
} else {
vm.$el = vm.__patch__(prevVnode,vnode)
}
}
function updateComponent(){
vm._update(vm._render())
}
首先读取当前的虚拟DOM——vm._vnode,判断其是否为空,若为空,则为初次渲染,将虚拟DOM所有渲染到所对应的容器当中(vm.$el),若不为空,则是数据发生了修改,经过响应式咱们能够监听到这一状况,使用diff算法完成新旧对比并修改。
文章到此差不多结束,MVVM框架学习;这一篇主要讲解,MVVM的原理以及它的常用控件、模式以及优缺点。想更深入的学习大家可前往《架构师的技术手册》获取资料学习。
Android路漫漫,学习永无止境!