欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

详解Vue3的响应式原理解析

程序员文章站 2022-06-17 16:02:39
目录vue2响应式原理回顾vue3响应式原理剖析嵌套对象响应式避免重复代理总结vue2响应式原理回顾// 1.对象响应化:遍历每个key,定义getter、setter// 2.数组响应化:覆盖数组原...

vue2响应式原理回顾

// 1.对象响应化:遍历每个key,定义getter、setter
// 2.数组响应化:覆盖数组原型方法,额外增加通知逻辑
const originalproto = array.prototype
const arrayproto = object.create(originalproto)
  ;['push', 'pop', 'shift', 'unshift', 'splice', 'reverse', 'sort'].foreach(
    method => {
      arrayproto[method] = function () {
        originalproto[method].apply(this, arguments)
        notifyupdate()
      }
    }
  )
function observe (obj) {
  if (typeof obj !== 'object' || obj == null) {
    return
  }
  // 增加数组类型判断,若是数组则覆盖其原型
  if (array.isarray(obj)) {
    object.setprototypeof(obj, arrayproto)
  } else {
    const keys = object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      const key = keys[i]
      definereactive(obj, key, obj[key])
    }
  }
}
function definereactive (obj, key, val) {
  observe(val) // 解决嵌套对象问题
  object.defineproperty(obj, key, {
    get () {
      return val
    },
    set (newval) {
      if (newval !== val) {
        observe(newval) // 新值是对象的情况
        val = newval
        notifyupdate()
      }
    }
  })
}
function notifyupdate () {
  console.log('页面更新!')
}

vue2响应式弊端:
响应化过程需要递归遍历,消耗较大
新加或删除属性无法监听
数组响应化需要额外实现
map、set、class等无法响应式
修改语法有限制

vue3响应式原理剖析

vue3使用es6的proxy特性来解决这些问题。

function reactive (obj) {
  if (typeof obj !== 'object' && obj != null) {
    return obj
  }
  // proxy相当于在对象外层加拦截
  // http://es6.ruanyifeng.com/#docs/proxy
  const observed = new proxy(obj, {
    get (target, key, receiver) {
      // reflect用于执行对象默认操作,更规范、更友好
      // proxy和object的方法reflect都有对应
      // http://es6.ruanyifeng.com/#docs/reflect
      const res = reflect.get(target, key, receiver)
      console.log(`获取${key}:${res}`)
      return res
    },
    set (target, key, value, receiver) {
      const res = reflect.set(target, key, value, receiver)
      console.log(`设置${key}:${value}`)
      return res
    },
    deleteproperty (target, key) {
      const res = reflect.deleteproperty(target, key)
      console.log(`删除${key}:${res}`)
      return res
    }
  })
  return observed
}
//代码测试
const state = reactive({
  foo: 'foo',
  bar: { a: 1 }
})
// 1.获取
state.foo // ok
// 2.设置已存在属性
state.foo = 'fooooooo' // ok
// 3.设置不存在属性
state.dong = 'dong' // ok
// 4.删除属性
delete state.dong // ok

嵌套对象响应式

测试:嵌套对象不能响应

// 设置嵌套对象属性
react.bar.a = 10 // no ok

添加对象类型递归

      // 提取帮助方法
      const isobject = val => val !== null && typeof val === 'object'
      function reactive (obj) {
        //判断是否对象
        if (!isobject(obj)) {
          return obj
        }
        const observed = new proxy(obj, {
          get (target, key, receiver) {
            // ...
            // 如果是对象需要递归
            return isobject(res) ? reactive(res) : res
          },
          //...
        }

避免重复代理

重复代理,比如

reactive(data) // 已代理过的纯对象
reactive(react) // 代理对象

解决方式:将之前代理结果缓存,get时直接使用

const toproxy = new weakmap() // 形如obj:observed
      const toraw = new weakmap() // 形如observed:obj
      function reactive (obj) {
        //...
        // 查找缓存,避免重复代理
        if (toproxy.has(obj)) {
          return toproxy.get(obj)
        }
        if (toraw.has(obj)) {
          return obj
        }
        const observed = new proxy(...)
        // 缓存代理结果
        toproxy.set(obj, observed)
        toraw.set(observed, obj)
        return observed
      }
      // 测试效果
      console.log(reactive(data) === state)
      console.log(reactive(state) === state)

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注的更多内容!