Vue使用$refs來訪問組件實(shí)例或DOM元素的注意事項(xiàng)說明
在 Vue 項(xiàng)目里,$refs 是個(gè)超實(shí)用的工具,它能讓你直接訪問組件實(shí)例或者 DOM 元素。不過使用的時(shí)候,有一些地方可得注意,下面咱就詳細(xì)嘮嘮。
$refs只有在組件渲染完成后才可用
在 Vue 里,組件從創(chuàng)建到渲染完成是有個(gè)過程的。只有當(dāng)組件完全渲染好了,$refs 才能正常使用。要是在組件還沒渲染好的時(shí)候就想用 $refs 去訪問東西,那肯定會(huì)出問題。所以,通常會(huì)把使用 $refs 的代碼放到 mounted 鉤子函數(shù)里,因?yàn)檫@個(gè)鉤子函數(shù)是在組件渲染完成后才執(zhí)行的。
export default {
// 組件掛載完成后執(zhí)行的鉤子函數(shù)
mounted() {
// 這里可以安全地使用 $refs 訪問組件實(shí)例或 DOM 元素
this.$refs.myComponent.someMethod(); // 調(diào)用組件實(shí)例的方法
this.$refs.myElement.focus(); // 讓 DOM 元素獲取焦點(diǎn)
}
};
不要在模板里直接使用$refs
雖然 $refs 能讓你訪問組件實(shí)例或者 DOM 元素,但千萬(wàn)別在模板里直接用它。因?yàn)槟0謇锏拇a會(huì)在每次數(shù)據(jù)更新的時(shí)候重新計(jì)算,如果在模板里用 $refs,可能會(huì)導(dǎo)致意外的結(jié)果,而且還會(huì)影響性能。
<template>
<!-- 不要這樣做 -->
<!-- <div>{{ $refs.myElement.textContent }}</div> -->
<div ref="myElement">這是一個(gè) DOM 元素</div>
</template>
<script>
export default {
mounted() {
// 在 mounted 鉤子函數(shù)里使用 $refs
const text = this.$refs.myElement.textContent;
console.log(text); // 輸出: 這是一個(gè) DOM 元素
}
};
</script>
$refs不是響應(yīng)式的
$refs 不像 Vue 的響應(yīng)式數(shù)據(jù)那樣,數(shù)據(jù)一變頁(yè)面就跟著更新。$refs 只是一個(gè)普通的對(duì)象,它的屬性值在組件渲染完成后就固定了。所以,如果你想在數(shù)據(jù)變化的時(shí)候更新 $refs 相關(guān)的操作,就得手動(dòng)去處理。
<template>
<div>
<button @click="updateElement">更新元素</button>
<div ref="myElement">{{ message }}</div>
</div>
</template>
<script>
export default {
data() {
return {
message: '初始消息'
};
},
methods: {
updateElement() {
this.message = '更新后的消息';
// 手動(dòng)更新 $refs 相關(guān)的操作
this.$refs.myElement.textContent = this.message;
}
}
};
</script>
動(dòng)態(tài)綁定ref時(shí)要注意
要是你需要?jiǎng)討B(tài)綁定 ref,也就是根據(jù)不同的條件給不同的元素或者組件綁定 ref,那得小心了。因?yàn)閯?dòng)態(tài)綁定 ref 可能會(huì)導(dǎo)致 $refs 的值發(fā)生變化,所以在使用的時(shí)候要確保 $refs 里確實(shí)有你想要的元素或者組件。
<template>
<div>
<!-- 動(dòng)態(tài)綁定 ref -->
<component :is="currentComponent" :ref="currentRef"></component>
<button @click="switchComponent">切換組件</button>
</div>
</template>
<script>
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';
export default {
data() {
return {
currentComponent: ComponentA,
currentRef: 'componentRef',
componentRef: null
};
},
methods: {
switchComponent() {
this.currentComponent = this.currentComponent === ComponentA ? ComponentB : ComponentA;
// 切換組件后,確保 $refs 里有正確的組件實(shí)例
if (this.$refs.componentRef) {
this.$refs.componentRef.someMethod();
}
}
}
};
</script>
在子組件銷毀時(shí)清理$refs
當(dāng)子組件被銷毀的時(shí)候,$refs 里對(duì)應(yīng)的引用不會(huì)自動(dòng)清除。所以,要是你在子組件銷毀后還去訪問 $refs 里的這個(gè)引用,就會(huì)報(bào)錯(cuò)。為了避免這種情況,你可以在子組件銷毀的時(shí)候手動(dòng)清理 $refs 里的引用。
<template>
<div>
<child-component ref="childRef"></child-component>
<button @click="destroyChild">銷毀子組件</button>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
methods: {
destroyChild() {
// 銷毀子組件
this.$refs.childRef.$destroy();
// 手動(dòng)清理 $refs 里的引用
this.$refs.childRef = null;
}
}
};
</script>
總之,$refs 是個(gè)很強(qiáng)大的工具,但使用的時(shí)候得注意上面這些點(diǎn),這樣才能避免出現(xiàn)各種奇怪的問題,讓你的 Vue 項(xiàng)目更加穩(wěn)定。
除了$refs,Vue還有哪些可以訪問組件實(shí)例或DOM元素的方式?
在 Vue 里,除了使用 $refs 訪問組件實(shí)例或 DOM 元素外,還有以下幾種方式:
通過事件機(jī)制傳遞信息
在 Vue 中,你可以利用自定義事件和事件總線在組件間傳遞數(shù)據(jù),從而間接訪問組件實(shí)例。
自定義事件
子組件能夠通過 $emit 觸發(fā)自定義事件,把數(shù)據(jù)傳遞給父組件,父組件在接收到事件后就可以訪問子組件實(shí)例的屬性或方法。
<template>
<!-- 父組件 -->
<div>
<child-component @custom-event="handleCustomEvent"></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
methods: {
handleCustomEvent(childInstance) {
// 訪問子組件實(shí)例
console.log(childInstance.someMethod());
}
}
};
</script>
<template>
<!-- 子組件 -->
<div>
<button @click="sendInstance">發(fā)送實(shí)例</button>
</div>
</template>
<script>
export default {
methods: {
sendInstance() {
// 觸發(fā)自定義事件,傳遞當(dāng)前組件實(shí)例
this.$emit('custom-event', this);
},
someMethod() {
return '這是子組件的方法';
}
}
};
</script>
事件總線
事件總線是一個(gè)全局的事件中心,組件能夠在上面觸發(fā)和監(jiān)聽事件,以此實(shí)現(xiàn)組件間的通信。
// eventBus.js import Vue from 'vue'; export const eventBus = new Vue();
<template>
<!-- 發(fā)送組件 -->
<div>
<button @click="sendMessage">發(fā)送消息</button>
</div>
</template>
<script>
import { eventBus } from './eventBus.js';
export default {
methods: {
sendMessage() {
// 觸發(fā)事件總線的事件
eventBus.$emit('message-sent', this);
}
}
};
</script>
<template>
<!-- 接收組件 -->
<div></div>
</template>
<script>
import { eventBus } from './eventBus.js';
export default {
mounted() {
// 監(jiān)聽事件總線的事件
eventBus.$on('message-sent', (senderInstance) => {
console.log(senderInstance.someMethod());
});
}
};
</script>
使用provide和inject
provide 和 inject 主要用于實(shí)現(xiàn)跨級(jí)組件間的通信,父組件能夠通過 provide 提供數(shù)據(jù),子組件可以使用 inject 注入這些數(shù)據(jù)。
<template>
<!-- 父組件 -->
<div>
<child-component></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
provide() {
return {
parentInstance: this
};
}
};
</script>
<template>
<!-- 子組件 -->
<div></div>
</template>
<script>
export default {
inject: ['parentInstance'],
mounted() {
// 訪問父組件實(shí)例
console.log(this.parentInstance.someMethod());
}
};
</script>
使用Vue.observable(Vue 2)或reactive(Vue 3)
在 Vue 2 里可以使用 Vue.observable 創(chuàng)建一個(gè)響應(yīng)式對(duì)象,在 Vue 3 中則使用 reactive。通過這個(gè)響應(yīng)式對(duì)象存儲(chǔ)組件實(shí)例,從而實(shí)現(xiàn)對(duì)組件實(shí)例的訪問。
Vue 2
// store.js
import Vue from 'vue';
export const store = Vue.observable({
componentInstance: null
});
<template>
<!-- 組件 A -->
<div>
<button @click="setInstance">設(shè)置實(shí)例</button>
</div>
</template>
<script>
import { store } from './store.js';
export default {
methods: {
setInstance() {
// 將當(dāng)前組件實(shí)例存儲(chǔ)到響應(yīng)式對(duì)象中
store.componentInstance = this;
}
}
};
</script>
<template>
<!-- 組件 B -->
<div></div>
</template>
<script>
import { store } from './store.js';
export default {
mounted() {
// 訪問存儲(chǔ)的組件實(shí)例
if (store.componentInstance) {
console.log(store.componentInstance.someMethod());
}
}
};
</script>
Vue 3
// store.js
import { reactive } from 'vue';
export const store = reactive({
componentInstance: null
});
<template>
<!-- 組件 A -->
<div>
<button @click="setInstance">設(shè)置實(shí)例</button>
</div>
</template>
<script setup>
import { store } from './store.js';
const setInstance = () => {
// 將當(dāng)前組件實(shí)例存儲(chǔ)到響應(yīng)式對(duì)象中
store.componentInstance = getCurrentInstance();
};
</script>
<template>
<!-- 組件 B -->
<div></div>
</template>
<script setup>
import { store } from './store.js';
if (store.componentInstance) {
console.log(store.componentInstance.someMethod());
}
</script>
這些方法都有各自的適用場(chǎng)景,你可以根據(jù)具體需求來選擇合適的方式。
在Vue中,如何避免$refs訪問帶來的性能問題?
在 Vue 里使用 $refs 訪問組件實(shí)例或 DOM 元素時(shí),若使用不當(dāng)可能會(huì)引發(fā)性能問題。以下是一些避免 $refs 訪問帶來性能問題的方法:
避免在渲染循環(huán)中頻繁訪問$refs
$refs 本身并非響應(yīng)式的,但在渲染循環(huán)里頻繁訪問 $refs 會(huì)導(dǎo)致不必要的計(jì)算和 DOM 操作,從而降低性能。應(yīng)該把 $refs 的訪問邏輯移到事件處理函數(shù)或者生命周期鉤子函數(shù)中。
<template>
<div>
<div ref="myElement">示例元素</div>
<button @click="handleClick">點(diǎn)擊</button>
</div>
</template>
<script>
export default {
methods: {
handleClick() {
// 在事件處理函數(shù)中訪問 $refs
const element = this.$refs.myElement;
if (element) {
// 對(duì)元素進(jìn)行操作
element.style.color = 'red';
}
}
}
};
</script>
僅在必要時(shí)使用$refs
$refs 主要用于直接訪問組件實(shí)例或 DOM 元素,不過很多時(shí)候可以借助 Vue 的響應(yīng)式系統(tǒng)來實(shí)現(xiàn)相同的功能,從而避免使用 $refs。
示例:動(dòng)態(tài)改變樣式
不使用 $refs 的情況:
<template>
<div>
<div :style="{ color: textColor }">示例元素</div>
<button @click="changeColor">改變顏色</button>
</div>
</template>
<script>
export default {
data() {
return {
textColor: 'black'
};
},
methods: {
changeColor() {
this.textColor = 'red';
}
}
};
</script>
及時(shí)清理不再使用的$refs
當(dāng)組件被銷毀時(shí),$refs 里對(duì)應(yīng)的引用不會(huì)自動(dòng)清除。若不清理,可能會(huì)造成內(nèi)存泄漏。所以在組件銷毀時(shí)要手動(dòng)清理 $refs。
<template>
<div>
<child-component ref="childRef"></child-component>
<button @click="destroyChild">銷毀子組件</button>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
methods: {
destroyChild() {
// 銷毀子組件
this.$refs.childRef.$destroy();
// 手動(dòng)清理 $refs 里的引用
this.$refs.childRef = null;
}
}
};
</script>
避免在watch中頻繁訪問$refs
watch 用于監(jiān)聽數(shù)據(jù)變化,若在 watch 里頻繁訪問 $refs,會(huì)導(dǎo)致不必要的性能開銷??梢栽?watch 中設(shè)置 immediate: false,避免初始化時(shí)就執(zhí)行訪問操作。
<template>
<div>
<div ref="myElement">示例元素</div>
<input v-model="inputValue" />
</div>
</template>
<script>
export default {
data() {
return {
inputValue: ''
};
},
watch: {
inputValue: {
handler(newValue) {
if (this.$refs.myElement) {
// 對(duì)元素進(jìn)行操作
this.$refs.myElement.textContent = newValue;
}
},
immediate: false // 避免初始化時(shí)執(zhí)行
}
}
};
</script>
利用緩存機(jī)制
要是需要多次訪問 $refs,可以把訪問結(jié)果緩存起來,避免重復(fù)訪問。
<template>
<div>
<div ref="myElement">示例元素</div>
<button @click="doSomething">執(zhí)行操作</button>
<button @click="doAnotherThing">執(zhí)行另一個(gè)操作</button>
</div>
</template>
<script>
export default {
data() {
return {
cachedElement: null
};
},
methods: {
getElement() {
if (!this.cachedElement) {
this.cachedElement = this.$refs.myElement;
}
return this.cachedElement;
},
doSomething() {
const element = this.getElement();
if (element) {
// 對(duì)元素進(jìn)行操作
element.style.fontSize = '20px';
}
},
doAnotherThing() {
const element = this.getElement();
if (element) {
// 對(duì)元素進(jìn)行另一個(gè)操作
element.style.backgroundColor = 'yellow';
}
}
}
};
</script>
通過以上這些方法,可以有效避免 $refs 訪問帶來的性能問題,提升 Vue 應(yīng)用的性能和穩(wěn)定性。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Vuex子模塊調(diào)用子模塊的actions或mutations實(shí)現(xiàn)方式
這篇文章主要介紹了Vuex子模塊調(diào)用子模塊的actions或mutations實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10
vue-cli+webpack項(xiàng)目 修改項(xiàng)目名稱的方法
下面小編就為大家分享一篇vue-cli+webpack項(xiàng)目 修改項(xiàng)目名稱的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-02-02
詳解vue-cli+element-ui樹形表格(多級(jí)表格折騰小計(jì))
這篇文章主要介紹了vue-cli + element-ui 樹形表格(多級(jí)表格折騰小計(jì)),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-04-04

