数据响应式原理及源码

对象的响应式原理

利用闭包来实现defineReactive函数的封装,避免需要提前定义变量才可以实现set函数:

let obj = {}
// let item 不需要在提前定义变量
function defineReactive(data, key, val) {
  Object.defineProperty(data, key, {
    enumerable: true,
    configurable: true,
    get() {
      return val
    },
    set(newVal) {
      val = newVal
    }
  })
}

defineReactive(obj, 'a', 5)
obj.a = 9
console.log(obj.a)
复制代码

对象的一个循环递归图:

类 Observer作用:将一个正常的object转换为每个层级的属性都是响应式(可以被侦测)的。

image.png

index.js

import { observe } from './observe'
let obj = {
  a: {
    m: {
      n: 5
    }
  },
  b: 10
}

observe(obj)
console.log(obj.a.m.n)
复制代码

observe: 就是为了在调用类Observer的之前,进行判断,看是否需要进行Observer

import Observer from './Observer'

export const observe = (value) => {
  if (typeof value != 'object') return

  var ob
  if (typeof value.__ob__ !== 'undefined') {
    ob = value.__ob__
  } else {
    ob = new Observer(value)
  }
  return ob
}
复制代码

Observer类:利用defineReactive,对属性进行循环遍历绑定getter、setter

import { def } from './utils'
import defineReactive from './defineReactive'

export default class Observer {
  constructor(value) {
    def(value, '__ob__', this, false)
    this.walk(value)
  }

  //    遍历
  walk(value) {
    for (const k in value) {
      defineReactive(value, k)
    }
  }
}
复制代码

defineReactive :利用闭包,不需要在使用变量(上面有讲到),给属性绑定getter和setter

import { observe } from './observe'

export default function defineReactive(data, key, val) {
  console.log(data, key, 999)
  if (arguments.length == 2) {
    val = data[key]
  }
  let childOb = observe(val)
  Object.defineProperty(data, key, {
    enumerable: true,
    configurable: true,
    get() {
      console.log('试图访问' + key)
      return val
    },
    set(newVal) {
      val = newVal
      childOb = observe(newVal)
    }
  })
}
复制代码

util.js :在类Observer中使用,为了给每个属性绑定__ob__属性,同时设置enumerable

export const def = (obj, key, value, enumerable) => {
  Object.defineProperty(obj, key, {
    value,
    enumerable,
    writable: true,
    configurable: true
  })
}
复制代码

数组的响应式原理

对数组的7个函数进行重写,利用Object.setPrototypeOf方法进行处理,将这些方法的原型指向变异后的数组。但是我们原生的数组方法并不能丢失,我们需要复制取得原声的数组方法,在监听的时候进行apply改变this指向,传入arguments,调用复制的原生方法处理。说白了,就是要利用原生数组方法的功能,变异为可以被监听的方法
['push', 'pop', 'shift', 'unshift', 'splice', 'reverse', 'sort']

复制代码

//看看就行了,不完整
const nums = [1,2,3];

const ArrayProto = Array.prototype;

const arrayArguments = [];

const ArrayMethods = ['push'];

ArrayMethods.forEach((method) => {
    // 原生方法
    let orginal = ArrayProto[method];

    arrayArguments[method] = function() {
        console.log('push');

        return orginal.apply(this, arguments);
    }
})
// 重点
nums.__proto__ = arrayArguments;

nums.push(1);


console.log(nums)

复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享