# 基础概念
在使用Vue的过程中,当我们改变数据的时候,页面也会同样的改变,这就是数据的响应式,也就是双向绑定。
2 秒后页面的内容会变成hello world
<template>
<div>{{ msg }}</div>
</template>
<script>
export default {
data() {
return {
msg: "hello",
};
},
mounted() {
setTimeout(() => {
this.msg = "hello world";
}, 2000);
},
};
</script>
Vue.js主要通过Object.defineproperty(obj,key,desc)来定义响应式对象,其中最关键的属性是set,get。
Object.defineProperty(obj, key, {
get: function () {
// 当访问obj.key执行
return val;
},
set: function (newVal) {
// 当设置obj.key = '111',执行
val = newVal;
},
});
Vue.js会把data,props,computed等都变成响应式的对象。
# proxy
proxy 主要是用来进行数据的代理,在core/instance/state.js
const sharedPropertyDefinition = {
enumerable: true,
configurable: true,
get: noop,
set: noop,
};
export function proxy(target: Object, sourceKey: string, key: string) {
sharedPropertyDefinition.get = function proxyGetter() {
return this[sourceKey][key];
};
sharedPropertyDefinition.set = function proxySetter(val) {
this[sourceKey][key] = val;
};
Object.defineProperty(target, key, sharedPropertyDefinition);
}
在源码中,经常可以看到proxy(vm, _props, key),这里其实就是将_props内的数据作为sourceKey,每次通过this实例获取的时候,可以从_props内获取,其余data,computed等都是类似。
// 等同于this._props.age
this.age;
# observe
observe对数据进行响应式的处理,位置在core/observer/index.js
export function observe(value: any, asRootData: ?boolean): Observer | void {
// 不需要做响应式处理
if (!isObject(value) || value instanceof VNode) {
return;
}
// __ob__ 就是observer对象
let ob: Observer | void;
if (hasOwn(value, "__ob__") && value.__ob__ instanceof Observer) {
ob = value.__ob__;
} else if (
// 是否可以进行响应式处理,isVue为vue实例
shouldObserve &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
ob = new Observer(value);
}
if (asRootData && ob) {
ob.vmCount++;
}
return ob;
}
- 首先判断是否是对象,并且不能是 VNode 实例,否则直接返回
- 如果有
__ob__属性并且是Observer的实例,就说明该对象就是响应式对象,直接返回即可。 - 如果没有
__ob__属性,同时满足一些条件的情况下,会new Observer(value),最后如果是根实例,vmCount =1
# OBserver
export class Observer {
value: any;
dep: Dep;
vmCount: number; // number of vms that have this object as root $data
constructor(value: any) {
this.value = value;
this.dep = new Dep();
this.vmCount = 0;
def(value, "__ob__", this);
if (Array.isArray(value)) {
if (hasProto) {
protoAugment(value, arrayMethods);
} else {
copyAugment(value, arrayMethods, arrayKeys);
}
this.observeArray(value);
} else {
this.walk(value);
}
}
walk(obj: Object) {
const keys = Object.keys(obj);
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i]);
}
}
observeArray(items: Array<any>) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i]);
}
}
}
- 定义
__ob__存储实例 - 区分数组和对象,如果是对象,则调用
walk方法遍历defineReactive - 如果是数组,遍历递归调用所有的子元素,同时改写
array处理元素的方法,使其具有响应式。
# defineReactive
export function defineReactive(
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
const dep = new Dep();
const property = Object.getOwnPropertyDescriptor(obj, key);
if (property && property.configurable === false) {
return;
}
// cater for pre-defined getter/setters
const getter = property && property.get;
const setter = property && property.set;
if ((!getter || setter) && arguments.length === 2) {
val = obj[key];
}
let childOb = !shallow && observe(val);
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
const value = getter ? getter.call(obj) : val;
if (Dep.target) {
dep.depend();
if (childOb) {
childOb.dep.depend();
if (Array.isArray(value)) {
dependArray(value);
}
}
}
return value;
},
set: function reactiveSetter(newVal) {
const value = getter ? getter.call(obj) : val;
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return;
}
/* eslint-enable no-self-compare */
if (process.env.NODE_ENV !== "production" && customSetter) {
customSetter();
}
// #7981: for accessor properties without setter
if (getter && !setter) return;
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
childOb = !shallow && observe(newVal);
dep.notify();
},
});
}
- 对
Object.defineProperty()进行一层封装,进行setter和getter处理 - 判断
Object.getOwnPropertyDescriptor()获取属性描述,如果configurable为false,就不能被定义为响应式对象。