ref 属性

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

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

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

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

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

例如:

<template>
<div>
  <h1 v-text="msg" ref="title"></h1>
  <button @click="showDom">输出上方DOM</button>
  <hr>
  <MySite ref="site"/>
</div>
</template>

<script>
// 引入组件
import MySite from "./components/MySite.vue";

export default {
  name: 'app',
  components: {
    MySite,
  },
  data() {
    return {
      msg: 'Hello Vue.js!',
    }
  },
  methods: {
    showDom() {
      console.log(this.$refs);        // 获取模板中所有使用了 ref 属性的元素或组件
      console.log(this.$refs.title);  // 获取真实DOM元素
      console.log(this.$refs.site);   // 获取组件实例对象
    },
  },
}
</script>

nextTick 延迟执行

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

<template>
<div>
  <span v-show="!isEdit">{{name}}</span>
  <input 
    v-show="isEdit" 
    type="text"
    v-model.lazy="name"
    ref="input"
    @keyup.enter="handleBlur"
    @blur="handleBlur"
  />
  <br><br>
  <button v-show="!isEdit" @click="isEdit = true">
    编辑
  </button>
</div>
</template>

<script>
export default {
  name: 'app',
  data() {
    return {
      name: "张三",
      isEdit: false,
    }
  },
  methods: {
    handleBlur() {
      this.isEdit = false
      this.$ref.input.focus()
    }
  },
}
</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()

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

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

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

handleBlur() {
  this.isEdit = false
  setTimeout(() => {
      this.$refs.input.focus()
  })
}

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