Vue2.x 源码 - ref 和 $refs

上一篇:Vue2.x 源码 - v-model

ref 被用来给元素或子组件注册引用信息,引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例。

$refs 的注册

在函数 initLifecycle 上会往 vm 上设置一个 key$refs 值为一个对象,在src/core/instance/lifecycle.js 文件中:

function initLifecycle (vm) {
  ....
  vm.$refs = {};
  ....
}

解析

挂载的时候首先会将模板解析成一个AST对象,此时会执行processElement函数,该函数又会执行processRef去解析ref属性,如下:

function processRef (el) {  
  var ref = getBindingAttr(el, 'ref');  
  if (ref) {   
    //保存到el.ref里面                        
    el.ref = ref;  
    //执行checkInFor检查是否在v-for循环内,将结果保存到el.refInfor里面                         
    el.refInFor = checkInFor(el);           
  }
}
//检测ref属性是否在v-for里面
function checkInFor (el) {  
  //首先将el保存到parent里,这样v-for和ref就可以作用在同一个元素上     
  var parent = el; 
  //通过检测parent的AST对象是否由for来判断                 
  while (parent) {  
    //如果在v-for内则返回true                
    if (parent.for !== undefined) {
      return true                     
    }
    parent = parent.parent;
  }
  return false                        
}

最后将AST生成render函数的时候会执行genData,会判断是否有ref和refInFor属性,如果有则保存到data属性上;

function genData (el, state) {   
  var data = '{';
  var dirs = genDirectives(el, state);
  if (dirs) { data += dirs + ','; }
  // key
  if (el.key) { 
    data += "key:" + (el.key) + ",";
  }
  // ref
  if (el.ref) {                          
    data += "ref:" + (el.ref) + ",";      
  }
  if (el.refInFor) {                     
    data += "refInFor:true,";
  }
  ......
}

获取元素上的 ref

ref 对象中,内置函数 registerRef 是核心方法,在src/core/vdom/modules/ref.js 文件中:

export function registerRef (vnode: VNodeWithData, isRemoval: ?boolean) {
  //获取ref
  const key = vnode.data.ref
  if (!isDef(key)) return
  const vm = vnode.context
  const ref = vnode.componentInstance || vnode.elm
  const refs = vm.$refs
  if (isRemoval) {
    if (Array.isArray(refs[key])) {
      remove(refs[key], ref)
    } else if (refs[key] === ref) {
      refs[key] = undefined
    }
  } else {
    if (vnode.data.refInFor) {
      if (!Array.isArray(refs[key])) {
        refs[key] = [ref]
      } else if (refs[key].indexOf(ref) < 0) {
        // $flow-disable-line
        refs[key].push(ref)
      }
    } else {
      refs[key] = ref
    }
  }
}

1、首先通过 vnode.data 获取 ref 作为 key ,这个 key 是用来为获取到的 refs 示例作为键名,然后从 vnode 上下文 context 获取 vm ,获取 $refs;ref 其实是 DOM 节点或组件实例;
2、isRemoval 为 true 是更新和销毁钩子触发的时候,首先看一下 else 里面的逻辑:ref 和 v-for 一起使用的时候,如果不是数组格式,强制转换一下,外层套一个数组;如果是数组看数组里面是否存在当前这个 ref,如果不存在,push 进去;
3、如果不是和 v-for 一起用:直接设置对象的 key 和 value;
4、如果是更新和销毁钩子触发的时候:会对 key 对应的 refs 删除;

总结

1、ref 本身是作为渲染结果被创建的,在初始渲染的时候你不能访问它们 ,因为它们还不存在;
2、ref 相对来说比较简单,它内置了 create 、update、destory 生命周期钩子方法,会在不同的钩子函数触发的时候自动触发;在 patch 过程中会在invokeCreateHooks 函数中调用 create ,这也就会触发 ref 内置的 create 钩子函数,然后就会调用 registerRef 方法来注册 ref
3、在 update、destory 会把对应的 refs 对象删除掉;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值