Vueにコンポーネントがあり、これはフレームサイズであり、ウィンドウサイズにスケーリングされ、(<slot>
)要素(通常<img>
または<canvas>
)フレームに合わせてスケーリングし、その要素のパンとズームを有効にします。
コンポーネントは、要素が変更されたときに反応する必要があります。それを行うために確認できる唯一の方法は、それが発生したときに親がコンポーネントを生成することですが、コンポーネントが<slot>
要素が変更され、それに応じて反応します。これを行う方法はありますか?
私の知る限り、Vueはこれを行う方法を提供していません。しかし、ここで検討する価値のある2つのアプローチがあります。
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インスタンスをグローバルイベントバスとして使用します。どのコンポーネントもイベントバス上のイベントをエミット/リッスンできます。この場合、親コンポーネントは "updated- content "イベント、そして子コンポーネントはそれに反応することができます。
以下に簡単な例を示します。
// 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);
},
_
これは、DOM要素の追加や削除など、99%の変更がスロットで発生する場合は常に機能します。唯一のEdgeケースは次のような使用法でした:
_<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>
_
このトリックを検討することをお勧めします: https://codesandbox.io/s/1yn7nn72rl 、変更を監視してスロットコンテンツで何かをするために使用しました。
vuetify の動作VIconコンポーネントに触発されたアイデアは、ロジックを実装する機能コンポーネントを使用することですrender
関数。 context
オブジェクトは、render
関数の2番目の引数として渡されます。特に、context
オブジェクトには、スロットに対応するdata
プロパティ(属性attrs
を見つけることができます)とchildren
プロパティがあります。 (同じ結果でcontext.slot()
関数をイベント呼び出しできます)。
宜しくお願いします