vue2.0 组件之间通信

概念

双向数据流:
父组件数据能传递到子组件,子组件接收数据,进行展示;此外子组件可以修改父组件中的数据

双向数据绑定的劣势:
它破环了单向数据流的简洁性, 这增加了分析数据时的难度
不要在子组件中,通过修改引用类型props中的数据,达到“父子组件数据同步”的需求!
这样会让数据流变得更加难以分析

父子之间通信(主要数据逻辑在父级完成)

父->子 : 组件标签上绑定自定义属性,挂载数据,子页面通过props获取
子->父:组件标签上绑定这个自定义事件,子组件中通过$emit('自定义事件'),触发自定义事件

组件通信方式很多


组件数据通信

实例如下:

1.父级中

<div>
  <h6>父级内容,子级在当前组件中</h6>
  <Mytag 
    @changemesfn2="changemesfn2handle" 
    :parentdata="data1" 
  />
</div>

//父级数据
data: function() {
    return {
        data1: '挂载在 组件<Mytag >上的数据 '
    }
},
methods: {
    changemesfn2handle: function(val) {
       console.log(val); //新的数据
       this.data1 = '挂载在 组件<Mytag >上的数据  被改变了~~~~~~~~~~~~~~~~ ';
    }
}

2.子组件中

<div>
  <p>我是子组件Mytag
    <span>{{parentdata}}</span>
  </p>
  <a 
    @click="changeMes2" 
  href="javascript:void(0);">按钮</a>
</div>

props: {
    parentdata: {
        type: String,
    default:
        '默认值'
    }
},
methods: {
    changeMes2: function() {
        this.$emit("changemesfn2",'新的数据');
    }
}
父子之间通信(主要数据逻辑在子级完成)

鉴于组件复用的原理,数据逻辑应该在父级来做操作

1.父级中

<div>
  <h6>子组件直接修改父级</h6>
  <!--写法1-->
  <Mytag2 
    v-bind:fathermes="yourdata" 
    v-on:update:fathermes="yourdata=$event" 
  />
  <!--写法2-->
  <Mytag2 
    v-bind:fathermes="yourdata" 
    v-on:update:fathermes="val => yourdata = val" 
  />
  <!--写法3-->
  <Mytag2
    :fathermes.sync="yourdata"
  />
</div>

data: function() {
    return {
        yourdata:'挂载在 组件<Mytag2 >上的数据'
    }
}

2.子组件中

<div>
  <p>我是子组件Mytag2<br />
    {{ fathermes }}
  </p>
  <a 
    @click="changesyncfn" 
  href="javascript:void(0);">按钮</a>
</div>

props: {
    fathermes: {
        type: String,
        default: 'hello'
    }
},
methods: {
    changesyncfn: function() {
        this.$emit('update:fathermes', '数据改变了~~~~~~');
    }
}
父子之间通信(子组件在router-view中)

当子元素为router-view中的组件时候

1.父级

<p> {{msg}} </p>
<transition :name="runway">
  <router-view 
    @changemesfn="changemeshandle"
  ></router-view>
</transition>

methods: {
    changemeshandle: function() {
        this.msg = "挂载在<router-view>的数据 从(子)改变(父)~~~~~~~~~~~~~~~~~~~~~~";
    }
}

2.子级router-view

<a href="javascript:void(0)" 
  @click="changeMes"
>改变父级内容</a>

methods: {
    changeMes: function() {
        this.$emit("changemesfn");
    }
}

父子之间通信(通过v-model的方式传递数据)

v-model是 Vue的一个语法糖,在表单中,主要表现为:
在输入框、文本域中,v-bind:value="数据",v-on:input="事件处理"
在复选框,单选框中,v-bind:checked="布尔值",v-on:change="事件处理"

所以我们也可以借助v-model实现组件的数据传递
1.父组件内

<Page1
   v-model="msg1"
 />

data: function() {
    return {
        msg1: '我是父级数据~~~~~~~~'
    }
}

2.子组件

<a href="javascript:void(0);" 
  @click="fn1"
>按钮</a>

...
props: {
    value: {  //通过value接收数据
        type: String,
        default:"默认值"
    }
},
methods: {
    fn1: function() {
        this.$emit('input', '数据变化了');  //通过emit发送事件
    }
}
组件内快速数据传递

方式1原理:通过根实例作为媒介
vm.on( event, callback )监听当前实例上的自定义事件,callback中的参数为emit中的第二个参数
事件可以由vm.$emit触发

通过根实例$emit,$on实现快速通信
1.借助Vue的$root找到根组件上的Vue实例对象
2.通过$root.$emit发布事件,通过$root.$on接收事件
借助根实例

例:
组件1 输入框,添加内容,发送事件
组件2 接收内容,渲染到ul-li列表

1.组件1 (发送新增的内容)

<label>
  <input type="text" ref="input1" value="" />
  <a href="javascript:void(0);" 
    @click="addlist"
  >添加列表</a>
</label>

methods: {
    addlist: function() {
        var _val = this.$refs.input1.value;
        this.$root.$emit('addlistfn', _val);
    }
}

2.组件2(接收内容,渲染到页面)

<h6>列表:</h6>
<ul class="lists">
  <li v-for="item in arr">{{item}}</li>
</ul>

data: function() {
    return {
        arr: []
    }
},
created() {
    var that = this;
    this.$root.$on('addlistfn',
    function(val) {
        that.arr.push(val);
        console.log(this) //$parent undefined 根节点的Vue实例
        console.log(this.$root == this)  //true

        console.log(that) //$parent 存在 //当前组件页面的Vue实例        
    });
}

上面实现的方法是借助于根实例,
另外,也可以通过在入口文件中,对根实例添加插件,Vue原型上增加$eventBus属性,值为new Vue()
使用新的Vue实例来做
在main.js文件中

Vue.use({
  install(Vue){
    Vue.prototype.$eventBus = new Vue();
  }
})

事件总线(event bus)

将上面实现方式中的 $root 替换成 $eventBus 依然是可行的

这种方式的不足
1.操作事件总线中的组件越多,后续调试越麻烦
2.结果没有规则,不可调试

父组件获取子组件中数据 单向数据流

将ref挂载子组件上,父级通过$refs可以查找对应的组件,和上面的数据

ref 被用来给元素或子组件注册引用信息

1.引用信息将会注册在父组件的 $refs 对象上
2.如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素
3.如果用在子组件上,引用就指向组件实例:
因为 ref 本身是作为渲染结果被创建的,在初始渲染的时候你不能访问它们 - 它们还不存在!$refs 也不是响应式的

父组件中

<button @click="getsondata">点击我,可以获取子组建数据</button>
 是"{{mess4sondata}}"
<Page4 :p4val="mess4" ref="sonpage4" />

data: function() {
    return {
        mess4sondata: ''
    }
},
methods: {
    getsondata: function() {
        if (this.$refs.sonpage4.sondata) {
            this.mess4sondata = this.$refs.sonpage4.sondata
        };
    }
}

子组件中

<p>子页面的数据:{{sondata}}</p>

data: function() {
    return {
        sondata: '我是子页面数据'
    }
}
祖先传递孙子组件 (provide/inject 单向数据流)

祖辈们传递到孙子辈组件中

provide/inject这对选项需要一起使用,
以允许一个祖先组件向其所有子孙后代注入一个依赖,
不论组件层次有多深,并在起上下游关系成立的时间里始终生效

组先

export default {
        name: "new",
        provide: {
            foo: 'hi'
        }
}

孙子辈们组件

{{foo}}

export default {
        name: "son1",
        inject: {
            foo: {
                type: String,
                default:'默认'
            }
        }
 }
单层父子通信

父级获取子级的属性以及方法

this.$refs.(子级上挂载的ref名).(属性名)
this.$refs.(子级上挂载的ref名).(方法名)

子级获取父级的属性以及方法

this.$parent.(属性名);
this.$parent.(方法名);

多层获取,可以通过不断寻找父级方式,进行封装,如下:

后代传递到祖先(单向数据流)

通过封装函数的方式,核心语法是:

$emit('自定义事件','数据');
$parent
$on('自定义事件',callback);

祖先 App.vue 根实例

created() {
    this.$on('aaa', function(val) {
        console.log(val);  //haha
    })
}

祖孙组件

<button @click="sendit">发送</button>

data: function() {
    return {
        sondata1: 'haha'
    }
},
methods: {
    sendit: function() {

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