第20天 Vue 对MVVM理解

View :界⾯
Model :数据模型
ViewModel :作为桥梁负责沟通 View 和 Model

MVVM

在 MVVM 中, UI 是通过数据驱动的,数据⼀旦改变就会相应的刷新对应的 UI , UI
如果改变,也会改变对应的数据。这种⽅式就可以在业务处理中只关⼼数 据的流转,⽽⽆需直接和⻚⾯打交道。 ViewModel只关⼼数据和业务的处理,不关 ⼼ View 如何处理数据,在这种情况下, View 和 Model 都可以独⽴出来,任何⼀⽅改变了也不⼀定需要改变另⼀⽅,并且可以将⼀些可复⽤的逻辑放在⼀个 ViewModel 中,让多个 View 复⽤这个 ViewModel。

在 MVVM 中,最核⼼的也就是数据双向绑定,例如 Angluar 的脏数据检测, Vue 中的
数据劫持。

脏数据检测

当触发了指定事件后会进⼊脏数据检测,这时会调⽤ $digest 循环遍历所有的数据观察者,判断当前值是否和先前的值有区别,如果检测到变化的话,会 调⽤ $watch 函数,然后再次调⽤ $digest循环直到发现没有变化。循环 ⾄少为⼆次 ,⾄多为⼗次。

数据劫持

Vue 内部使⽤了 Object.defineProperty() 来实现双向绑定,通过这个 函数可以监听到 set 和 get 的事件

var data = { name: 'yck' }
observe(data)
let name = data.name // -> get value
data.name = 'yyy' // -> change value
function observe(obj) {
 // 判断类型
 if (!obj || typeof obj !== 'object') {
 	return
 }
 Object.keys(obj).forEach(key => {
 	defineReactive(obj, key, obj[key])
 }) 
}
function defineReactive(obj, key, val) {
 // 递归⼦属性
 observe(val)
 Object.defineProperty(obj, key, {
	 enumerable: true,
	 configurable: true,
	 get: function reactiveGetter() {
	 	console.log('get value')
	 	return val
 	 },
	 set: function reactiveSetter(newVal) {
		 console.log('change value')
		 val = newVal
	 }
 }) 
}

以上代码简单的实现了如何监听数据的 set 和 get 的事件,但是仅仅如此 是不够的,还需要在适当的时候给属性添加发布订阅

<div>
 {{name}}
</div>

在解析如上模板代码时,遇到 就会给属性 name 添加发布订阅

// 通过 Dep 解耦
class Dep {
 constructor() {
 	this.subs = []
 }
 addSub(sub) {
 // sub 是 Watcher 实例
 	this.subs.push(sub)
 }
 notify() {
	 this.subs.forEach(sub => {
	 	sub.update()
	 })
 	} 
 }
 // 全局属性,通过该属性配置 Watcher
Dep.target = null
function update(value) {
 document.querySelector('div').innerText = value
}
class Watcher {
 constructor(obj, key, cb) {
	 // 将 Dep.target 指向⾃⼰
	 // 然后触发属性的 getter 添加监听
	 // 最后将 Dep.target 置空
	 Dep.target = this
	 this.cb = cb
	 this.obj = obj
	 this.key = key
	 this.value = obj[key]
	 Dep.target = null
 }
 update() {
 // 获得新值
 	this.value = this.obj[this.key]
 // 调⽤ update ⽅法更新 Dom
 	this.cb(this.value)
 } }
var data = { name: 'yck' }
observe(data)
// 模拟解析到 `{{name}}` 触发的操作
new Watcher(data, 'name', update)
// update Dom innerText
data.name = 'yyy'

接下来,对 defineReactive 函数进⾏改造

function defineReactive(obj, key, val) {
 // 递归⼦属性
 observe(val)
 let dp = new Dep()
 Object.defineProperty(obj, key, {
	 enumerable: true,
	 configurable: true,
	 get: function reactiveGetter() {
	 console.log('get value')
	 // 将 Watcher 添加到订阅
	 if (Dep.target) {
	 dp.addSub(Dep.target)
 }
 return val
 },
 set: function reactiveSetter(newVal) {
 	console.log('change value')
 	val = newVal
 // 执⾏ watcher 的 update ⽅法
 	dp.notify()
 }
 }) 
}

以上实现了⼀个简易的双向绑定,核⼼思路就是⼿动触发⼀次属性的
getter 来实现发布订阅的添加

Proxy 与 Object.defineProperty 对⽐
Object.defineProperty 虽然已经能够实现双向绑定了,但是他还是有缺陷 的。

  • 只能对属性进⾏数据劫持,所以需要深度遍历整个对象 对于数组不能监听到数据的变化
  • 虽然 Vue 中确实能检测到数组数据的变化,但是其实是使⽤了 hack 的办法,并且也是 有缺陷的。
const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)
// hack 以下⼏个函数
const methodsToPatch = [
 'push',
 'pop',
 'shift',
 'unshift',
 'splice',
 'sort',
 'reverse'
 ]
methodsToPatch.forEach(function (method) {
 // 获得原⽣函数
 const original = arrayProto[method]
 def(arrayMethods, method, function mutator (...args) {
 // 调⽤原⽣函数
	 const result = original.apply(this, args)
	 const ob = this.__ob__
	 let inserted
	 switch (method) {
		 case 'push':
		 case 'unshift':
		 inserted = args
		 break
		 case 'splice':
		 inserted = args.slice(2)
		 break
	 	}
	 if (inserted) ob.observeArray(inserted)
	 // 触发更新
	 ob.dep.notify()
	 return result
 	}) 
 })

反观 Proxy 就没以上的问题,原⽣⽀持监听数组变化,并且可以直接对整个 对象进⾏拦截,所以 Vue 也将在Vue3.0版本中使⽤ Proxy 替换Object.defineProperty

let onWatch = (obj, setBind, getLogger) => {
	 let handler = {
		 get(target, property, receiver) {
		 getLogger(target, property)
		 return Reflect.get(target, property, receiver);
	 },
	 set(target, property, value, receiver) {
		 setBind(value);
		 return Reflect.set(target, property, value);
	 }
  };
 return new Proxy(obj, handler); 
};
let obj = { a: 1 }
let value
let p = onWatch(obj, (v) => {value = v}, (target, property) => {
 console.log(`Get '${property}' = ${target[property]}`); 
})
p.a = 2 // bind `value` to `2`
p.a // -> Get 'a' = 2
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue中,MVVM(Model-View-ViewModel)是一种软件架构模式的理解MVVM将应用程序分为三个部分:Model(模型),View(视图)和ViewModel(视图模型)。 在Vue中,Model代表着应用程序的数据和业务逻辑,View则是用户所看到的界面。ViewModel作为连接Model和View的桥梁,负责监听Model的变化,并将数据和状态同步到View上。ViewModel还负责处理用户的交互行为,将用户的操作反馈到Model中。这样,ViewModel实现了视图和模型层的解耦,使得应用程序更容易维护和扩展。 为了实现MVVM的双向绑定,Vue使用了Object.defineProperty方法,它能够在对象上定义新的属性或修改现有属性的行为。通过这种方式,当Model中的数据发生变化时,ViewModel能够监听到并及时更新View的内容,反之亦然。这种双向绑定的机制使得开发者无需手动操作DOM,使得开发更加方便和高效。 需要注意的是,在Vue中,ViewModel并不完全取代了Controller,而是将展示的业务逻辑从Controller中抽离出来,实现了业务逻辑组件的重用。其他视图操作和业务逻辑仍然应该放在Controller中实现。 综上所述,Vue中的MVVM是一种软件架构模式,通过将应用程序分为Model、View和ViewModel三个部分,实现了视图和模型层的解耦,并借助双向绑定机制实现数据的自动更新。这种架构模式能够提高开发效率和代码的可维护性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值