前言
写在前面的话,作者是一只技术菜鸟,文章参考了网上的文章,也有作者的思考。如果热爱技术的你刚巧路过作者的文章,希望对文中表述不准确或错误的地方给予指正,作者不吝赐教,谢谢。
来自官网的引述:
特别提醒:$refs 只会在组件渲染完成之后生效,并且它们不是响应式的。这仅作为一个用于直接操作子组件的“逃生舱”——你应该避免在模板或计算属性中访问 $refs。
ref
ref
:被用来给元素或子组件注册引用信息,引用信息将会注册在父组件的$refs
对象上。如果在普通的DOM元素上使用,那么指向的就是普通的DOM元素。
ref
有三种用法:
ref
加在普通的元素上,用this.$refs.name
获取到的是dom元素。ref
加在子组件上,用this.ref.name
获取到的是组件实例,可以使用组件的所有方法。- 如何利用
v-for
和ref
获取一组数组或者dom 节点。
1.普通的DOM元素上使用
<div id="app">
<input type="text"ref="TEXT"/ >
<button @click="add">添加</button>
</div>
var app=new Vue({
el:"#app",
data:{
},
methods:{
add:function(){
console.log(this.$refs);
}
}
})
2.子组件上使用
<div id="app">
<a ref=inputText></a>
<input type="text"ref="TEXT" >
<button @click="add">添加</button>
</div>
Vue.component('a',{
template:"<div>我是一个组件</div>"
})
var app=new Vue({
el:"#app",
data:{
},
methods:{
add:function(){
console.log(this.$refs.inputText);
console.log(this.$refs);
}
}
})
var a=app.$refs.inputText;
3.v-for 和 ref 一起使用
前情提要:使用v-for
循环组件,同时在组件上使用ref
属性
<template>
<div class="hello">
{{ msg }}
<ul>
<list-item
v-for="item in items"
:key="item.id"
:value="item.text"
:ref="`item${item.id}`"
/>
</ul>
</div>
</template>
<script>
import ListItem from "./ListItem";
export default {
name: "HelloWorld",
components: {
ListItem
},
data() {
return {
msg: "Welcome to Your Vue.js App",
items: [
{ id: 1, text: "foo" },
{ id: 2, text: "bar" },
{ id: 3, text: "baz" },
{ id: 4, text: "foobar" }
]
};
},
mounted() {
var _this = this
setTimeout(function () {
console.error(_this.$refs)
console.error(_this.$refs['[object Object],[object Object],[object Object],[object Object]'])
_this.$refs['[object Object],[object Object],[object Object],[object Object]'][2].highlight()
}, 1500)
}
};
</script>
And ListItem component:
<template>
<li v-bind:class="{ highlight: isHighlighted }">
{{value}}
</li>
</template>
<script>
export default {
name: "list-item",
props: ["value"],
data() {
return {
isHighlighted: false
};
},
methods: {
highlight() {
this.isHighlighted = !this.isHighlighted;
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.highlight {
color: red;
}
</style>
运行报错: Uncaught TypeError: _this.$refs.item2.highlight is not a function
在setTimeOut
的回调函数中打印this.$refs
,得到一个对象,如下图。对象中有一个key值为
[object Object],[object Object],[object Object],[object Object]']
value为长度为4的数组的键值对,取相应的index能得到对应的子元素。但是显然这不是一个很好的方案。
优化:
在v-for
中使用静态ref
,组件或元素节点被存储在一个数组中, 不用再使用序号
<list-item
v-for="item in items"
:key="item.id"
:value="item.text"
ref="items"
/>
And use the refs in your component like this:this.$refs.items[index]
总结:在
v-for
循环的组件或元素上使用静态ref
属性,会将组件集合或元素集合储存在一个数组中,即:当v-for
用于元素或组件的时候,引用信息将是包含 DOM 节点或组件实例的数组。
注意:只要想要在
Vue
中直接操作DOM元素,就必须用ref
属性进行注册。
refs
refs
:一个对象,包含所有注册过ref特性的所有DOM元素和组件实例
refs
只会在组件渲染完成之后生效(只能在mounted
及之后的生命周期中使用),并且它们不是响应式的(使用了v-if
、v-show
、v-for
动态操作DOM,在mounted
阶段是访问不到refs
的)。这只意味着一个直接的子组件封装的“逃生舱”——你应该避免在模板或计算属性中访问refs
。也就是说只有等页面加载完成好之后你才能调用this.$refs
,如果使用v-if
、v-for
渲染页面的话,那么在刚开始页面没没渲染之前你是拿不到this.$refs
的,所以要等到页面渲染之后拿才可以。
Vue
的生命周期:
creating
状态 ---- vue 实例被创建的过程mounting
状态 ---- 挂到到真实的 DOM 节点,渲染出html页面updating
状态 ---- 如果 data 中的数据改变就会触发对应组件的重新渲染destroying
状态 ---- 实例销毁
解决办法:
1、如果在mounted
里获取this.$refs
,因为dom还未完全加载,所以是拿不到的,可以使用 this.$nextTick(() => {})
等页面渲染好再调用。 update
阶段则是完成了数据更新到 DOM 的阶段(对加载回来的数据进行处理),此时,就可以使用this.$refs
了
2、使用setTimeOut()
setTimeout(() => {
console.log(this.$refs.***)
}, 0)
this.$refs.xxx为undefined的几种情况
1、在created里钩子函数中调用
原因:created()在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,refs压根就调不到那个dom,因为页面还没有挂载上去。
解决:在mounted () 钩子函数中调用
注意:在此种情况中,元素节点一定是直接写在html中的,而不是通过数据或者条件渲染的
2、数据或条件渲染(v-if,v-show)之后的调用
原因:
ref 本身作为渲染结果被创建,在初始渲染的时候不能访问他们,是不存在的 refs对象上 调用对象是否和v-if结合使用 ref不是响应式的,所有的动态加载的模板更新它都无法相应的变化。
解决:可以通过setTimeOut(()=>{...}, 0)来实现
总结:
通过this.$refs
访问子组件或子元素的注意事项
-
在vue中操作DOM,必须使用
ref
属性注册一个引用id
,再通过this.$refs.引用id
操作DOM。 -
this.$refs
是一个对象,组件中所有已注册的组件或元素都可以通过this.$refs.注册id
的方式访问。 -
在
v-for
中注册的组件或元素,通过this.$refs.注册id
得到的是一个数组,可通过索引获得某个具体的组件或元素。 -
this.$refs
只会在组件渲染完成之后生效,组件在mounted
中开始渲染,完成时间不确定,所以只能在mounted
及之后的生命周期中访问this.$refs
,也可以使用setTimeout(fun, 0)
的方式来访问,因为这是新一轮宏任务的开始。 -
在
mounted
中使用this.$refs
,由于DOM挂载时间不缺定,必须在this.$nextTick
的回调函数中访问子组件或子元素。 -
避免在计算属性中使用
this. $refs
,由于vue
是响应式的,在数据更新之后不会立马更新DOM,计算属性在依赖改变后立即执行,这时获取到的可能是旧的DOM。 -
使用
id
一般是为了获取元素操作DOM,Vue
是数据驱动不操作DOM,在Vue
项目一般是不使用id
的。