1. 对象的单层劫持
- data 为函数和对象的处理,及当 data 为函数时的 this 指向问题
- Observer 类:对数据进行观测
- walk 方法:遍历 data 属性(对象属性遍历)
- defineReactive 方法:利用 Object.defineProperty 实现数据劫持
1,initData 中获取 data
data 在 options 中,而 options 已被 vm 实例共享( vm.$options = options )
function initData(vm){
let data = vm.$options.data;// 拿到 vue 初始化时,用户传入的data数据
console.log("进入 state.js - initData,数据初始化操作", data)
}
2,处理 data 的两种情况
上篇说了,data 有可能是函数,也有可能是对象,因此后续逻辑需要对此进行处理
export function isFunction(val){
return typeof val == 'function'
}
如果 data 是函数,则需要让 data 函数执行,拿到它返回的对象
// 如果 data 是函数,需要执行 data 拿到它的返回值
if(isFunction(data)){
data = data(); // 这样执行,this 不是 vm,而是window
}
测试
let vm = new Vue({
el: '#app',
data() {
console.log("打印 data函数执行时,this的指向")
console.log(this)
return { message: 'Hello Vue' } // data 返回一个对象
}
});
此时,data 函数执行时,this 指向 window
3,处理 this 的指向问题
在 Vue 中,data 函数执行时 this 应指向当前 vm 实例
所以,在 data 执行时绑定 this 为当前 vm 实例
if(isFunction(data)){
data = data.call(vm);// data 执行时,绑定this 为 vm
}
测试:
将 data 是对象和函数的两种情况进行逻辑简化:
// data 可能是函数或对象
// 如果 data 是函数,则需要让 data 函数执行,拿到它返回的对象
// 如果 data 是对象,不做处理
data = isFunction(data) ? data.call(vm) : data;
注意:只有根实例的 data 可以是对象,组件必须为函数
4,核心模块 observe:对数据进行观测
data 数据的响应式原理是:
通过 Object.defineProperty,重写 data 上的所有属性
这就需要遍历 data 对象拿到每一个属性,再逐一通过 Object.defineProperty 重新定义
调用 observe 方法观测数据,实现 data 数据的响应式
import { observe } from "./observe";
import { isFunction } from "./utils";
export function initState(vm) {
const opts = vm.$options;
if (opts.data) {
initData(vm);
}
}
function initData(vm) {
let data = vm.$options.data;
data = isFunction(data) ? data.call(vm) : data;
observe(data); // 使用 observe 实现 data 数据的响应式
}
经过 data = isFunction(data) ? data.call(vm) : data;处理后,此时 data 一定是一个对象
所以,对处理后的 data 进行检测,如果不是对象就结束
// 判断是否为对象:类型是object,且不能为 null
export function isObject(val) {
return typeof val == 'object' && val !== null
}
import { isObject } from "../utils";
export function observe(value) {
// 如果 value 不是对象,就不需要观测了,直接 return
if(!isObject(value)){
return;
}
}
5,Observer 类:对【对象】进行观测
完成的逻辑是这样的:
export function observe(value) {
if(!isObject(value)){
return;
}
// 观测 value 对象,实现数据响应式
return new Observer(value);
}
Observer 类:
遍历对象属性,使用 Object.defineProperty 重新定义 data 对象中的属性
class Observer {
constructor(value){ // 如果value是对象,遍历对象中的属性,使用 Object.defineProperty 重新定义
this.walk(value); // 循环对象属性
}
// 循环 data 对象,使用 Object.keys 不循环原型方法
walk(data){
Object.keys(data).forEach(key => {
// 使用 Object.defineProperty 重新定义 data 对象中的属性
defineReactive(data, key, data[key]);
});
}
}
function defineReactive(obj, key, value) {
Object.defineProperty(obj, key, {
get(){ // 闭包
return value;
},
set(newValue) {
if (newValue === value) return
value = newValue;
}
})
}
至此,obj 中的所有属性都通过 defineProperty 重新定义,具有 get、set 方法
2. 对象的深层劫持
1,实现思路
当对象属性 obj 即将被 Object.defineProperty 劫持时,
再对 obj 对象做一次“对象的单层劫持”
更深层的对象属性劫持,就是在递归执行“对象的单层劫持”
即:当属性为对象类型时(非 null)
继续调用 observe 进行观测,observe 的递归实现了对象属性的深层劫持
2,代码逻辑
function defineReactive(obj, key, value) {
observe(value);// 递归实现深层观测
Object.defineProperty(obj, key, {
get() {
return value;
},
set(newValue) {
if (newValue === value) return
value = newValue;
}
})
}