Bas*_*MHL 57 javascript vue.js vuejs2
似乎Vue.js 2.0不会将大孩子的事件发送到他的祖父组件.
Vue.component('parent', {
template: '<div>I am the parent - {{ action }} <child @eventtriggered="performAction"></child></div>',
data(){
return {
action: 'No action'
}
},
methods: {
performAction() { this.action = 'actionDone' }
}
})
Vue.component('child', {
template: '<div>I am the child <grand-child></grand-child></div>'
})
Vue.component('grand-child', {
template: '<div>I am the grand-child <button @click="doEvent">Do Event</button></div>',
methods: {
doEvent() { this.$emit('eventtriggered') }
}
})
new Vue({
el: '#app'
})
Run Code Online (Sandbox Code Playgroud)
这个JsFiddle解决了问题https://jsfiddle.net/y5dvkqbd/4/,但通过发布两个事件:
添加此中间事件似乎是重复且不必要的.有没有办法直接向祖父母发出我不知道的事情?
Ber*_*ert 32
Vue社区通常倾向于使用Vuex来解决此类问题.对Vuex状态进行了更改,DOM表示只是从那里流出,在许多情况下消除了对事件的需求.
除此之外,重新发射可能是下一个最佳选择,最后你可能会选择使用事件总线,详见其他高度回答这个问题的答案.
下面的答案是我对这个问题的原始答案,并不是我现在采取的方法,对Vue有更多的经验.
在这种情况下,我可能不同意Vue的设计选择并采用DOM.
在grand-child,
methods: {
doEvent() {
try {
this.$el.dispatchEvent(new Event("eventtriggered"));
} catch (e) {
// handle IE not supporting Event constructor
var evt = document.createEvent("Event");
evt.initEvent("eventtriggered", true, false);
this.$el.dispatchEvent(evt);
}
}
}
Run Code Online (Sandbox Code Playgroud)
在parent,
mounted(){
this.$el.addEventListener("eventtriggered", () => this.performAction())
}
Run Code Online (Sandbox Code Playgroud)
否则,是的,你必须重新发射,或使用公共汽车.
注意:我在doEvent方法中添加了代码来处理IE; 该代码可以以可重用的方式提取.
Sly*_*nal 21
是的,你是正确的事件只是从孩子到父母.他们不会走得更远,例如从孩子到祖父母.
Vue文档(简要地)解决了非父 - 子通信部分中的这种情况.
一般的想法是,在祖父母组件中,您创建一个空Vue组件,通过道具从祖父母传递给子孙.然后,祖父母监听事件,孙子孙女在"事件总线"上发出事件.
某些应用程序使用全局事件总线而不是每个组件的事件总线.使用全局事件总线意味着您需要具有唯一的事件名称或命名空间,以便事件不会在不同组件之间发生冲突.
以下是如何实现简单的全局事件总线的示例.
dig*_*out 20
如果您想灵活地向所有父母及其父母递归地广播一个事件,直到根,您可以执行以下操作:
let vm = this.$parent
while(vm) {
vm.$emit('submit')
vm = vm.$parent
}
Run Code Online (Sandbox Code Playgroud)
Bas*_*MHL 17
新答案(2018年11月更新)
我发现我们实际上可以通过利用$parent大子组件中的属性来做到这一点:
this.$parent.$emit("submit", {somekey: somevalue})
Run Code Online (Sandbox Code Playgroud)
更清洁,更简单。
Mic*_*ush 17
Vue 2.4引入了一种使用以下方法轻松将事件传递到层次结构的方法 vm.$listeners
从https://vuejs.org/v2/api/#vm-listeners:
包含父级
v-on事件侦听器(不带.native修饰符)。可以通过以下方式将其传递给内部组件v-on="$listeners"-在创建透明包装器组件时很有用。
请参阅以下代码段,在模板v-on="$listeners"的grand-child组件中使用child:
Vue.component('parent', {
template: '<div><p>I am the parent. The value is {{displayValue}}.</p> <child @toggle-value="toggleValue"></child></div>',
data(){
return {
value: false
}
},
methods: {
toggleValue() { this.value = !this.value }
},
computed: {
displayValue(){
return (this.value ? "ON" : "OFF")
}
}
})
Vue.component('child', {
template: '<div class="child"><p>I am the child. I\'m just a wrapper providing some UI.</p><grand-child v-on="$listeners"></grand-child></div>'
})
Vue.component('grand-child', {
template: '<div><p>I am the grand-child: <button @click="emitToggleEvent">Toggle the value</button></p></div>',
methods: {
emitToggleEvent() { this.$emit('toggle-value') }
}
})
new Vue({
el: '#app'
})Run Code Online (Sandbox Code Playgroud)
.child {
padding: 10px;
border: 1px solid #ddd;
background: #f0f0f0
}Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<parent></parent>
</div>Run Code Online (Sandbox Code Playgroud)
Sph*_*inx 14
另一种解决方案是:
使用vm.$root.$emit在盛大的孩子,然后使用vm.$root.$on在祖先(或者任何你想).
Vue.component('parent', {
template: '<div><button @click="toggleEventListener()">Listener is {{eventEnable ? "On" : "Off"}}</button>I am the parent - {{ action }} <child @eventtriggered="performAction"></child></div>',
data(){
return {
action: 1,
eventEnable: false
}
},
created: function () {
this.addEventListener()
},
beforeDestroy: function () {
this.removeEventListener()
},
methods: {
performAction() { this.action += 1 },
toggleEventListener: function () {
if (this.eventEnable) {
this.removeEventListener()
} else {
this.addEventListener()
}
},
addEventListener: function () {
this.$root.$on('eventtriggered1', () => {
this.performAction()
})
this.eventEnable = true
},
removeEventListener: function () {
this.$root.$off('eventtriggered1')
this.eventEnable = false
}
}
})
Vue.component('child', {
template: '<div>I am the child <grand-child @eventtriggered="doEvent"></grand-child></div>',
methods: {
doEvent() {
//this.$emit('eventtriggered')
}
}
})
Vue.component('grand-child', {
template: '<div>I am the grand-child <button @click="doEvent">Emit Event</button></div>',
methods: {
doEvent() { this.$root.$emit('eventtriggered1') }
}
})
new Vue({
el: '#app'
})Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<parent></parent>
</div>Run Code Online (Sandbox Code Playgroud)
VueJS 2 组件有一个$parent包含其父组件的属性。
该父组件还包含其自己的$parent属性。
然后,访问“祖父母”组件就是访问“父母的父母”组件:
this.$parent["$parent"].$emit("myevent", { data: 123 });
Run Code Online (Sandbox Code Playgroud)
不管怎样,这有点棘手,我建议使用像 Vuex 这样的全局状态管理器或类似的工具,正如其他响应者所说的那样。
这是我使用事件总线的唯一情况!!用于将数据从深层嵌套的子级传递到非直接父级的通信。
首先:使用以下内容创建一个 js 文件(我将其命名为 eventbus.js):
import Vue from 'vue'
Vue.prototype.$event = new Vue()
Run Code Online (Sandbox Code Playgroud)
第二:在您的子组件中发出一个事件:
this.$event.$emit('event_name', 'data to pass')
Run Code Online (Sandbox Code Playgroud)
第三:在父级中收听该事件:
this.$event.$on('event_name', (data) => {
console.log(data)
})
Run Code Online (Sandbox Code Playgroud)
注意:如果您不想再参加该活动,请取消注册:
this.$event.$off('event_name')
Run Code Online (Sandbox Code Playgroud)
信息:无需阅读以下个人意见
我不喜欢将 vuex 用于孙子到祖父的通信(或类似的通信级别)。
在 vue.js 中将数据从祖父母传递给孙子女,您可以使用provide/inject。但是相反的东西没有类似的东西。(孙子到祖父母)所以每当我必须进行这种通信时,我都会使用事件总线。
我根据 @digout 的回答做了一个简短的 mixin。您想将其放在 Vue 实例初始化(新 Vue...)之前,以便在项目中全局使用它。您可以像普通事件一样使用它。
Vue.mixin({
methods: {
$propagatedEmit: function (event, payload) {
let vm = this.$parent;
while (vm) {
vm.$emit(event, payload);
vm = vm.$parent;
}
}
}
})
Run Code Online (Sandbox Code Playgroud)
重复 @kubaklam 和 @digout 的答案,这就是我用来避免在孙子和(可能遥远的)祖父母之间的每个父组件上发出信号的方法:
{
methods: {
tunnelEmit (event, ...payload) {
let vm = this
while (vm && !vm.$listeners[event]) {
vm = vm.$parent
}
if (!vm) return console.error(`no target listener for event "${event}"`)
vm.$emit(event, ...payload)
}
}
}
Run Code Online (Sandbox Code Playgroud)
当构建一个具有远孙子组件的组件时,您不希望许多/任何组件与存储相关联,但希望根组件充当存储/事实来源,这非常有效。这类似于 Ember 的“数据向下行动向上”的理念。缺点是,如果您想监听中间每个父母的该事件,那么这是行不通的。但是,您可以使用 $propogateEmit,如上面 @kubaklam 的回答所示。
编辑:初始 vm 应该设置为组件,而不是组件的父组件。即let vm = this而不是let vm = this.$parent