理解 Vue 的 MVVM 模式
程序员文章站
2022-03-30 23:10:02
...
MVVM 模式实现
MVVM模式又称双向数据绑定,是数据影响视图,视图影响数据的模式
Vue: 实现方式使用数据劫持+ 发布订阅模式,其核心为 Object.defineProperty 不兼容IE8以下
Angualr: 使用脏值检测
这次主要介绍 Vue 如何通过 Object.defineProperty() 实现 MVVM
Object.defineProperty(obj, ‘propertyName’, {
value: Value,
enumerable: boolean, //默认为fasle 其他属性为不可枚举 无法使用for遍历
configurable: boolean, //默认false时不能进行删除
writable: boolean, // 默认为false时无法修改
// 当有writable和value时不可以使用set get
//获取obj的值时调用
get() {
return 'silverwing'
},
//当给obj赋值时调用,参数为赋值的参数
set(val) {
console.log(val)
}
})
数据劫持
使用 Object.defineProperty
定义所有属性
Vue 实现 MVVM 的方式主要通过data里面的值 递归的进行数据劫持达到深度响应(因为每次赋予一个新对象时会给这个新对象增加数据劫持)
让我们手动模拟这么一个实现过程:
先创建 一个 html(任意名字) 和 mvvm.js 在同一个目录下
先创建 一个 html(任意名字) 和 mvvm.js 在同一个目录下
*.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
let obj = {}
Object.defineProperty(obj, 'school',{
value:'silverwing'
})
delete obj.school
</script>
</body>
</html>
mvvm.js
function Soyas(options = {}) {
this.$options = options // 将所有属性挂在在了$options
// this._data
var data = this._data = this.$options.data;
observe(data)
}
function observe(data) {
if (typeof data !== 'object')
return new Observe(data)
}
// 观察对象增加Object.defineProperty
function Observe(data) { // 这里写主要逻辑
for (let key in data) { // 把 data 属性 通过Object.defineProperty方式定义属性
let val = data[key];
Object.defineProperty(data, key, {
enumerable: true,
get() {
return val;
},
set(newVal) { // 更改值的时候
if (newVal === val) {
// 设置的值和旧值一样的时候
return;
}
val = newVal; // 如果以后获取值的时候将刚才设置的值放回去
}
})
}
}
控制台查看
这样就完成了一个简单的数据劫持
但是任然存在问题
当赋予一个对象时候我们仍需要对内部的对象进行数据劫持,以达到深度响应
如果没有进行数据劫持的情况下,属性不具有 set() get() 方法,此时是无法实现数据相应的
function Observe(data) { // 这里写主要逻辑
for (let key in data) { // 把 data 属性 通过Object.defineProperty方式定义属性
let val = data[key];
observe(val); // 添加劫持
Object.defineProperty(data, key, {
...
set(newVal){
...
observe(newVal) // 返回对象进行劫持
}
})
}
}
再次查看控制台
如此即可完成数据的每层对象进行劫持达到深度响应的效果
此时我们还需要做一件事,我们每次通过 soyas._data 这种方式访问并不优雅
我们可以通过数据代理达到 soyas.property 的方式访问
function Soyas(options = {}) {
...
// this 代理了 this._data;
for (let key in data) {
Object.defineProperty(this, key, {
enumerable: true,
get() {
return this._data[key];
},
set(newVal) {
this._data = newVal
}
})
}
}
添加完上述代码后
Very Good!一切OK
总结:
——Soyas 类内部 通过调用一个 observe 进行数据劫持到达到双向数据绑定
——达到深度响应我们需要在 Observe 遍历数据,和返回新值时候 递归调用 observe,对每一个对象进行数据劫持
——通过对象的内部使用 Object.defineProperty 使用 this 代理 this._data
上一篇: 这个月为啥不给我们生活费