ref主要是引用 DOM 节点或者引用组件实例,说白了就是 Vue 本身不需要你操作 DOM,Vue 底层已经帮你做好了数据的绑定,所有视图的更新都来源于 viewModel 去做双向数据绑定。但是,某些时候你又不得不去获取 DOM 节点(或者 DOM 的某些信息),或者你要操作组件的实例,这个时候你就可以使用ref去引用!
ref 引用 DOM 元素
当我们给 DOM 元素设置ref属性的时候,我们就可以在this.$refs中拿到 DOM 节点的引用。
<template><p><label for="">Username:</label><input type="text" ref="myRef" name="" id="" /></p></template><script>export default{mounted(){console.log(this.$refs);}}</script>

但其实,组件实例在beforeCreate阶段就已经能拿到$refs对象,只不过是个空对象,直到mounted组件挂载后,$refs对象才能拿到 DOM 的引用!
<template><p><label for="">Username:</label><input type="text" ref="myRef" name="" id="" /></p></template><script>export default {beforeCreate() {console.log(this.$refs);},created() {console.log(this.$refs);},beforeMount() {console.log(this.$refs);},mounted() {console.log(this.$refs);}};</script>

这主要是因为 DOM 节点还没有挂载,所以你并不能拿到引用。
如果你尝试修改$refs对象的属性,你讲看到警告信息,因为$refs对象属性是只读的。
export default {mounted() {const oLink = document.createElement("a");oLink.innerText = "Google";oLink.href = "https://www.google.com";// 不可进行修改this.$refs.myRef = oLink;}};

因为ref是在 DOM 渲染后才进行挂载,所以当你通过v-if快速切换ref的时候,你将看到非理想的的结果:
<template><p v-if="showWhat === 'input'"><label for="">Username:</label><input type="text" ref="myRef" name="" id="" /></p><p v-else-if="showWhat === 'link'"><a href="https://www.google.com" ref="myRef">Google</a></p></template><script>export default {data() {return {showWhat: "link"};},mounted() {console.log(this.$refs);this.showWhat = "input";console.log(this.$refs);}};</script>

你将看到两个$refs.myRef都为a!
如果想要避免这样的情况发生,你应该使用setTimeout进行延迟获取,或者使用 Vue 的nextTick来回调获取:
export default {data() {return {showWhat: "link"};},mounted() {console.log(this.$refs);this.showWhat = "input";setTimeout(() => {console.log(this.$refs);});// 或者console.log(this.$refs);this.showWhat = "input";this.$nextTick(() => {console.log(this.$refs);});}};
当我们获取到 DOM 节点的时候,我们就可以对 DOM 进行操作或获取 DOM 的信息:
export default {mounted() {this.$refs.myRef.focus();this.$refs.myRef.offsetHeight;}};
ref 引用组件实例
ref还可以用来引用组件实例:
<my-test ref="myTestRef"></my-test>
当ref作用在组件上的时候,是可以拿到组件的实例,而 DOM 则是渲染后的 DOM 节点。
和 DOM 一样,ref引用组件也是在渲染完成后才能获取到。
export default {beforeCreate() {console.log(this.$refs);},created() {console.log(this.$refs);},mounted() {console.log(this.$refs);}};

另外,ref本身并不是响应式的,所以不要在模版、计算属性(因为非响应式不会触发computed重新计算)中进行使用,也不要尝试去修改ref的值(ref只提供你获取 DOM 或组件,并不是让你去操作的!)
export default{mounted(){// 不要这么做this.$refs.myTestRef.count++;}}
通过ref可以获取到组件内部的属性或者方法,但大多数情况下,你应该首先使用标准的props和emit接口来实现父子组件交互!
<template><my-test ref="myTestRef"></my-test></template><script>import MyTest from "./components/MyTest/index.vue";export default {components: {MyTest},methods: {handleClick() {// 可以获取到 myTestRef.count 属性console.log(this.$refs.myTestRef.count);// 可以调用 myTestRef.addCount 方法this.$refs.myTestRef.addCount();}}};</script>
<template><button @click="handleLog">点击</button></template><script>export default {name: "Test",data() {return {count: 0};},methods: {handleLog() {this.addCount();},addCount() {this.count++;console.log(this.count)}}};</script>
v-for 中的模板引用
当在 v-for 中使用模板引用时,相应的引用中包含的值是一个数组:
<template><ul><li v-for="item in 10" ref="items">{{ item }}</li></ul></template><script>export default {mounted() {console.log(this.$refs.items)}}</script>
:::warning
⚠️ 注意
应该注意的是,ref 数组并不保证与源数组相同的顺序。
:::
