从0到Vue3企业项目实战【06.$ref与$nextTick使用】

355 阅读2分钟

这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情

$ref入门

什么是$ref?

请问大家:我们在Vue中是如何获取真实Dom元素的呢?
我们知道,js最初是为了表单校验而设计的语言,而在不停的更新过程中,js能够做的事情也越来越多,现在js已经是一款非常强大的语言了,再各类程序中都能看到js的身影,我们现在不仅可以用js写校验,还可以用来操作Dom,做动画效果,还可以写算法,js中操作Dom,只需要获取到对应的Dom元素即可,如document.getElementById('app'),js中获取Dom元素的方法有很多种,而在Vue,要获取Dom元素我们使用$ref可以更便携的获取到Dom元素,也是vue为我们提供的语法糖之一

$ref的使用

两种获取Dom标签方式对比

  1. 为目标标签添加上 ref/id
 <p ref="myP" id="name">我叫张三</p>
  1. 通过 ref/id 属性,获取目标标签的Dom,在生命周期中打印出来(注意:这里ref获取直接通过this.$refs.自定义ref名称获取
mounted () {
    console.log(document.getElementById('name'));
    console.log(this.$refs.myP);
  }

完整代码为: refDemo.vue在App.vue中引用查看

<template>
  <div>
    <p ref="myP" id="name">我叫张三</p>
  </div>
</template>

<script>
export default {
  mounted () {
    console.log(document.getElementById('name'));
    console.log(this.$refs.myP);
  }
}
</script>

<style>
</style>

这里教大家一个快捷引入的方法,以后对于组件引入这块我就不再赘述了

动画.gif

image.png

我们通过案例可以看到,$ref获取Dom比原生获取方式更简洁,并且效果完全一样.

通过ref获取组件对象

假如我们给vue组件给上ref\id,分别获取组件对象,会不会出现什么区别呢?

话不多说直接开干看看

App.vue

<template>
  <div>
    <!-- <LifeCycle v-if="isShow"></LifeCycle>
    <button @click="isShow = !isShow">显隐组件</button>-->
    <ref-demo ref="refDemo" id="myRef"></ref-demo>
  </div>
</template>

<script>
import LifeCycle from "./components/lifeCycle.vue";
import RefDemo from './components/refDemo.vue';
export default {
  components: { LifeCycle, RefDemo },
  data () {
    return {
      isShow: true
    }
  },
  mounted () {
    console.log(this.$refs.refDemo);
    console.log(document.getElementById('myRef'));
  }
}
</script>

<style>
</style>

运行可以看到效果 image.png

此时就有了差距,用ref获取到的Dom节点更清晰,有点类似我们的虚拟Dom,这还没玩,接下来我们为子组件添加上方法与属性,然后尝试使用父组件获取到的组件对象,来使用组件内的方法

refDemo.vue组件

<template>
  <div>
    <p ref="myP" id="name">我叫{{name}}</p>
    <button @click="changeName">改名</button>
  </div>
</template>

<script>
export default {
  data () {
    return {
      name: '张三'
    }
  },
  mounted () {
    console.log(document.getElementById('name'));
    console.log(this.$refs.myP);
  },
  methods: {
    changeName () {
      this.name = '李四'
    }
  }
}
</script>

<style>
</style>
  • 吧名称抽成了一个name属性
  • 添加了一个修改名称的方法,将张三修改为李四

此时我们在父组件中调用方法,App.vue

 mounted () {
    this.$refs.refDemo.changeName()
  }

可以看到我们没有改名,名称直接发生了修改,可以证明的是我们可以直接使用ref获取到的组件对象直接调用组件内的方法 image.png

当我们使用document.getElementById获取的对象调用组件内的方法

 mounted () {
    document.getElementById('myRef').changeName
  }

image.png

需要注意的是我们提供js获取到的Dom对象内的changeName是其中一个属性,如果我们在后面加上()就会报错,需要类似于属性的方式调用组件内的方法,所以ref不仅仅可以用来获取Dom,同时在获取组件对象使用组件内方法也有额外的处理所以推荐在Vue中关于获取元素尽量的使用ref
ref有如下优势

  • 获取更便携简洁
  • 调用组件内方法更语义化
  • 获取到的节点信息更清晰

$nextTick入门

为什么要学习$nextTick?

还是上面的案例,当我在changeName方法中立即获取当前名称,获取到的会是修改后的名称吗?
refDemo.vue组件

changeName () {
      this.name = '李四' //数据更新
      console.log(this.$refs.myP.innerHTML);
    }

image.png

可以发现,我们打印出来的名称依旧是张三,而非是李四,这到底是为什么呢?
因为,vue更新Dom是异步的
我们知道,js是单线程的,同步任务会在主线程从上往下执行,而异步任务会放在异步任务队列中,当主线程执行栈中的同步任务执行完毕,线程空闲了就会去执行任务队列中的异步任务
所以,更新操作并不会阻塞后面的获取值的操作,所以取到了更新前的值
我们要怎么获取到更新后的值呢? -> $nextTick

$nextTick的使用

  • 语法 this.$nextTick(函数体)
this.$nextTick(() => {
        console.log(this.$refs.myP.innerHTML);
      })

使用箭头函数更简洁,可以看到结果就是修改后的值了

image.png

大白话的解释nextTick就是,你放在nextTick内的函数不会立即执行,Vue会尝试$nextTick内的回调函数使用原生的Promise.thenMutationObserversetImmediate等将回调函数挂载在异步任务队列中,如果执行环境不支持,那么将会采用setTimeout(fn, 0)来替代,即:nextTick内的函数会在修改完Dom后进行操作

$nextTick的实践

我们来实现一个案例,让我们对于nextTick掌握的更深
需求:

  • 提供一个按钮点击按钮变为输入框,同时输入框聚焦

这个案例我们需要用到nextTick,那么控制显影就需要用到v-if不然不会重新创建Dom那么nextTick在案例中也没有必要使用\

创建一个vue组件tickDemo.vue

<template>
  <div @click="hide">
    <h1>tick实例</h1>
    <input ref="input" type="text" placeholder="请输入搜索关键字" v-if="isShow" />
    <button v-else @click.stop="show">搜索</button>
  </div>
</template>

<script>
// 点按钮 消失输入框出现并聚焦
export default {
  data () {
    return {
      isShow: false
    }
  },
  methods: {
    show () {
      this.isShow = true
      this.$nextTick(() => {
        this.$refs.input.focus()
      })
    },
    hide () {
      this.isShow = false
    }
  }
}
</script>

<style>
</style>

注意 :

  • 按钮与input控制显影字段相反,实现了两者只显示一个的效果
  • 按钮事件添加了.stop修饰符防止事件冒泡
  • 最大的div上添加了hide事件,这样可以让搜索框变回按钮
  • 聚焦事件需要等待Dom创建后再进行,如果此处不用nextTick获取到的Dom会是空也就没有focus方法

看看效果

动画.gif

来看看我的其他章节吧,正在长更中

从0到Vue3企业项目实战【01.Vue的基本概念与学习指南】 - 掘金 (juejin.cn)

从0到Vue3企业项目实战【02.了解并理解Vue指令以及虚拟Dom】 - 掘金 (juejin.cn)

从0到Vue3企业项目实战【03.vue基本api入门】 - 掘金 (juejin.cn)

从0到Vue3企业项目实战【04.从vue组件通讯到eventBus以及vuex(附mock接口与axios简单实践)】 - 掘金 (juejin.cn)

从0到Vue3企业项目实战【05.vue生命周期】 - 掘金 (juejin.cn)

从0到Vue3企业项目实战【06.refref与nextTick使用】 - 掘金 (juejin.cn)

从0到Vue3企业项目实战【07.vue动态组件,组件缓存,组件插槽,子组件直接修改props,自定义指令看这一篇就够了】 - 掘金 (juejin.cn)

从0到Vue3企业项目实战【08.Vue路由基础】 - 掘金 (juejin.cn)

从0到Vue3企业项目实战【09.Vue路由进阶】 - 掘金 (juejin.cn)