加入收藏 | 设为首页 | 会员中心 | 我要投稿 温州站长网 (https://www.0577zz.com/)- 低代码、办公协同、物联平台、操作系统、5G!
当前位置: 首页 > 站长学院 > PHP教程 > 正文

vue处理响应式数据的方法指的是什么

发布时间:2023-07-21 10:31:58 所属栏目:PHP教程 来源:互联网
导读:   这篇“vue处理响应式数据的方法是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章
  这篇“vue处理响应式数据的方法是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“vue处理响应式数据的方法是什么”文章吧。
 
  那继续下一个问题。
 
  这个回答感觉就比较官方了,没有回答到我想要的答案。
 
  理由其实很简单,因为proxy方法不能代理值类型,只能代理对象。所以需要一个额外的方法,来处理值类型的数据。
 
  当然你也可以一个ref走天下,vue源码做了兼容会自动进行转换
 
  有了这些理解之后,我们来看今天的重头戏,自己仿照vue3源码,实现一个响应式系统。
 
  相关代码可以点击查看,根据文章的标题选择不同的分支即可。
 
  实现reactive
 
  在前文我们也介绍了,reactive其实用的是proxy代理对象。
 
  我们可以使用proxy实现一个简单的代理函数reactive。
 
        function reactive(target) {        const isObject = (val) =>  val !== null && typeof val === 'object'
 
          if (!isObject(target)) {          console.warn(`数据必须是对象: ${String(target)}`)          return target
 
          }        const proxy = new Proxy(target, {          get: (target, key, receiver) => {            console.log('get', key)            const res = Reflect.get(target, key, receiver)            // track(target, key)
 
              // 这句很关键,不然嵌套数据,里面的不会响应
 
              if (isObject(res)) {              return reactive(res)
 
              }            return res
 
            },          set: (target, key, value, receiver) => {            console.log('set', key, value)            const result = Reflect.set(target, key, value, receiver)            // trigger(target, key, value)
 
              return result
 
            },          deleteProperty: () => {            const result = Reflect.deleteProperty(target, key)            return result
 
            }
 
          })        return proxy
 
        }      const person = reactive({        name: '',        age: 18,        like: ['apple']
 
        })
 
        person.name  = 'vue test'复制代码
 
  注意:Reflect
 
  Reflect.get(target, key)跟直接访问target[key]是有一点差别的。
 
  当代理对象中,有get,set等this指向的时候,可以重定向this。
 
  举个例子:
 
          const person = new Proxy({            name: "vue test",            age: 18,            get info() {                return 'name is :' + this.name + ' age is:' + this.age
 
              }
 
          }, {            get: (target, key, receiver) => {                console.log('get', key)                // return target[key]
 
                  return Reflect.get(target, key, receiver)
 
              }
 
          })        console.log(person.info)复制代码
 
  使用Reflect,我们在访问name, age的时候均可以触发。vue处理响应式数据的方法是什么
 
  改成target之后,则只会在info触发一次。
 
  return target[key]复制代码
 
  vue处理响应式数据的方法是什么
 
  实现ref
 
  在vue3中,ref是通过get和set来实现的。
 
  跟上面类似,也还是先声明一个函数,然后通过get,set来访问数据。
 
        function ref(value) {        return new RefImpl(value)
 
        }      class RefImpl {        constructor(value) {          // 如果值是对象,则用reactive处理
 
            this._value = isObject(value) ? reactive(value) : value          // 记录一下初始值
 
            this._rawValue = value
 
          }        get value() {          // trackRefValue(this)
 
            return this._value
 
          }        set value(newVal) {          if (!Object.is(newVal, this._rawValue)) {            // 更新原始数据
 
              this._rawValue = newVal            // 更新 .value 的值
 
              this._value = isObject(newVal) ? reactive(newVal) : value            // triggerRefValue(this)
 
            }
 
          }
 
        }复制代码
 
  源码也很直观的解释了,为什么ref的使用一定要用.value的方式使用,因为get/set就是通过value来设置的。
 
  添加依赖收集与触发
 
  数据代理我们已经完成了,但是数据更改之后,怎么通知组件实现双向绑定呢?
 
  答案就是依赖收集与触发,也就是当触发get的时候,我把触发get的这个条件【函数】给保存起来,当触发set的时候,重新执行一下触发一下这个条件【函数】不就行了吗。
 
  还是来看代码,我加一个track收集的方法,加一个target触发的方法。(也就是上面代码片段注释掉的两行代码)
 
  除此之外,还需要一个effect函数来管理触发函数。
 
        let activeEffect = null
 
        const targetMap = new WeakMap()      // 依赖收集/触发
 
        function track(target, key) {        let depsMap = targetMap.get(target)        if (!depsMap) {
 
            targetMap.set(target, (depsMap = new Map()))
 
          }        let dep = depsMap.get(key)        if (!dep) {
 
            dep = new Set()
 
          }
 
          dep.add(activeEffect)
 
          depsMap.set(key, dep)
 
        }      function trigger(target, key, value) {        const depsMap = targetMap.get(target)        if (!depsMap) {          return
 
          }        const deps = depsMap.get(key)        if (!deps) {          return
 
          }
 
          deps.forEach(effectFn => {          if (effectFn.scheduler) {
 
              effectFn.scheduler()
 
            } else {            effectFn()
 
            }
 
          })
 
        }      function effect(fn,options = {}) {        const effectFn = () => {          try {
 
              activeEffect = effectFn            return fn()
 
            } catch (error) {
 
              activeEffect = null
 
            }
 
          }        if (!options.lazy) {          effectFn()
 
          }
 
          effectFn.scheduler = options.scheduler
 
          return effectFn
 
        }      const person = reactive({        name: "hello world"
 
        })      effect(() => {        console.log('effect person', person.name)
 
        })      setTimeout(() => {
 
          person.name = 'setTimeout world'
 
        }, 2000)复制代码
 
  activeEffect用来存触发的条件函数。
 
  targetMap用来存放依赖字典,格式如下
 
  {
 
      target: {
 
          key: []
 
      }
 
  }复制代码
 
  输出结果为hello world,2秒之后,重新执行依赖性函数,输出setTimeout world
 
  总结
 
  数据的代理并不复杂,就是在proxy的基础上,加上一些边界处理。而要实现响应式则需要加上依赖收集,依赖触发的实现。
 
  effect是一个很重要的函数,很多api是基于这个函数开发的,比如useEffect, watch。组件的更新也是基于effect函数,这个后续还会提到。
 
  如果不太理解effect,可以梳理一下执行顺序。
 
  1.调用effect函数,并传入参数fn
 
  2.申明一个effectFn函数,并执行,将函数保存为activeEffect
 
  3.执行fn,读取person.name
 
  4.走proxy的get代理
 
  5.收集依赖,将activeEffect保存的函数存到全局map中
 
  6.此时,effect函数执行完毕,等待数据更新
 
  7.2s后,走proxy的set代理
 
  8.执行全局map中保存的函数,重新执行effect函数,再次回到步骤1
 
  相关代码可以点击查看,选择lesson3分支即可。
 
  文章相关代码,可在vue/examples中查看,仿vue实现版本可在packages/reactivity中查看。
 

(编辑:温州站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章