Implementation of the responsive principle of Vue2 and Vue3

Learned the notes of coderwhy's advanced JavaScript grammar video class

If there are any mistakes or inappropriate places, please forgive me, welcome to point out and expand, thank you

1. Implementation of the responsive principle of Vue3

  • The following steps are a step-by-step derivation and a step-by-step implementation process.

Step 1: Encapsulation of reactive functions

// 响应式函数的封装
const reactiveFns = []
function watchFn(fn) {
  reactiveFns.push(fn)
}

const obj = {
  name: 'xwl',
  age: 18
}

// watchFn()封装,区分开和普通函数的区别
watchFn(function () {
  console.log('obj.name的值:' + obj.name)
})
watchFn(function () {
  console.log('obj.name2的值:' + obj.name)
})

obj.name = 'xxx'
reactiveFns.forEach(item => {
  item()
})
复制代码

Step 2: Encapsulation of dependent collection classes

The first step is to use an array to manage, so use a class to manage it.

class Depend {
  constructor() {
    this.reactiveFns = []
  }

  addDepend(fn) {
    this.reactiveFns.push(fn)
  }

  notify() {
    this.reactiveFns.forEach(item => {
      item()
    })
  }
}

// 响应式函数的封装
const depend = new Depend()
function watchFn(fn) {
  depend.addDepend(fn)
}

const obj = {
  name: 'xwl',
  age: 18
}

// watchFn()封装,区分开和普通函数的区别
watchFn(function () {
  console.log('obj.name的值:' + obj.name)
})
watchFn(function () {
  console.log('obj.name2的值:' + obj.name)
})

obj.name = 'xxx'
depend.notify()
复制代码

Step 3: Automatically monitor object changes

Use Proxy and Reflect to listen for changes in objects.

class Depend {
  constructor() {
    this.reactiveFns = []
  }

  addDepend(fn) {
    this.reactiveFns.push(fn)
  }

  notify() {
    this.reactiveFns.forEach(item => {
      item()
    })
  }
}

// 响应式函数的封装
const depend = new Depend()
function watchFn(fn) {
  depend.addDepend(fn)
}

const obj = {
  name: 'xwl',
  age: 18
}

const objProxy = new Proxy(obj, {
  get(target, key, receiver) {
    return Reflect.get(target, key, receiver)
  },
  set(target, key, newValue, receiver) {
    Reflect.set(target, key, newValue, receiver)
    depend.notify()
  }
})

// watchFn()封装,区分开和普通函数的区别
watchFn(function () {
  console.log('obj.name的值:' + objProxy.name)
})
watchFn(function () {
  console.log('obj.name2的值:' + objProxy.name)
})

watchFn(function () {
  console.log('obj.age的值:' + objProxy.age)
})

objProxy.name = 'xxx'
objProxy.age = 20
复制代码

Step 4: Dependency Collection Management

This requires the use of Map and WeakMap. The third step is that when objProxy.age = 20 is executed, all responsive functions are executed once, and the responsive functions corresponding to name and age are not distinguished.

class Depend {
  constructor() {
    this.reactiveFns = []
  }

  addDepend(fn) {
    this.reactiveFns.push(fn)
  }

  notify() {
    this.reactiveFns.forEach(item => {
      item()
    })
  }
}

// 响应式函数的封装
const depend = new Depend()
function watchFn(fn) {
  depend.addDepend(fn)
}

// 获取depend函数的封装
const targetMap = new WeakMap()
function getDepend(target, key) {
  let map = targetMap.get(target)
  if (!map) {
    map = new Map()
    targetMap.set(target, map)
  }

  let depend = map.get(key)
  if (!depend) {
    depend = new Depend()
    map.set(key, depend)
  }

  return depend
}

const obj = {
  name: 'xwl',
  age: 18
}

const objProxy = new Proxy(obj, {
  get(target, key, receiver) {
    return Reflect.get(target, key, receiver)
  },
  set(target, key, newValue, receiver) {
    Reflect.set(target, key, newValue, receiver)
    const depend = getDepend(target, key)
    depend.notify()
  }
})

// watchFn()封装,区分开和普通函数的区别
watchFn(function () {
  console.log('obj.name的值:' + objProxy.name)
})
watchFn(function () {
  console.log('obj.name2的值:' + objProxy.name)
})

watchFn(function () {
  console.log('obj.age的值:' + objProxy.age)
})

objProxy.name = 'xxx'
objProxy.age = 20
复制代码

Step 5: Correctly collect dependencies

class Depend {
  constructor() {
    this.reactiveFns = []
  }

  addDepend(fn) {
    this.reactiveFns.push(fn)
  }

  notify() {
    this.reactiveFns.forEach(item => {
      item()
    })
  }
}

// 响应式函数的封装
let activeReactiveFn = null //用于给depend.addDepend(activeReactiveFn)传递参数
function watchFn(fn) {
  activeReactiveFn = fn
  fn()
  activeReactiveFn = null
}

// 获取depend函数的封装
const targetMap = new WeakMap()
function getDepend(target, key) {
  let map = targetMap.get(target)
  if (!map) {
    map = new Map()
    targetMap.set(target, map)
  }

  let depend = map.get(key)
  if (!depend) {
    depend = new Depend()
    map.set(key, depend)
  }

  return depend
}

const obj = {
  name: 'xwl',
  age: 18
}

const objProxy = new Proxy(obj, {
  get(target, key, receiver) {
    const depend = getDepend(target, key)
    depend.addDepend(activeReactiveFn)
    return Reflect.get(target, key, receiver)
  },
  set(target, key, newValue, receiver) {
    Reflect.set(target, key, newValue, receiver)
    const depend = getDepend(target, key)
    depend.notify()
  }
})

// watchFn()封装,区分开和普通函数的区别
watchFn(function () {
  console.log('obj.name的值:' + objProxy.name)
})
watchFn(function () {
  console.log('obj.name2的值:' + objProxy.name)
})

watchFn(function () {
  console.log('obj.age的值:' + objProxy.age)
})

objProxy.name = 'xxx'
objProxy.age = 20
复制代码

Step 6: Refactor the Depend class

There are two modifications in this step:

  • depend.addDepend(activeReactiveFn) is changed to depend.depend(), and a global variable activeReactiveFn is defined to solve the transfer function problem;
  • Ordinary array [ ] is changed to new Set() to remove duplicates.
let activeReactiveFn = null

class Depend {
  constructor() {
    //
    this.reactiveFns = new Set()
  }

  // addDepend(fn) {
  //   this.reactiveFns.push(fn)
  // }

  depend() {
    if (activeReactiveFn) {
      this.reactiveFns.add(activeReactiveFn)
    }
  }

  notify() {
    this.reactiveFns.forEach(item => {
      item()
    })
  }
}

// 响应式函数的封装
function watchFn(fn) {
  activeReactiveFn = fn
  fn()
  activeReactiveFn = null
}

// 获取depend函数的封装
const targetMap = new WeakMap()
function getDepend(target, key) {
  let map = targetMap.get(target)
  if (!map) {
    map = new Map()
    targetMap.set(target, map)
  }

  let depend = map.get(key)
  if (!depend) {
    depend = new Depend()
    map.set(key, depend)
  }

  return depend
}

const obj = {
  name: 'xwl',
  age: 18
}

const objProxy = new Proxy(obj, {
  get(target, key, receiver) {
    const depend = getDepend(target, key)
    // depend.addDepend(activeReactiveFn)
    depend.depend()
    return Reflect.get(target, key, receiver)
  },
  set(target, key, newValue, receiver) {
    Reflect.set(target, key, newValue, receiver)
    const depend = getDepend(target, key)
    depend.notify()
  }
})

// watchFn()封装,区分开和普通函数的区别
watchFn(function () {
  // 这里两次使用objProxy.name,就会在数组中添加重复的响应式函数,所以上面用new Set()
  console.log('obj.name的值:' + objProxy.name)
  console.log('obj.name的值111:' + objProxy.name)
})
watchFn(function () {
  console.log('obj.name2的值:' + objProxy.name)
})

watchFn(function () {
  console.log('obj.age的值:' + objProxy.age)
})

objProxy.name = 'xxx'
objProxy.age = 20
复制代码

The seventh step (the last step): the responsive operation of the object vue3

let activeReactiveFn = null

class Depend {
  constructor() {
    //
    this.reactiveFns = new Set()
  }

  // addDepend(fn) {
  //   this.reactiveFns.push(fn)
  // }

  depend() {
    if (activeReactiveFn) {
      this.reactiveFns.add(activeReactiveFn)
    }
  }

  notify() {
    this.reactiveFns.forEach(item => {
      item()
    })
  }
}

// 响应式函数的封装
function watchFn(fn) {
  activeReactiveFn = fn
  fn()
  activeReactiveFn = null
}

// 获取depend函数的封装
const targetMap = new WeakMap()
function getDepend(target, key) {
  let map = targetMap.get(target)
  if (!map) {
    map = new Map()
    targetMap.set(target, map)
  }

  let depend = map.get(key)
  if (!depend) {
    depend = new Depend()
    map.set(key, depend)
  }

  return depend
}

// 第一种方式
// const obj = {
//   name: 'xwl',
//   age: 18
// }
// const objProxy = reactive(obj)

// 第二种方式:简写
const objProxy = reactive({
  name: 'xwl',
  age: 18
})

function reactive(obj) {
  return new Proxy(obj, {
    get(target, key, receiver) {
      const depend = getDepend(target, key)
      // depend.addDepend(activeReactiveFn)
      depend.depend()
      return Reflect.get(target, key, receiver)
    },
    set(target, key, newValue, receiver) {
      Reflect.set(target, key, newValue, receiver)
      const depend = getDepend(target, key)
      depend.notify()
    }
  })
}

// watchFn()封装,区分开和普通函数的区别
watchFn(function () {
  // 这里两次使用objProxy.name,就会在数组中添加重复的响应式函数,所以上面用new Set()
  console.log('obj.name的值:' + objProxy.name)
  console.log('obj.name的值111:' + objProxy.name)
})
watchFn(function () {
  console.log('obj.name2的值:' + objProxy.name)
})

watchFn(function () {
  console.log('obj.age的值:' + objProxy.age)
})

objProxy.name = 'xxx'
objProxy.age = 20

// ---------------------------------------------------
const infoProxy = reactive({
  address: 'gd'
})

watchFn(function () {
  console.log('-----info.address的值:' + infoProxy.address)
})

infoProxy.address = 'bj'
复制代码

Second, the realization of Vue2 responsive principle

The logic implemented by vue2 and vue3 is similar, vue2 uses Object.defineProperty(), and vue3 uses Proxy and Reflect.

let activeReactiveFn = null

class Depend {
  constructor() {
    //
    this.reactiveFns = new Set()
  }

  // addDepend(fn) {
  //   this.reactiveFns.push(fn)
  // }

  depend() {
    if (activeReactiveFn) {
      this.reactiveFns.add(activeReactiveFn)
    }
  }

  notify() {
    this.reactiveFns.forEach(item => {
      item()
    })
  }
}

// 响应式函数的封装
function watchFn(fn) {
  activeReactiveFn = fn
  fn()
  activeReactiveFn = null
}

// 获取depend函数的封装
const targetMap = new WeakMap()
function getDepend(target, key) {
  let map = targetMap.get(target)
  if (!map) {
    map = new Map()
    targetMap.set(target, map)
  }

  let depend = map.get(key)
  if (!depend) {
    depend = new Depend()
    map.set(key, depend)
  }

  return depend
}

// 第一种方式
// const obj = {
//   name: 'xwl',
//   age: 18
// }
// const objProxy = reactive(obj)

// 第二种方式:简写
const objProxy = reactive({
  name: 'xwl',
  age: 18
})

function reactive(obj) {
  Object.keys(obj).forEach(key => {
    let value = obj[key]
    Object.defineProperty(obj, key, {
      get: function () {
        const depend = getDepend(obj, key)
        depend.depend()
        return value
      },
      set: function (newValue) {
        value = newValue
        const depend = getDepend(obj, key)
        depend.notify()
      }
    })
  })
  return obj
}

// watchFn()封装,区分开和普通函数的区别
watchFn(function () {
  // 这里两次使用objProxy.name,就会在数组中添加重复的响应式函数,所以上面用new Set()
  console.log('obj.name的值:' + objProxy.name)
  console.log('obj.name的值111:' + objProxy.name)
})
watchFn(function () {
  console.log('obj.name2的值:' + objProxy.name)
})

watchFn(function () {
  console.log('obj.age的值:' + objProxy.age)
})

objProxy.name = 'xxx'
objProxy.age = 20

// ---------------------------------------------------
const infoProxy = reactive({
  address: 'gd'
})

watchFn(function () {
  console.log('-----info.address的值:' + infoProxy.address)
})

infoProxy.address = 'bj'
复制代码

Guess you like

Origin juejin.im/post/7080456357811486751