ref 属性

在Vue的模板中可以通过为元素或子组件添加ref属性(如ref="elementRefValue")来告诉Vue实例(或组件实例)要获取该元素。例如:

1<h1 v-text="msg" ref="title"></h1>  <!-- 获取Html内置标签元素 -->
2<MySite ref="site"/>  <!-- 获取Vue组件 -->

在Vue中,ref属性被用来给元素或子组件注册引用信息

然后在Vue实例或组件实例中,通过this.$refs.elementRefValue来获取该元素或子组件的对象。例如:

1this.$refs.title  // 获取真实DOM元素
2this.$refs.site   // 获取组件实例对象
  • 如果ref在Html内置标签上使用,那么this.$refs.elementRefValue获取到的是该元素的真实DOM元素。
  • 如果ref在Vue组件标签上使用,那么this.$refs.elementRefValue获取到的是该组件的VueComponent对象。

例如:

 1<template>
 2<div>
 3  <h1 v-text="msg" ref="title"></h1>
 4  <button @click="showDom">输出上方DOM</button>
 5  <hr>
 6  <MySite ref="site"/>
 7</div>
 8</template>
 9
10<script>
11// 引入组件
12import MySite from "./components/MySite.vue";
13
14export default {
15  name: 'app',
16  components: {
17    MySite,
18  },
19  data() {
20    return {
21      msg: 'Hello Vue.js!',
22    }
23  },
24  methods: {
25    showDom() {
26      console.log(this.$refs);        // 获取模板中所有使用了 ref 属性的元素或组件
27      console.log(this.$refs.title);  // 获取真实DOM元素
28      console.log(this.$refs.site);   // 获取组件实例对象
29    },
30  },
31}
32</script>

nextTick 延迟执行

假设有如下组件,需要在点击编辑按钮时显示输入框并且自动获取焦点:

 1<template>
 2<div>
 3  <span v-show="!isEdit">{{name}}</span>
 4  <input 
 5    v-show="isEdit" 
 6    type="text"
 7    v-model.lazy="name"
 8    ref="input"
 9    @keyup.enter="handleBlur"
10    @blur="handleBlur"
11  />
12  <br><br>
13  <button v-show="!isEdit" @click="isEdit = true">
14    编辑
15  </button>
16</div>
17</template>
18
19<script>
20export default {
21  name: 'app',
22  data() {
23    return {
24      name: "张三",
25      isEdit: false,
26    }
27  },
28  methods: {
29    handleBlur() {
30      this.isEdit = false
31      this.$ref.input.focus()
32    }
33  },
34}
35</script>

但是运行后发现,<input>并不能自动获取焦点。

产生这个问题的原因是,this.$ref.input.focus()执行的时机在组件DOM更新完成之前。即,点击编辑按钮之后,组件调用this.handleBlur(),等到this.handleBlur()执行完成时,组件才开始重新解析页面。

想要让this.$ref.input.focus()在下次DOM更新完成之后才执行,可以使用Vue提供的this.$nextTick(callback)this指Vue或组件实例)。

this.$nextTick(callback)需要传入一个回调。当执行到this.$nextTick(callback)时,Vue并不会立即执行回调,而是等到数据更新完毕,且DOM更新完成之后才执行callback

callback使用function定义时,callback中的this指向的是当前Vue或组件实例。

也就是说,this.$nextTick(callback)可以让callback下一次DOM更新结束之后执行

例如,修改上方案例中的handleBlur()

1handleBlur() {
2  this.isEdit = false
3  // 模板重新解析完成后才自动获取焦点
4  // $nextTick() 指定的回调,会在下次DOM更新完成之后才执行
5  this.$nextTick(() => {
6      this.$refs.input.focus()
7  })
8}

nextTick的使用场景:当改变数据后,要基于更新之后的新DOM进行某些操作时。

其实还有一种解决方法,就是使用一个不带时间、立即执行的定时器:

1handleBlur() {
2  this.isEdit = false
3  setTimeout(() => {
4      this.$refs.input.focus()
5  })
6}

这是因为setTimeout()即使不带时间,它也会将回调推向一个队列去执行,所以本质上来说并不算立即执行。