[英]In Vue.js can a component detect when the slot content changes
我們在 Vue 中有一個組件,它是一個框架,縮放到 window 大小,它包含(在<slot>
中)一個元素(通常是<img>
或<canvas>
),它可以縮放以適應框架並啟用平移和縮放在那個元素上。
當元素發生變化時,組件需要做出反應。 我們可以看到的唯一方法是讓父組件在發生這種情況時刺激組件,但是如果組件能夠自動檢測<slot>
元素何時發生變化並做出相應反應,那就更好了。 有沒有辦法做到這一點?
據我所知,Vue 沒有提供這樣做的方法。 然而,這里有兩種方法值得考慮。
使用MutationObserver來檢測<slot>
中的 DOM 何時發生變化。 這不需要組件之間的通信。 只需在組件的mounted
回調期間設置觀察者。
這是一個片段,顯示了這種方法的實際效果:
Vue.component('container', { template: '#container', data: function() { return { number: 0, observer: null } }, mounted: function() { // Create the observer (and what to do on changes...) this.observer = new MutationObserver(function(mutations) { this.number++; }.bind(this)); // Setup the observer this.observer.observe( $(this.$el).find('.content')[0], { attributes: true, childList: true, characterData: true, subtree: true } ); }, beforeDestroy: function() { // Clean up this.observer.disconnect(); } }); var app = new Vue({ el: '#app', data: { number: 0 }, mounted: function() { //Update the element in the slot every second setInterval(function(){ this.number++; }.bind(this), 1000); } });
.content, .container { margin: 5px; border: 1px solid black; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script> <template id="container"> <div class="container"> I am the container, and I have detected {{ number }} updates. <div class="content"><slot></slot></div> </div> </template> <div id="app"> <container> I am the content, and I have been updated {{ number }} times. </container> </div>
如果 Vue 組件負責更改插槽,那么最好在更改發生時發出事件。 這允許任何其他組件在需要時響應發出的事件。
為此,請使用一個空的 Vue 實例作為全局事件總線。 任何組件都可以在事件總線上發出/監聽事件。 在您的情況下,父組件可能會發出“更新內容”事件,而子組件可能會對它做出反應。
這是一個簡單的例子:
// Use an empty Vue instance as an event bus var bus = new Vue() Vue.component('container', { template: '#container', data: function() { return { number: 0 } }, methods: { increment: function() { this.number++; } }, created: function() { // listen for the 'updated-content' event and react accordingly bus.$on('updated-content', this.increment); }, beforeDestroy: function() { // Clean up bus.$off('updated-content', this.increment); } }); var app = new Vue({ el: '#app', data: { number: 0 }, mounted: function() { //Update the element in the slot every second, // and emit an "updated-content" event setInterval(function(){ this.number++; bus.$emit('updated-content'); }.bind(this), 1000); } });
.content, .container { margin: 5px; border: 1px solid black; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script> <template id="container"> <div class="container"> I am the container, and I have detected {{ number }} updates. <div class="content"> <slot></slot> </div> </div> </template> <div id="app"> <container> I am the content, and I have been updated {{ number }} times. </container> </div>
據我了解 Vue 2+,當插槽內容發生變化時,應該重新渲染組件。 在我的情況下,我有一個error-message
組件,它應該隱藏,直到它有一些插槽內容要顯示。 起初,我將此方法附加到組件根元素的v-if
上( computed
屬性不起作用,Vue 似乎對this.$slots
沒有反應性)。
checkForSlotContent() {
let checkForContent = (hasContent, node) => {
return hasContent || node.tag || (node.text && node.text.trim());
}
return this.$slots.default && this.$slots.default.reduce(checkForContent, false);
},
只要插槽中發生 99% 的更改(包括任何添加或刪除 DOM 元素),這都會很好地工作。 唯一的邊緣情況是這樣的用法:
<error-message> {{someErrorStringVariable}} </error-message>
這里只更新了一個文本節點,由於我仍然不清楚的原因,我的方法不會觸發。 我通過連接beforeUpdate()
和created()
修復了這種情況,讓我得到一個完整的解決方案:
<script>
export default {
data() {
return {
hasSlotContent: false,
}
},
methods: {
checkForSlotContent() {
let checkForContent = (hasContent, node) => {
return hasContent || node.tag || (node.text && node.text.trim());
}
return this.$slots.default && this.$slots.default.reduce(checkForContent, false);
},
},
beforeUpdate() {
this.hasSlotContent = this.checkForSlotContent();
},
created() {
this.hasSlotContent = this.checkForSlotContent();
}
};
</script>
還有另一種方法可以對插槽更改做出反應。 老實說,我覺得它更干凈,以防萬一。 對我來說,emit+event-bus 和突變觀察都不正確。
采取以下場景:
<some-component>{{someVariable}}</some-component>
在這種情況下,當 someVariable 發生變化時,一些組件應該做出反應。 我在這里要做的是在組件上定義一個 :key,這會在 someVariable 更改時強制它重新呈現。
<some-component :key="someVariable">Some text {{someVariable}}</some-component>
親切的問候 Rozbeh Chiryai Sharahi
在帶有腳本設置語法的 Vue 3 中,我使用 MutationObserver 取得了巨大成功:
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
const container = ref();
const mutationObserver = ref(null);
const mockData = ref([]);
const desiredFunc = () => {
console.log('children changed');
};
const connectMutationObserver = () => {
mutationObserver.value = new MutationObserver(desiredFunc);
mutationObserver.value.observe(container.value, {
attributes: true,
childList: true,
characterData: true,
subtree: true,
});
};
const disconnectMutationObserver = () => {
mutationObserver.value.disconnect();
};
onMounted(async () => {
connectMutationObserver();
setTimeout(() => { mockData.value = [1, 2, 3]; }, 5000);
});
onUnmounted(() => {
disconnectMutationObserver();
});
</script>
<template>
<div ref="container">
<div v-for="child in mockData" :key="child">
{{ child }}
</div>
</div>
</template>
在此處閱讀更多信息: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.