让前端飞Web前端之路

vue数据监听和劫持

2020-04-08  本文已影响0人  wade3po

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
上一篇下一篇

猜你喜欢

热点阅读