vue对数据的监听都知道是通过Object.defineProperty,今天简单把过程捋了一下,弄清楚vue对数据的处理。
//index.js
//声明vue构造函数,进行初始化
function Vue(options) {
this._init(options);
}
Vue.prototype._init = function (options) {
//定义一个vm
let vm = this;
vm.$options = options;
//初始化
initState(vm);
}
//observe/index.js
function initState(vm) {
//初始化props methods data computed watch
let opts = vm.$options;
if(opts.data){
initData(vm);
}
// if(opts.watch){
// initWatch(opts.watch);
// }
}
function initData(vm) {
let data = vm.$options.data;
//挂载data到vm_data,不改变原data
data = vm._data = typeof data === 'function' ? data.call(vm) : data || {};
//代理data上的数据到_data
for (let key in data) {
proxy(vm, '_data', key)
}
//数据进行观察
observe(vm._data);
}
function proxy(vm, source, key) {
Object.defineProperty(vm, key, {
get(){
return vm[source][key];
},
set(newValue){
vm[source][key] = newValue;
}
})
}
function observe(data) {
//如果不是对象,就不进行监听{a:5},监听了a,不监听5
if(typeof data !== 'object' || data === null){
return
}
return new Observer(data);
}
//observe/observer.js
function Observer(data) {
//如果是数组另外处理
if(Array.isArray(data)){
//劫持数组方法
data.__proto__ = arrrayMethods;
//数组每一项进行监听
observerArray(data);
}else{
//对象进行监听
this.walk(data);
}
}
Observer.prototype.walk = function (data) {
let keys = Object.keys(data);
for (let i = 0; i < keys.length; i++) {
defineReactive(data, keys[i], data[keys[i]]);
}
}
//核心代码,对对象进行监听
function defineReactive(data, key, value) {
//判断,如果对象里面嵌套对象,递归监听
//vue一个性能痛点,vue3用proxy改进,proxy兼容会有点问题
observe(value);
//核心
Object.defineProperty(data, key, {
get(){
console.log('get---' + key + '---' + value);
return value;
},
set(newValue){
console.log('set---' + key + '---' + value);
if(value === newValue){
return
}
value = newValue;
}
})
}
//observe/array.js
let arrayProto = Array.prototype;
//拷贝数组方法
let arrrayMethods = Object.create(arrayProto);
let changeMethods = ['push', 'pop', 'unshift', 'shift', 'sort', 'reverse', 'splice'];
changeMethods.forEach(method => {
//对七个会改变原数组的方法进行劫持,切片编程
arrrayMethods[method] = function (...args) {
let inserted;
switch (method) {
case 'push':
case 'unshift':
inserted = args;
break;
case 'splice':
inserted = args.splice(2);
break;
default:
break;
};
//对于新增的数组项进行监听
if(inserted) observerArray(inserted);
//最终还是调用数组原方法
return arrayProto[method].apply(this, args);
}
})
function observerArray(inserted) {
for (let i = 0; i < inserted.length; i++) {
observe(inserted[i]);
}
}
然后就可以去调用:
let vm = new Vue({
data(){
return {
msg: 'hello',
obj: {a: 10},
arr: [1, 2, 3]
}
},
methods:{},
})
console.log(vm.$options);
这边只是很简单的理了一下,源码大概也能找到这些方法,源码当然更复杂,把方法都写到一起了,如果是npm包的话是通过import和export导入导出,这边也有把划分的写了一下。
真的觉得好好理解一下,对于vue的数据响应有很大帮助,复制到编辑器里,静下来理一理,你会发现顿悟一样。
image
网友评论