Nous avons un composant dans Vue qui est un cadre, ajusté à la taille de la fenêtre, qui contient (dans un <slot>
) un élément (généralement <img>
ou <canvas>
) qu’il redimensionne pour s’adapter au cadre et permet un panoramique et un zoom sur cet élément.
Le composant doit réagir lorsque l'élément change. La seule façon de le faire est que le parent prodigue le composant lorsque cela se produit. Toutefois, il serait beaucoup plus agréable que le composant puisse détecter automatiquement le moment où l'élément <slot>
change et réagir en conséquence. Y a-t-il un moyen de faire cela?
À ma connaissance, Vue ne permet pas de le faire. Cependant, voici deux approches qui méritent d'être considérées.
Utilisez un MutationObserver pour détecter quand le DOM dans <slot>
change. Cela ne nécessite aucune communication entre les composants. Configurez simplement l'observateur lors du rappel mounted
de votre composant.
Voici un extrait montrant cette approche en action:
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>
Si un composant Vue est responsable de la modification de l'emplacement, il est préférable de émettre un événement lorsque cette modification se produit. Cela permet à tout autre composant de répondre à l'événement émis si nécessaire.
Pour ce faire, utilisez une instance Vue vide en tant que bus d'événements global. Tout composant peut émettre/écouter des événements sur le bus d’événements. Dans votre cas, le composant parent peut émettre un événement "updated-content" et le composant enfant peut y réagir.
Voici un exemple simple:
// 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>
Je vous suggérerais d’envisager cette astuce: https://codesandbox.io/s/1yn7nn72rl , que j’avais l'habitude de surveiller les modifications et de faire quoi que ce soit avec un contenu de fente.
L'idée, inspirée par le fonctionnement de composant VIcon de vuetify , consiste à utiliser un composant fonctionnel dans lequel nous implémentons la logique dans sa fonction render
. Un objet context
est passé en tant que deuxième argument de la fonction render
. En particulier, l'objet context
a une propriété data
(dans laquelle vous pouvez trouver des attributs, attrs
) et une propriété children
, correspondant à l'emplacement (vous pouvez éventuellement appeler la fonction context.slot()
avec le même résultat).
Meilleures salutations