我是靠谱客的博主 热情牛排,最近开发中收集的这篇文章主要介绍Vue 数据响应式一、MVVM模式二、数据响应式三、Object.defineProperty()四、observe:递归侦测对象全部属性五、数组的响应式处理,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一、MVVM模式

现在常用的框架 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
})
  • 小程序和 React 属于是侵入式的, 数据响应的方式比较直观,是通过主动调函数修改数据的同时触发视图更新。
  • Vue 的方式是非侵入式,Vue 的数据只是通过修改变量本身就触发了视图更新。Vue 利用了 JS 的检测对象属性变化相关的 API 去劫持数据更新视图。

Vue2 侦测数据变化使用的是 Object.defineProperty() 方法,通过定义对象属性上的 get 和 set 方法,在 get 方法中收集观察者,在 set 方法中通知观察者们进行更新视图或处理其他事务。

三、Object.defineProperty()

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)

四、observe:递归侦测对象全部属性

observe 作用

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 方法

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 类用于将传进来的对象的每个属性都处理成响应式数据。并将 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
})
}

defineReactive 利用 observe 处理对象类型

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 七个数组元素操作的方法进行改写。

未完待续。。。

最后

以上就是热情牛排为你收集整理的Vue 数据响应式一、MVVM模式二、数据响应式三、Object.defineProperty()四、observe:递归侦测对象全部属性五、数组的响应式处理的全部内容,希望文章能够帮你解决Vue 数据响应式一、MVVM模式二、数据响应式三、Object.defineProperty()四、observe:递归侦测对象全部属性五、数组的响应式处理所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(62)

评论列表共有 0 条评论

立即
投稿
返回
顶部