当前位置: 移动技术网 > IT编程>网页制作>Html5 > Vue.js 源码分析(十) ref属性详解

Vue.js 源码分析(十) ref属性详解

2019年06月24日  | 移动技术网IT编程  | 我要评论

用法


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

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>document</title>
    <script src="vue.js"></script>
</head>
<body>
    <div id="app">
        <h1 ref="info">11</h1>
        <child ref="child"></child>
        <p v-for="item in items" ref="item">{{item}}</p>
        <button @click='show'>test</button>
    </div>
    <script>
        vue.component('child',{template:'<h1>i am childcomponent</h1>'})
        debugger
        var app = new vue({
            el:'#app',
            data:{items:[11,12,13]},
            methods:{show:function(){console.log(this.$refs)}}              //点击后输出vue实例的$refs属性
        })
    </script>   

    
</body>
</html>

渲染如下:

点击test后输出如下:

 

源码分析


 _init初始化的时候会执行initlifecycle()函数,该函数会初始化当前vue实例的$refs为一个空对象。

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

function processref (el) {          //第9359行 解析ref属性
  var ref = getbindingattr(el, 'ref');  //尝试获取ref属性
  if (ref) {                            //如果存在
    el.ref = ref;                           //保存到el.ref里面
    el.refinfor = checkinfor(el);           //执行checkinfor检查是否在v-for循环内,将结果保存到el.refinfor里面
  }
}
function checkinfor (el) {        //第9605行 检测ref属性是否在v-for里面
  var parent = el;                  //首先将el保存到parent里,这样v-for和ref就可以作用在同一个元素上
  while (parent) {                  //通过检测parent的ast对象是否由for来判断
    if (parent.for !== undefined) {
      return true                     //如果在v-for内则返回true
    }
    parent = parent.parent;
  }
  return false                        //否则返回false
}

检测是否在v-for内会影响最后的保存方式,如果在v-for内则最后保存为数组形式,例如例子里的p标签,否则就是非数组,对于h1属性来说,执行到这里后,属性如下:

最后将ast生成render函数的时候会执行gendata$2()函数($2是vue项目build的时候node自动转换的,防止同名),gendata$2()会判断是否有ref和refinfor属性,如果有则保存到data属性上(就是render属性对应的参数,这个参数是一个函数,函数的第二个参数),如下:

function gendata$2 (el, state) {  //第10274行 
  var data = '{';

  // directives first.
  // directives may mutate the el's other properties before they are generated.
  var dirs = gendirectives(el, state);
  if (dirs) { data += dirs + ','; }

  // key
  if (el.key) { 
    data += "key:" + (el.key) + ",";
  }
  // ref
  if (el.ref) {                           //对应ref属性
    data += "ref:" + (el.ref) + ",";      
  }
  if (el.refinfor) {                      //如果组件元素有设置了v-for指令
    data += "refinfor:true,";
  }
  /*略*/
}

最后等到dom创建后,会执行ref模块的create钩子函数(vue内部有七个模块,分别对应属性、样式、事件、dom属性、样式、动画、ref和指令,用于在dom新增、更新、卸载时执行一些列操作)

ref模块初始化时会执行registerref函数,如下:

function registerref (vnode, isremoval) {     //第5389行 ref的实现函数 vnode:节点对应的vnode,isremoval:是否移除
  var key = vnode.data.ref;
  if (!isdef(key)) { return }                     //如果没有定义ref属性,则直接返回

  var vm = vnode.context;                             //当前的根vue实例
  var ref = vnode.componentinstance || vnode.elm;    //优先获取vonde的组件实例(对于组件来说),或者el(该vnode对应的dom节点,非组件来说)
  var 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) {                          //当在v-for之内时,则保存为数组形式
      if (!array.isarray(refs[key])) {
        refs[key] = [ref];
      } else if (refs[key].indexof(ref) < 0) {
        // $flow-disable-line
        refs[key].push(ref);
      }
    } else {                                            //不是在v-for之内时 
      refs[key] = ref;                                      //直接保存到refs对应的key属性上
    }
  }
}

ref属性比较简单的,可以方便的引用某个dom节点或子组件实例,在很多地方用得到,比如elementui里的表单等

 

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网