我们在Vue中有一个组件,它是一个按窗口大小zoom 的框架,其中包含(在<slot>
中)一个元素(通常是<img>
或<canvas>
),它可以zoom 该元素以适应框架,并可以对该元素进行平移和zoom .
当元素发生变化时,组件需要做出react .我们能看到的唯一方法是,当这种情况发生时,父级可以刺激组件,但是如果组件能够自动检测<slot>
个元素的变化并做出相应的react ,那就更好了.有办法做到这一点吗?
我们在Vue中有一个组件,它是一个按窗口大小zoom 的框架,其中包含(在<slot>
中)一个元素(通常是<img>
或<canvas>
),它可以zoom 该元素以适应框架,并可以对该元素进行平移和zoom .
当元素发生变化时,组件需要做出react .我们能看到的唯一方法是,当这种情况发生时,父级可以刺激组件,但是如果组件能够自动检测<slot>
个元素的变化并做出相应的react ,那就更好了.有办法做到这一点吗?
据我所知,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组件负责更改插槽,则最好在发生更改时更改emit an event.这允许任何其他组件在需要时响应发出的事件.
为此,请将空的Vue实例用作全局事件总线.任何组件都可以在事件总线上发出/侦听事件.在您的情况下,父组件可能会发出"更新的内容"事件,子组件可能会对此做出react .
下面是一个简单的例子:
// 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>