# Vue组件二-事件反馈 - 子组件向父组件发送消息,父组件监听消息

Vue组件二-事件反馈 - 子组件向父组件发送消息,父组件监听消息

开始

Vue组件是学习Vue框架最比较难的部分,而这部分难点我认为可以分为三个部分学习,即

  1. 组件的传值 - 父组件向子组件中传值
  2. 事件回馈 - 子组件向父组件发送消息,父组件监听消息
  3. 分发内容

整片博客使用的源代码-请点击

所以将用三篇博客分别进行介绍以上三种情况和使用

消息监听,消息发送

在理解Vue事件之前,可以简单理解一下消息中心的设计模式,如下图,即每一个订阅者,都可以去订阅消息。而消息会提供一个"消息名称",订阅者可以通过"消息名称",订阅特定的消息。一定订阅者订阅了消息,则只要发出消息,订阅者就会被触发。

消息中心模型

而在Vue中,通过v-on去订阅一个消息,通过emit发出一个消息。

这两个特有的模式是v-on:message-name="someMethod"订阅,this.$emit("message-name")发送一个消息。此时someMethod会被触发调用。

具体的实例

父组件和子组件的事件响应中,主要分为四种情况

  1. "v-on"/"@"绑定事件(@是对v-on的缩写)
  2. 绑定原生事件
  3. .sync同步父组件和子组件之间的props
  4. 兄弟组件进行通信

"v-on"或"@"绑定事件

父组件中模版的定义

<div>
    <h4>组件四-"v-on"绑定事件</h4>
    <span>{{sumOfTotal}}</span>
    <br />
    <!--'@'是'v-on:'监听器的简写-->
    <component-span-child-4 v-on:increment-total="incrementWithTotal"></component-span-child-4>
    <component-span-child-4 @increment-total="incrementWithTotal"></component-span-child-4>
    <component-span-child-4 @increment-total="incrementWithTotal"></component-span-child-4>
</div>

子组件的定义

Vue.component("component-span-child-4", {
    template: "<button v-on:click='incrementOfButtonCounter'>{{counter}}</button>",
    data: function() {
        return {
            counter: 0
        }
    },
    methods: {
        incrementOfButtonCounter: function() {
            this.counter = this.counter + 1;
            // post a notification of increment counter
            // 'increment-total' 相当于一个通知名称,在父组件中,会检测一个同名的通知名称
            this.$emit("increment-total");
        }
    }
})

子组件在点击事件触发的时候,会发送一个消息名称为"increment-total"的消息,而在父组件中,订阅了这个名称的消息。所以父组件可以响应子组件的通知

绑定原生事件

父组件中模版的定义

<div>
    <h4>组件五-绑定原生事件</h4>
    <span>{{nativeSumOfTotal}}</span>
    <br />
    <component-span-child-5 v-on:click.native="nativeDoThing"></component-span-child-5>
</div>

子组件的定义

Vue.component("component-span-child-5", {
    template: "<button>检测原生事件-点击</button>"
})

通过v-on:click.native="nativeDoThing"订阅原生的事件。这里没有emit关键字,可以理解为这个消息是原生组件发送出的,但是订阅还是通过v-on

.sync同步父组件和子组件之间的props

在一些情况下,我们可能会需要对一个 prop 进行『双向绑定』。当一个子组件改变了一个 prop 的值时,这个变化也会同步到父组件中所绑定的值。这很方便,但也会导致问题,因为它破坏了『单向数据流』的假设。

父组件中模版的定义

<div>
    <h4>组件六-.sync同步父组件和子组件之间的props</h4>
    父组件中的值: {{food}}
    <component-span-child-6 :food.sync=food></component-span-child-6>
    <component-span-child-6 v-bind:food.sync=food></component-span-child-6>
    <!--扩展之后的模版-->
    <component-span-child-6 v-bind:food=food v-on:update:food="val => food = val"></component-span-child-6>
</div>

子组件中的定义

Vue.component("component-span-child-6", {
    props: ["food"],
    template: "<div>{{selectedFood}}<button v-on:click='changeSelectedFood'>点击选择其他食物</button></div>",
    data: function() {
        return {
            selectedFood: this.food,
            foods: ["米饭", "水果", "青菜", "沙拉"]         
        }
    },
    methods: {
        changeSelectedFood: function() {
            var idx = this.foods.indexOf(this.selectedFood);
            if (idx == -1 || idx == this.foods.length - 1) {
                idx = 0;
            } else {
                idx += 1;
            }
            this.selectedFood = this.foods[idx];
            this.$emit('update:food', this.selectedFood);
        }
    }
})

通过父组件中国呢三种写法(功能都是一样的,只是由上而下,将模版扩展开写,以窥探.sync的作用),其实.sync其实会扩展出一个v-on:update:food订阅消息,并且在收到消息,进行了对原值的修改。
而在子组件中,依旧通过this.$emit('update:food')发送一个消息出来

这个就是.sync真正做了什么。

兄弟组件进行通信

两个不是父子组件的组件如何通信,可以定一个中间总线(中介的意思),通过中间中间总线订阅消息,中间总线发送消息,完成两个组件之间的通信。如下

父组件模版的定义

<div>
    <h4>组件七-兄弟组件进行通信</h4>
    <component-span-child-7 component-name="组件7"></component-span-child-7>
    <component-span-child-8 component-name="组件8"></component-span-child-8>
</div>

子组件的定义

var bus = new Vue();
Vue.component("component-span-child-7", {
    props: ["componentName"],
    template: "<div><span>{{componentName}}</span>:<span>{{counter}}</span></div>",
    data: function() {
        return {
            counter: 0
        }
    },
    mounted: function() {
        // 此处在monuted阶段监听'notificationFromPartner',需要用bind方法绑定当前的this,否则回调function中的this则是bus实例,而不是当前Vue的实例
        bus.$on("notificationFromPartner", function() {
            this.counter += 1;
        }.bind(this));
    }
})
Vue.component("component-span-child-8", {
    props: ["componentName"],
    template: "<button v-on:click='componentClickPushMessage'>{{componentName}}</button>",
    methods: {
        componentClickPushMessage: function() {
            bus.$emit("notificationFromPartner");
        }
    }
})

组件7在装载mounted后,通过bus.$on("notificationFromPartner", callbackFunction)订阅了notificationFromPartner消息,而在组件8中,通过bus.$emit("notificationFromPartner");发送出这个消息。则订阅者就可以响应消息。

总结

在学习Vue中,组件作为十分重要的一个组成部分,对组件的通信的理解也十分重要。对于组件间的事件反馈,应该先理解消息中心的设计模式,则能更快的理解其中的原理。则不用纠结一些语法特性比较奇怪。不用纠结为什么v-on要和emit匹配、为什么只需要v-on就可以监听原生native事件、为什么.sync可以实现props的同步等一系列问题。

整个博客使用的源代码-请点击

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容