Vue.js 源码分析 ref属性详解
Posted greatdesert
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue.js 源码分析 ref属性详解相关的知识,希望对你有一定的参考价值。
用法
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里的表单等
以上是关于Vue.js 源码分析 ref属性详解的主要内容,如果未能解决你的问题,请参考以下文章
Vue.js 源码分析(二十九) 高级应用 transition-group组件 详解
Vue.js 源码分析(二十) 指令篇 v-once指令详解
Vue.js 源码分析(二十六) 高级应用 作用域插槽 详解