• Vue provide/inject 部分源码分析 实现响应式数据更新


     

     

    provide/inject 数据响应式更新的坑及源码解析

     

    下面是我自己曾经遇到 一个问题,直接以自己QA的形式来写吧

    自问自答了,需要的同学也可以直接访问segmentfault地址

    官网给出实例,说本身是不支持数据响应式的, 但是可以传入响应式数据,那么provide,inject就可以实现响应式。
    我这里理解应该没错哈,有不对的地方请指出。

    我自己写的demo,做了如下更改

    parent 页面:

      export default {
            provide(){
             return   {foo:this.fonnB}
            },
            data(){
              return {fonnB:'old word'}   
            }
             created() {
              setTimeout(()=>{
                this.fonnB="new words";    // 这种跟新,仅仅foonB变化了,foo没有变化
                this._provided.foo="new words"; 
                //这种更新 foo变化了,但子组件获得的foo  依旧是old words
                console.log( this._provided)
              },1000)
    
            },
            
          }

    child页面:

     
         export default {
            inject:['foo'],
            data(){
              return {chilrfoo:this.foo}   
            }    
          }
          
         通过上面2个方法,经过验证,子组件页面都没办法实现响应更新this.foo的值。
         求解释,谢谢


    以上是我自己的问题, 下面是我基本理解后,在自己回答的问题

    现做了如下修改,可以达到父亲组件改变,下面的孙子组件都能更新数据.这样就是传入了一个响应式数据,如果需要双向数据的话,需要在child页面的computed 中手动写set 函数,computed 本身就只相当于一个get函数。

    值得注意是:child页面data 数据中childfooOld并不会响应。如果这里childfooOld=this.foo ,obj的形式也是可以响应的,那么a也是响应式数据。
    如果是单数据格式不能响应,childfooOld下没有set/get 只是在data下的set/get 是控制data下属性变化时触发的,而不是this.foo.a 触发的;

    parent页面:
    export default {

        provide(){
         return   {foo:this.fonnB}
        },
        data(){
          return {
          fonnB:{a:'old word'}
          }   
        }
         created() {
          setTimeout(()=>{
            this.fonnB.a="new words";    
          
            //这种更新 foo变化了,但子组件获得的foo  依旧是old words
           
          },1000)
    
        },
        
      }

    child页面:

     export default {
        inject:['foo'],
        data(){
          return {
           childfooOld:this.foo.a
          }   
        },
        computed:{
            chilrfoo(){
                return  this.foo.a
            }
        }    
      }
    

    关于prodive 和inject 源码部分如下
    export function initInjections (vm: Component) {
    const result = resolveInject(vm.$options.inject, vm)
    if (result) {

    observerState.shouldConvert = false
    Object.keys(result).forEach(key => {
      defineReactive(vm, key, result[key])
    })
    observerState.shouldConvert = true

    }
    }
    可以看出 prodive 也运用了defineReactive 函数,增加了自身的set,get函数,也是响应式数据,如下图

        

    如下 是inject 源码,我没看出来那里明确增加了set/get,但是打印出来结果inject 也是有set/get的

    export function resolveInject (inject: any, vm: Component): ?Object {
    if (inject) {

    // inject 是 :any 类型因为流没有智能到能够指出缓存
    const result = Object.create(null)
    // 获取 inject 选项的 key 数组
    const keys = hasSymbol
      ? Reflect.ownKeys(inject).filter(key => {
        /* istanbul ignore next */
        return Object.getOwnPropertyDescriptor(inject, key).enumerable
      })
      : Object.keys(inject)
    
    for (let i = 0; i < keys.length; i++) {
      const key = keys[i]
      const provideKey = inject[key].from
      let source = vm
      while (source) {
        if (source._provided && provideKey in source._provided) {
          result[key] = source._provided[provideKey]
          break
        }
        source = source.$parent
      }
      if (!source) {
        if ('default' in inject[key]) {
          const provideDefault = inject[key].default
          result[key] = typeof provideDefault === 'function'
            ? provideDefault.call(vm)
            : provideDefault
        } else if (process.env.NODE_ENV !== 'production') {
          warn(`Injection "${key}" not found`, vm)
        }
      }
    }
    return result

    }
    }

     

    通过computed 就实现了上下传值
    当然可以直接 通过绑定data属性,但是不能是单数据绑定,一定是地址类传值

     

     



  • 相关阅读:
    两人合作
    JUnit单元测试
    结对编程-——游戏五子棋
    使用Junit等工具进行单元测试
    软件工程
    两人项目---打飞机的游戏
    使用Junit等工具进行单元测试
    软件工程
    使用Junit等工具进行单元测试
    软件工程
  • 原文地址:https://www.cnblogs.com/leolovexx/p/9714088.html
Copyright © 2020-2023  润新知