Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vue 2.x 的响应式原理 #33

Open
lovelmh13 opened this issue Feb 12, 2020 · 0 comments
Open

Vue 2.x 的响应式原理 #33

lovelmh13 opened this issue Feb 12, 2020 · 0 comments

Comments

@lovelmh13
Copy link
Owner

Vue 2.x 的响应式主要就是利用Object.defineProperty来重新定义data里的每个属性。

这篇只写了观察属性,没有提及收集依赖

let vm = new Vue({
	el: '#app',
	data() {
		return {
			msg: 'hello',
			school: { name: 'lmh', age: 24 },
			arr: [1, 2, 3]
		}
	}
})

当我们创建一个Vue实例的时候,传入了data,可以是一个对象,也可以是一个函数

所以在vue里面,需要对data做接收
new Vue的时候,需要经过初始化阶段,其中就有初始化数据的阶段(把数据重定义为响应式的)。

import { initState } from './observe';
function Vue(options) {
	this._init(options);    // 初始化
}

Vue.prototype._init = function(options) {
	let vm = this; // 把this叫成vm,方便表示this是实例,方便看,不用多想this指向
	vm.$options = options;

	// MVVM原理,数据响应式,需要重新初始化数据
	initState(vm);
};
export default Vue;

看一下initState函数是什么初始化数据的

import Observe from './observe';

export function initState(vm) {
	let opts = vm.$options;
	if (opts.data) {
		initData(vm); // 如果有数据,就初始化数据
	}
}

function initData(vm) {
	let { data } = vm.$options;
	// 给vm新加了一个_data属性。需要判断一下,data是函数还是数据。函数的话,要执行拿到数据
	data = vm._data = typeof data === 'function' ? data.call(vm) : data; // 不太想得明白,为啥要call
	observe(vm._data);  // 对属性进行观察
}

export function observe(data) {
	if (typeof data !== 'object' || data == null) {
		return;	// 不过不是对象或者为空,就不用观察
	}
	return new Observe(data);
}

接下来到了核心了:Observe观察

// 监控,就是通过循环,给每个属性重新定义
class Observe {
	constructor(data) {
		this.walk(data);
	}

	// 遍历data里的属性,给每个属性都用defineProperty重新定义
	walk(data) {
		let keys = Object.keys(data);
		for (let i = 0; i < keys.length; i++) {
			let key = keys[i];
			let value = data[key];
			defineReactive(data, key, value);
		}
	}
}

export function defineReactive(data, key, value) {
	observe(value);	// 递归,如果value还是对象,那么同样给里面的属性都重新定义成响应式的
	Object.defineProperty(data, key, {
		// 定义、修改data上的,key属性
		get() {
			return value;
		},
		set(newValue) {
			if (newValue === value) {
				return;
			}
			value = newValue;
			console.log('更新视图')
		}
	});
}

export default Observe;

然后,就可以通过vm._data.msg 拿到一开始创建vue实例的时候创建的msg的数据了。

但是,在用的时候,我们都是通过vm.msg拿到的数据,而不是vm._data.msg。对于这个,我们只需要再做一层代理,使得我们通过vm.msg拿数据的时候,返回vm._data.msg的数据就可以了。

看回上面第二段的initData函数,在这里,我们给vm新添加了一个_data属性,并且把参数里的data的值挂到了上面。这里,我们就做一下刚才说的所需要的处理:

function initData(vm) {
	let { data } = vm.$options;
	// 给vm新加了一个_data属性。需要判断一下,data是函数还是数据。函数的话,要执行拿到数据
	data = vm._data = typeof data === 'function' ? data.call(vm) : data; // 不太想得明白,为啥要call
	// 添加一个代理,使得在获取数据的时候,不需要再vm._data.xx得到数据,而是直接vm.xx就可以拿到数据
	for (let key in data) { // 遍历每一个属性,给每个属性都加上代理
		proxy(vm, '_data', key);
	}
	observe(vm._data);	// 对属性进行观察
}

function proxy(vm, source, key) {
	Object.defineProperty(vm, key, {  // 给vm加上data里的属性
		get() {  // 如果 vm.msg,实际上返回vm._data.msg
			return vm[source][key]
		},
		set(newValue) {
			return  vm[source][key] = newValue;
		}
	})
}

这样,就可以直接获取到vm.msg的值了。

不过,这里我还有问题没有搞懂,如果获取vm.school或者vm._data.school的值,打印出来是{},需要深拷贝JSON.parse(JSON.stringify(vm.school)才可以正常显示。
微信图片_20200212161633

在用实际的vue的时候也遇到过,不知道为啥。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant