雖然Vue實現了MVVM模型,將數據和表現進行了分離,我們只需要更新數據就能使DOM同步更新,但是某些情況下,還是需要獲取DOM元素進行操作(比如引入的某個庫要求傳入一個根dom元素作為根節點,或者寫一些自定義指令),本文主要介紹幾種在Vue中獲取DOM元素的方法。
使用DOM API直接找元素
<script>
...
mounted () {
let elm = this.$el.querySelector('#id')
}
</script>
這種方法足夠簡單直觀,Vue組件在patch階段結束時會把this.$el
賦值為掛載的根dom元素,我們可以直接使用$el
的querySelector, querySelectorAll
等方法獲取匹配的元素。
refs
<template>
<div ref="bar">{{ foo }}</div>
<MyAvatar ref="avatar" />
...
</template>
<script>
...
mounted () {
let foo = this.$refs['bar'] // 一個dom元素
let avatar = this.$refs['avatar'] // 一個組件實例對象
}
</script>
使用組件實例的$refs
即可拿到組件上ref
屬性對應的元素。
如果ref屬性加在一個組件上,那么拿到的是這個組件的實例,否則拿到的就是dom元素了。
值得注意的是包含v-for
循環模板指令的情況,其循環元素和子元素上ref
屬性對應的都是一個數組(就算動態生成ref,也是數組):
<template>
<div v-for="item in qlist" :key="item.id" ref="qitem">
<h3>{{ item.title }}</h3>
<p ref="pinitem">{{ item.desc }}</p>
<p :ref="'contact'+item.id">{{ item.contact }}</p>
</div>
...
</template>
<script>
...
data () {
return {
qlist: [
{ id: 10032, title: 'abc', desc: 'aadfdcc', contact: 123 },
{ id: 11031, title: 'def', desc: '--*--', contact: 856 },
{ id: 20332, title: 'ghi', desc: '?/>,<{]', contact: 900 }
]
}
},
mounted () {
let foo = this.$refs['qitem'] // 一個包含dom元素的數組
let ps = this.$refs['pinitem'] // p元素是v-for的子元素,同樣是一個數組
let contact1 = this.$refs['contact' + this.qlist[0].id] // 還是個數組
}
</script>
關於這個的原因,可以從Vue關於ref處理的部分代碼得到:
function registerRef (vnode, isRemoval) {
var key = vnode.data.ref;
if (!isDef(key)) { return }
var vm = vnode.context;
// vnode如果有componentInstance表明是一個組件vnode,它的componentInstance屬性是其真實的根元素vm
// vnode如果沒有componentInstance則不是組件vnode,是實際元素vnode,直接取其根元素
var ref = vnode.componentInstance || vnode.elm;
var refs = vm.$refs;
if (isRemoval) {
...
} else {
// refInFor是模板編譯階段生成的,它是一個布爾值,為true表明此vnode在v-for中
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;
}
}
}
使用自定義指令
Vue提供了自定義指令,官方文檔給出了如下的使用方法,其中el
就是dom元素的引用
Vue.directive('focus', {
// 當被綁定的元素插入到 DOM 中時……
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
// 在模板中
<template>
<input v-model="name" v-focus />
</template>
關於自定義指令,在一些組件庫和事件上報等場景下非常有用,以后再專門寫一篇文章討論一下吧。