当前位置 : 主页 > 网络编程 > JavaScript >

详解Vue3的响应式原理解析

来源:互联网 收集:自由互联 发布时间:2022-05-11
目录 Vue2响应式原理回顾 Vue3响应式原理剖析 嵌套对象响应式 避免重复代理 总结 Vue2响应式原理回顾 // 1.对象响应化:遍历每个key,定义getter、setter// 2.数组响应化:覆盖数组原型方法
目录
  • Vue2响应式原理回顾
  • Vue3响应式原理剖析
    • 嵌套对象响应式
    • 避免重复代理
  • 总结

    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)
    

    总结

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

    【本文转自:美国cn2站群服务器 http://www.558idc.com/mggfzq.htm提供,感谢支持】
    网友评论