Data
1. data 的处理
Object.defineProperty - get
,用于 依赖收集Object.defineProperty - set
,用于 依赖更新- 每个
data
声明的属性,都拥有一个的专属依赖收集器subs
- 依赖收集器
subs
保存的依赖是watcher
watcher
可用于 进行视图更新
2. data 监听的实现
JS
function Observer(data) {
// 递归出口
if (!data || typeof data !== "object") return;
Object.keys(data).forEach((key) => {
// 使用defineProperty后属性里的值会被修改 需要提前保存属性的值
let value = data[key];
// 递归劫持data里的子属性
Observer(value);
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
// 收集数据依赖
get() {
return value;
},
// 触发视图更新
set(newVal) {
value = newVal;
// 处理赋值是对象时的情况
Observer(newVal);
},
});
});
}
function Observer(data) {
// 递归出口
if (!data || typeof data !== "object") return;
Object.keys(data).forEach((key) => {
// 使用defineProperty后属性里的值会被修改 需要提前保存属性的值
let value = data[key];
// 递归劫持data里的子属性
Observer(value);
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
// 收集数据依赖
get() {
return value;
},
// 触发视图更新
set(newVal) {
value = newVal;
// 处理赋值是对象时的情况
Observer(newVal);
},
});
});
}
3. data 代理的实现
JS
function initData(vm) {
var data = vm.$options.data;
var keys = Object.keys(data);
var i = keys.length;
data = vm._data = ( typeof data === 'function' ? data.call(vm) : data ) || {};
while (i--) {
var key = keys[i];
if (只要不是_和$开头的属性) {
proxy(vm, "_data", key);
}
}
}
function proxy(target, sourceKey, key) {
Object.defineProperty(target, key, {
get() {
return this[sourceKey][key]
},
set(val) {
this[sourceKey][key] = val;
}
});
}
function initData(vm) {
var data = vm.$options.data;
var keys = Object.keys(data);
var i = keys.length;
data = vm._data = ( typeof data === 'function' ? data.call(vm) : data ) || {};
while (i--) {
var key = keys[i];
if (只要不是_和$开头的属性) {
proxy(vm, "_data", key);
}
}
}
function proxy(target, sourceKey, key) {
Object.defineProperty(target, key, {
get() {
return this[sourceKey][key]
},
set(val) {
this[sourceKey][key] = val;
}
});
}
问题
1. 为什么 data 是一个函数?
- 一个组件被复用多次的话,也就会创建多个实例。本质上,这些实例用的都是同一个构造函数。
- 如果
data
是对象的话,对象属于引用类型,会影响到所有的实例。所以为了保证组件不同的实例之间data
不冲突,data
必须是一个函数(因为函数有自己的作用域)。
2. 如何重置 data 数据?
this.$data
获取当前状态下的 data
this.$options.data()
获取该组件初始状态下的 data
所以,下面就可以将初始状态的 data
复制到当前状态的 data
,实现重置效果:
JS
Object.assign(this.$data, this.$options.data())
Object.assign(this.$data, this.$options.data())
当然,如果你只想重置 data
中的某一个对象或者属性:
JS
this.form = this.$options.data().form
this.form = this.$options.data().form
原因:是 Vue
实例化时有个 vm.$options = options
操作,所以 this.$options.data()
返回 data
中的初始化时数据