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.jsfunction 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.jsfunction 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.jslet 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的数据响应有很大帮助,复制到编辑器里,静下来理一理,你会发现顿悟一样。