vue 组件通讯

组件之间的通讯简述:
父子组件的关系可以总结为 props 向下传递,事件event向上传递
祖先组件和后代组件(跨多代)的数据传递,可以使用provide和inject来实现
跨组件或者兄弟组件之间的通信,可以通过eventBus或者vuex等方式来实现

  1. 页面级组件传值
    • 通过路由带参数进行传值
    • 通过设置 sessionStorage缓存的形式进行传递
  2. 父子组件传值
    • 父传子props,子传父$emit
      • .sync
      • v-model
    • $parent$children
  3. 后代组件传值
    • provide / inject
    • $attrs$listeners
  4. 跨组件或者兄弟组件之间传值
    • 小项目少页面用eventBus
    • 大项目多页面使用 vuex

页面级组件传值

1. 通过路由带参数进行传值

//两个组件 A和B,A组件通过query把orderId传递给B组件(触发事件可以是点击事件、钩子函数等)
this.$router.push({ path: '/f4', query: { orderId: 123 } }) //跳转到f4
//在B组件中获取A组件传递过来的参数
this.$route.query.orderId
<router-link to="/f4?orderId=123">router-link 进入Father4</router-link>
<router-view></router-view>

2. 通过设置 Session Storage缓存的形式进行传递

//两个组件A和B,在A组件中设置缓存orderData
const orderData = { 'orderId': 123, 'price': 88 }
sessionStorage.setItem('orderInfo', JSON.stringify(orderData))

//B组件就可以获取在A中设置的缓存了
const orderInfo = JSON.parse(sessionStorage.getItem('orderInfo'))

父子组件传值

1. 父传子props,子传父$emit

  • .sync
//父组件
<template>
    <div>
        父组件:{{title}}
        <childDom :title.sync="title"></childDom>
    </div>
</template>

<script>
import childDom from '../components/Father3-Child'
export default {
    data(){
        return {
            title:'简单我的简单'
        }
    },
    components:{childDom}
}
</script>
//子组件
<template>
    <div>
        <div>子组件:{{title}}</div>
        <button @click="$emit('update:title','哈哈哈')">点我更新父组件的数据</button>
    </div>
</template>

<script>
export default {
    props:['title'],
}
</script>
  • v-model
//父组件
<template>
  <div>
    <aa class="abc" v-model="test" ></aa> 
    {{'外面的值:' + test}}
    <button @click="fn">外面改变里面</button> 
  </div>
</template>

<script>
  import aa from './aa'
  export default {
    data () {
      return {
        test: ''
      }
    },
    methods: {
      fn () {
        this.test += 1 
      }
    },
    components:{
      aa
    }
  }
</script>
//子组件
<template>
  <div>
    {{'里面的值:'+ msg}}
    <button @click="fn2">里面改变外面</button>
  </div>
</template>

<script>
  export default {
    /**
     * 使用model, 这里有2个属性
     * prop属性说,父组件的v-model的值就是msg
     * event说,我emit ‘cc’ 的时候,父组件v-model的值就是参数的值
     */
    model: {
      prop: 'msg',
      event: 'cc'
    },
    props: {
      msg: ''
    },
    methods: {
      fn2 () {
        this.$emit('cc', this.msg+2)
      }
    }
  }
</script>

2. $parent$children

在组件内部可以直接通过子组件$parent对父组件进行操作,父组件通过$children对子组件进行操作.

//父组件
<template>
    <div>
        <p>fatherMessage:{{fatherMessage}}</p>
        <button @click="changeChildValue">改变子组件的数据childMessage</button >
        <child></child>
        <child2></child2>
    </div>
</template>

<script>
import child from '../components/Father4-Child'
import child2 from '../components/Father4-Child2'
export default {
    data(){
      return {
        fatherMessage:'hello'
      }
    },
    components:{child,child2},
    methods:{
        changeChildValue(){
            this.$children[0].childMessage = 'hello';
            this.$children[1].childMessage = 'hello2';
        }
    }
}
</script>
//子组件1
<template>
    <div class="container">
        child
        <input type="text" v-model="childMessage" @input="changeValue">
    </div>
</template>

<script>
export default {
    // props:{
    //   value:String, //v-model会自动传递一个字段为value的prop属性
    // },
    data(){
      return {
        childMessage:this.value
      }
    },
    methods:{
      changeValue(){
        this.$parent.fatherMessage = this.childMessage;//通过如此调用可以改变父组件的值
      }
    },
}
</script>
//子组件2
<template>
    <div class="container">
        child2
        <input type="text" v-model="childMessage" @input="changeValue">
    </div>
</template>

<script>
export default {
    // props:{
    //   value:String, //v-model会自动传递一个字段为value的prop属性
    // },
    data(){
      return {
        childMessage:this.value
      }
    },
    methods:{
      changeValue(){
        this.$parent.fatherMessage = this.childMessage;//通过如此调用可以改变父组件的值
      }
    },
}
</script>

后代组件传值(父组件与子、孙子、曾孙子组件传值)

1. provide / inject

provide / inject 可以以一个祖先组件向所有子孙后代注入依赖。
简单的来说就是在父组件中通过provide来提供变量,然后在后代组件中通过inject来注入变量,不仅限于prop的父子组件数据交互,只要在上一层级的声明的provide,那么下一层级无论多深都能够通过inject来访问到provide的数据

//父组件
<template>
    <div>
        <son></son>
    </div>
</template>

<script>
import son from '../components/son';
export default {
    provide: {
        foo: 'bar'
    },
    components:{son}
}
</script>
//子组件
<template>
    <div>
        我是子组件{{foo}}
        <grandson></grandson>
    </div>
</template>

<script>
import grandson from '../components/grandson'
export default {
    inject: ['foo'],
    components:{grandson}
}
</script>
//孙子组件
<template>
    <div>
        我是孙子组件{{foo}}
    </div>
</template>

<script>
export default {
    inject: ['foo'],
}
</script>
通过 provide / inject 后代组件都可以访问父组件的数据

2. $attrs$listeners

通过$attrs属性将父组件的数据(不作为 prop 被识别的数据)传递给子组件、孙子组件、曾孙子组件等后代组件

$listeners属性是一个对象,里面包含了作用在这个组件上的所有监听器,我们在子组件上绑定v-on=”$listeners”, 就可以在父组件中监听孙子组件、曾孙子组件触发的事件,就能把孙子组件、曾孙子组件等后代组件发出的数据,传递给父组件。

//父组件
<template>
 <div>
    {{coo}}
   <child-dom
    :foo="foo"
    :coo="coo"
     @upRocket="reciveRocket">
   </child-dom>
 </div>
</template>
<script>
 import childDom from "../components/Father2-Child";
 export default {
   name:'demoNo',
   data() {
        return {
            foo:"Hello, world",
            coo:"Hello,rui"
        }
    },
    components:{childDom},
    methods:{
        reciveRocket(ev){
            this.coo = ev;//改变数据
            console.log(this.coo)
        }
    }
}
</script>
//子组件
<template>
    <div>
        <p>foo:{{foo}}</p>
        <p>attrs:{{$attrs}}</p>
        <childDomChild v-bind="$attrs" v-on="$listeners"></childDomChild>
    </div>
</template>
<script>
import childDomChild from '../components/Father2-Child-Child';
export default {
    props:["foo"],
    components:{childDomChild}
}
</script>
//孙子组件
<template> 
    <div>
    <p>coo:{{$attrs.coo}}</p>
    <button @click="startUpRocket">我要发射火箭</button>
    </div>
</template>
<script>
 export default {
    methods:{
        startUpRocket(){
            this.$emit("upRocket",'简单我的简单');
        }      
    }
 }
</script>

跨组件或者兄弟组件之间传值

小项目少页面用eventBus

总结:
EventBus 又称为事件总线,在Vue中可以使用 EventBus 来作为沟通桥梁,就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件,所有组件都可以上下平行地通知其他组件
在需要传值的组件中用eventBus.$emit触发一个自定义事件,并传递参数
在需要接收数据的组件中用eventBus.$on监听自定义事件,并在回调函数中处理传递过来的参数
EventBus原理是发布订阅
eventBus容易导致命名冲突,所以大项目要用vuex

<template>
    <div>
        <brother1></brother1>
        <brother2></brother2>
    </div>
</template>

<script>
import {eventBus} from '../components/eventBus.js';
let brother1 = {
    template:'<div>{{color}} <button @click="fnEmit2">变红</button></div>',
    data(){
        return {color:'红色',old:'红色'}
    },
    created(){
        eventBus.$on('changeColor1',(val)=>{ // 绑定自定义事件
            this.color = val;
        })
    },
    methods:{
        fnEmit2(){
            eventBus.$emit('changeColor2',this.old) // 触发自定义事件
        }
    }
}
let brother2 = {
    template:'<div>{{color}} <button @click="fnEmit1">变绿</button></div>',
    data(){
        return {color:'绿色',old:'绿色'}
    },
    created(){
        eventBus.$on('changeColor2',(val)=>{
            this.color = val;
        })
    },
    methods:{
        fnEmit1(){
            eventBus.$emit('changeColor1',this.old)
        }
    }
}

export default {
    components:{
        brother1,brother2
    }
}

</script>
//eventBus.js
import Vue from 'vue';
export const eventBus = new Vue();

大项目多页面使用 vuex

vuex主要借鉴了flux redux,vuex只能在vue中使用(单独为vue开发的)
vuex为大型项目而生,主要是(状态)管理,状态指的是数据,将数据统一管理
每一个vuex应用的核心是store,store是一个容器
不能直接改变store中的状态,通过commit一个mutation来更改状态

vuex计数器项目架构图
//main.js
import Vue from 'vue';
import App from './App.vue';
import store from './store'

//计数器
new Vue({
    el:'#app',
    ...App,
    store, //store被注册到了实例上 所有组件都会有this.$store这个属性
})
//store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
import logger from 'vuex/dist/logger'; //logger是一个日志插件
Vue.use(Vuex);
//容器是唯一的 不可以更改 所以用const比较好 
const state={count:0}; 
const getters={ //相当于computed
    val:(state)=>state.count%2 ? '奇数' : '偶数'
};
import mutations from './mutations';
export default new Vuex.Store({
    state,
    mutations,
    getters, 
    plugins:[logger()],
    strict:true //严格模式 只能通过mutation(管理员)来更改状态,mutation不支持异步
})
//App.vue
<template>
    <div>
        <Counter></Counter>
    </div>
</template>

<script>
import Counter from './components/Counter'
export default {
    data(){
        return {
            msg:'hello',
        }
    },
    components:{
        Counter
    }
}
</script>
//Counter.vue
 <template>
     <div>
         计数器:<button @click="reduce">-</button>
         <br>
         当前:{{$store.state.count}}<br>
         计数器:<button @click="add">+</button><br>
         {{$store.getters.val}}
     </div>
 </template>
 
 <script>
 import * as Types from '../store/mutations-types'; 
 export default {
    data(){
        return {
            msg:'hello',
        }
    },
    methods:{
        add(){ 
            this.$store.commit(Types.INCREMENT,2); //提交add的mutation, 2是载荷payload
        },
        reduce(){
            this.$store.commit(Types.DECREMENT,1);
        }
    }
 }
 </script>
// store/mutations.js
import * as Types from './mutations-types';
const mutations={
    [Types.INCREMENT](state,count){ //state是自动放入的,默认指的就是当前的state
        state.count+=count;
    },
    [Types.DECREMENT](state,count){
        state.count-=count;
    }
};
export default mutations;
// store/mutations-types.js
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容

  • 有三种,分别是1、父组件向子组件传值;2、子组件向父组件传值;3、非父子组件传值; 父向子传值:父组件定义一个属性...
    hudaren阅读 153评论 0 0
  • 前言 组件是 vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引...
    用技术改变世界阅读 2,159评论 1 3
  • 组件是vue最主要的语法特性之一,组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素,...
    前端一菜鸟阅读 316评论 0 4
  • 父子通讯 父组件向子组件通讯(单向数据流传递,子组件不应该改变父组件里面数据的值),父组件代码如下 父组件在调用子...
    Hachiman阅读 265评论 0 1
  • 摘要: 总有一款合适的通信方式。 作者:浪里行舟 Fundebug经授权转载,版权归原作者所有。 前言 组件是 v...
    Fundebug阅读 15,574评论 3 57