搜索
javascript - Vue.js 跨层事件传递问题
天蓬老师
天蓬老师 2017-04-10 16:31:05
[JavaScript讨论组]

我在 app.vue 里写了这么一个结构,打算通过 v-bind:is="view" 来切换不同的页面。

大概的结构是这样的:

      app.vue  
      /      \
     /        \
login.vue   chat.vue
               |
               |
          contentbox.vue

现在,我需要在 login.vue 上,向 contentbox.vue 发送一个数据,于是我使用事件来传递数据,通过 vm.$dispatchvm.$broadcast 来往上和往下传播事件。

但是,当我在 login.vue 上把 app.vue 的 view 切换到 chat.vue 后,我发现 chat.vue 并不能接收到我传递的事件,更无法传递到 contentbox.vue

不明白这哪里出错了,或者大家有没有别的方法来传递数据呢?请各位指教指教。

以下是代码:

app.vue

<template>
    <p class="container">
        <p class="col-md-6 col-md-offset-3">
            <component v-bind:is="view"></component>
        </p>
    </p>
</template>

<script>

module.exports = {
    events: {
        test: function (data) {
            // app.vue 接收到下层传上来的 test 后,往下广播 test 事件
            console.log('app.vue: got it.')
            this.$broadcast('test', data)
            return false
        }
    }
}

</script>

login.vue

<script>

module.exports = {
    ready: function(){

        // 监听 socket.io 的 login-success 事件
        this.$root.socket.on('login-success', function(data){
            this.$parent.view = 'chat'
            history.pushState null, null, '#/chat'

            // 切换页面后向上传播 test 事件
            console.log('login.vue:sent')
            this.$dispatch('test', data)
        })
    }
}
</script>

chat.vue

<template>
    <inputbox></inputbox>
    <contentbox></contentbox>
</template>
<script>

module.exports = {
    events: {
        test: function (data) {
            console.log('chat.vue: got it.') // 并不能收到
        }
    }
} 

</script>

contentbox.vue

<script>

module.exports = {
    events: {
        test: function (data) {
            console.log('contentbox.vue: got it!') // 当然也到不了这里
            console.log('data:', data)
            return false
        }
    }
}

</script>
天蓬老师
天蓬老师

欢迎选择我的课程,让我们一起见证您的进步~~

全部回复(6)
天蓬老师

采纳的答案有点答非所问,最恰当的答案应该是 HelloVirus 的。

在此补充下 HelloVirus 没说明的:

history.pushState 后立即执行 this.$dispatch('test', data) 这时子组件可能还未渲染,更谈不上绑定事件及监听到 test 事件了!

将广播事件的逻辑放在 $nextTick 触发,即可以保证完成切换组件后子组件响应指定事件。

PHP中文网

DOM上传数据这事大致有两种思路

一种是redux那一套,强调单向传递,虽然和react可能更容易整合,但实际上并没有强依赖react,应该可以和vuejs配合的。写代码比较无脑也比较啰嗦,但思路清晰不容易出问题

另一种传统的,也是题主用了的就是事件了。一般而言,想要把程序结构整理清楚的话,应该遵循的原则是上层通过引用、通过方法调用直接管理下层,而下层不关心上层,需要传递数据时通过事件冒泡向上传递状态。也就是事件一律往上走,方法调用一律往下调用。

vuejs我好久以前用的,印象里也没见过$broadcast这样的用法,总之向下扩散事件即使支持这个用法,也很容易导致整体结构混乱,不建议。

类似的还用向上引用,比如题主的this.$parent.view = xxx 也是很容易导致混乱的实践(login这个组件只能在$parent.view = xxx有意义的地方创建了)。建议是login中触发login-success事件,app监听它创建的login组件的login-success,处理时调用chat组件的方法(比如刷新信息之类)

又比如我做一个dialog组件,里面可能会用alertconfirm这样的方法来显示对话框,然后会对外暴露close confirm-yes 之类的事件来告知调用dialog的人这个对话框的情况

ringa_lee

你所说的v-bind:is="view"是关于动态组件的用法对吧?
如果组件chat.vuecontentbox.vue是动态组件的话,从app.vue广播的事件,它们时收不到的,因为动态组件这时候还没有实例化出来,自然监听不到(个人实践分析的,有不同看法请告知纠正)。如果是非动态组件,是可以收到父组件的广播的(实践得知)。
这是我目前的一些实践:https://github.com/toplan/vue-spa-example

大家讲道理

父容器的broadcast延迟到最后执行nextTick或者setTimeout

module.exports = {
    events: {
        test: function (data) {
            // app.vue 接收到下层传上来的 test 后,往下广播 test 事件
            console.log('app.vue: got it.');
            var _self=this;
            Vue.nextTick(function(){
                _self.$broadcast('go2chat','go2chat');
            });
            //or
            /*
            setTimeout(function(){
                _self.$broadcast('test', data);
                
            },0);
            */
           
            return false
        }
    }
}
天蓬老师

可以尝试在app.vue中声明一个变量来存储,login.vue冒泡上来的数据,然后针对这个数据再做广播

天蓬老师

我也碰到同样的问题,不知道楼主解决了吗?

热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号