赞
踩
现在常用的框架 Vue 和 React 都是 MVVM 模式。MVVM是Model-View-ViewModel的简写。M 表示 Model,V 表示 View,VM 表示 View-Model,即数据和模型的桥梁。MVVM 本质上就是 MVC 的改进版。MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开。
例子:
模版
<span> {{count}}</span>
数据变化
this.count++
数据变化时,视图中的数值也变化。
数据响应式的实现分为侵入式和非侵入式。下面几种框架的数据触发视图改变的写法。
Vue 的数据变化
this.count++
React 的数据变化
this.setState({
count: this.state.count + 1
})
小程序数据变化
this.setData({
count: this.data.count + 1
})
Vue2 侦测数据变化使用的是 Object.defineProperty() 方法,通过定义对象属性上的 get 和 set 方法,在 get 方法中收集观察者,在 set 方法中通知观察者们进行更新视图或处理其他事务。
Vue2 检测数据变化利用的是 Object.defineProperty() 方法来定义 属性的 get 和 set 方法来实现。该方法可直接在一个对象上定义新的属性或修改现有属性,并返回该对象。
const obj = {} let _count = 0 Object.defineProperty(obj,"count", { get() { console.log("获取 count 属性") return _count }, set(val) { console.log("修改 count 属性为" + val) _count = val } }) console.log(obj.count) obj.count = 10 console.log(obj.count)
四、defineReactive 方法
如上 Object.defineProperty 方法的例子中,在定义 obj.count 属性时,需要使用一个临时变量 _count 来存放属性值供 get 和 set 方法使用,这时候 _count 放于外部会显得很累赘。Vue2 中利用闭包的将
Object.defineProperty 封装到 defineReactive 方法中,临时变量也存放在该方法中。
defineReactive 方法用于将对象的一个属性值定义为响应式。
const obj = {} function defineReactive(data, key, value) { // 当不传 value 时表示监听 data[key],但不修改值。 if(arguments.length === 2) { value = data[key] } Object.defineProperty(data, key, { enumerable: true, configurale: true, get () { console.log(`获取 ${key} 属性`) return value }, set (newValue) { console.log(`修改 ${key} 属性为` + newValue) if (value === newValue) return value = newValue } }) } defineReactive(obj, "count", 0) console.log(obj.count) obj.count = 10 console.log(obj.count)
const obj = {
a: {
b: {
c: 0
}
},
d: 0
}
defineReactive(obj, "a")
defineReactive(obj, "d")
console.log(obj.a.b)
console.log(obj.d)
如上例子,defineReactive 方法只能侦测单个属性值,例子中的使用方式无法侦测 obj.a.b 和 obj.a.b.c 的属性的获取和修改。
在 Vue2 中定义了 observe 方法,用于将一个正常的 obejct 转换成每个层级的属性都是响应式。
observe(obj)
obserse 方法来将一个普通对象通过 new Oberserver() 处理为响应式对象。
observe 方法实现如下:
/**
* 将一个普通对象转成响应式对象
*/
function observe(value) {
if (typeof value !== "object") return;
let ob
// 响应式的对象处理后用 __ob__ 属性指向自身,标识为响应式对象
if (typeof value.__ob__ !== "undefined") {
ob = value.__ob__
} else {
//
ob = new Observer(value)
}
return ob
}
Observer 类用于将传进来的对象的每个属性都处理成响应式数据。并将 Observer 的实例挂在该对象的 __ob__
属性上。
class Observer { constructor(normalObj) { // 将 Observer 的实例挂在该对象的 __ob__ 属性上 def(normalObj, "__ob__", this, false) this.defineReactiveObj(normalObj) } /** * 将对象的所有属性设为响应式 */ defineReactiveObj(value) { console.log('将对象的所有属性设为响应式', value); for (let key in value) { defineReactive(value, key) } } } /** * 定义一个对象属性值,并可设置是否为可枚举。 */ function def(obj, key, value, enumerable) { Object.defineProperty(obj, key, { value, enumerable, writable: true, configurable: true }) }
observe() 、 new Observer() 两个方法将传入对象的每个可枚举属性都通过 defineReactive 方法进行数据劫持。
之前定义的 defineReactive 的只劫持了 data[key] 这个值,这样相当于只对传进来的对象的可枚举属性都做了数据劫持。如果有个属性的属性值为对象类型,则其属性修改就不能被劫持到。
为此需要对
defineReactive 方法做如下修改,让对象的每个属性的属性值都通过 observe 方法进行响应式处理,这样递归下去层层修改为响应式对象。并且在 set 方法传入的新值也需要处理为响应式。
function defineReactive(data, key, value) { // 当不传 value 时表示监听 data[key],但不修改值。 if (arguments.length === 2) { value = data[key] } // 将 object 类型的属性值修改为响应式对象 let childOb = observe(value) Object.defineProperty(data, key, { enumerable: true, configurable: true, get() { console.log(`获取 ${key} 属性`) return value }, set(newValue) { console.log(`修改 ${key} 属性为` + newValue) if (value === newValue || childOb === newValue) { return } // 修改的新值也处理为响应式 childOb = observe(newValue) } }) }
使用示例
let obj = { a: { b: { c: 0 } }, d: 0 } observe(obj) obj.a.b.c = 10 console.log(obj.a.b) /* 输出: 获取 a 属性 获取 b 属性 修改 c 属性为10 获取 a 属性 获取 b 属性 */
let obj = {
a: [1,2,3]
}
observe(obj)
obj.a.push(4)
/*
输出:
获取 a 属性
*/
如例子所示,当前的 observe
方法对数组的数据劫持处理是不起作用的,这不能侦测到 obj.a
属性被修改。所以还需要对数值类型做进一步处理。
这里需要对 push/pop/shift/splice/sort/reverse 七个数组元素操作的方法进行改写。
未完待续。。。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。