ref引用DOM元素和组件实例

目标:
能够使用ref获取页面上DOM或组件的引用:
给元素或组件添加ref="xxx"的引用名称,通过this.$refs.xxx获取元素或组件的实例

能够知道$nextTick的应用场景并合理地使用:把回调函数里面的代码推迟到当DOM重新渲染完毕之后执行

通过“购物车案例”巩固前4天所有学知识

ref引用 - 引用DOM元素 / 组件实例

Vue的优势:MVVM的数据驱动视图 – 在vue中,程序员不需要操作DOM,只需要把数据维护好即可。
因此,在vue项目中,墙裂不建议安装和使用jQuery,因为没有操作DOM的需求,如果有也是极少的。

  1. 什么是ref 引用
    ref用来辅助开发者在不依赖于jQuery的情况下在不调用DOM API的前提下获取页面上DOM元素或组件的引用

    每个vue的组件实例上,都包含一个$refs对象,里面存储着对应的DOM元素或组件的引用。默认情况下,组件的$refs 指向一个空对象

    打印出vue实例对象this,可以看到,凡是$开头的,都是vue内置的一些成员。

  2. 使用ref引用 DOM元素
    在标签上通过ref属性来指定一个名字,这个名字就指向这个标签,并添加到$ref对象中。如:

    1
    <h1 ref="myh1">APP根组件</h1>
    1
    2
    3
    4
    $refs:{//vue实例上的$refs属性值
    myh1: h1
    }
    this.$refs.myh1.style.color = 'red'//操作DOM
  3. 使用ref引用 组件实例
    只要在引用的子组件的标签上,加一个ref,这个名字就指向了这个子组件的vue实例,就可以通过它来调用这个子组件的属性和方法。

建议:只要是ref的引用,尽量都以Ref后缀结尾,这样一看名字就知道是个引用

this.$nextTick的应用场景

  • 问题

在app组件中,控制组件内按钮和输入框元素的切换

1
2
<input type="text" v-if="inputVisible" @blur="showButton" ref="iptRef">
<button v-else @click="showinput">显示输入框</button>

当我们想要在切换到输入框的时候,直接进入输入框焦点

1
2
3
4
5
6
7
showinput(){
this.inputVisible = true;
this.$refs.iptRef.focus()
},
showButton(){
this.inputVisible = false;
},

但是this.$refs.iptRef.focus()这样却报错了
TypeError: Cannot read properties of undefined (reading 'focus')
这个错误代表说,我要调用一个focus方法,或我要访问一个focus属性,但是它前面那个东西是undefined
当我们打印console.log(this.$refs.iptRef);会发现它是undefined

  • 分析原因

在这个函数中,我们是更改inputVisible的值让文本框显示出来,但是现在页面上文本框还未显示出来,所以拿不到这个元素,因此是undefined
在Vue的生命周期中,当data里面的数据变化,会触发DOM元素的更新,根据最新的数据,重新渲染页面的UI结构。在updated之前,页面还没来得及重新渲染,所以数据是新的,但是页面的结构是旧的。
所以this.inputVisible = true;这句执行之后,页面上还是按钮,文本框还未被展示,还需要一个更新的过程,需要一定的时间。如果立马去执行this.$refs.iptRef.focus()是拿不到文本框的

1
2
3
4
showinput(){
this.inputVisible = true;
this.$refs.iptRef.focus()
},

总结:this.inputVisible = true这行代码执行完,只是数据是最新的,页面还没来得及重新渲染,没有文本框,拿不到它的引用,所以是undefined
要想不报错,this.$refs.iptRef.focus()这一行调用应该放在页面重新渲染完毕之后

  • 解决this.$nextTick(cb)方法
    里面接收一个回调,cb是callback
    代表:把回调函数里面的代码推迟到当DOM重新渲染完毕之后执行
    保证cb回调函数可以操作到最新的DOM元素
1
2
3
4
5
6
showinput(){
this.inputVisible = true;
this.$nextTick(()=>{
this.$refs.iptRef.focus()
})
},