vue组件间通讯方式

2,251 阅读3分钟
  1. props$emit 父组件向子组件传递数据是通过prop传递的,子组件传递数据给父组件是通过$emit触发事件来做到的
  2. .syncv-model的使用(父子组件数据同步)
  3. $parent,$children 多层级传递数据,智能组件封装
  4. $attrs(属性的集合)和$listeners(方法的集合)。Vue 2.4 开始提供了$attrs$listeners来解决A->B->C问题,组件间向下传递,可以不用 props注册。v-bind属性传递,v-on方法传递
  5. 父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量。
  6. $refs 获取实例
  7. envetBus 平级组件数据传递 这种情况下可以使用中央事件总线的方式

props传递数据

父组件

<template>
    <div>
        父组件:{{money}}
        <son1 :money='money' @addMoney = 'addMoney'></son1>
    </div>
</template>
<script>
import son1 from './son1'
export default {
    components:{
        son1
    },
    data(){
        return {
            money:100
        }
    },
    methods:{
        addMoney(val){
            this.money = val
        }
    }
}
</script>

子组件 son1.vue

<template>
    <div>
        儿子1获得{{money}}
        <button @click="raiseMoney">多给点嘛</button>
    </div>
</template>
<script>
export default {
    props:{
        money:{
            type:Number,
            default:1
        }  
    },
    methods:{
        raiseMoney(){
            this.$emit('addMoney',200)
        }
    }
}
</script>

.sync 和 v-model的使用(父子组件数据同步)

.sync

  • 需求需要对一个 prop 进行“双向绑定”。不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以修改父组件,且在父组件和子组件都没有明显的改动来源。
  • update:myPropName 的模式触发事件取而代之
  • 父组件 <son1 :money.sync='money'></son1>
    子组件 this.$emit('update:money',200)
    是对下面代码的简写(语法糖)
    父组件 <son1 :money="money" @update:money="(val)=>this.money=val"></son1>
    子组件this.$emit('update:money',200)

v-model

  • <son1 :value="money" @input="(val)=>this.money=val></son1>
  • 也可以写成<son1 v-model="money"></son1>
  • v-model这种方法,子组件只能接收value,具有局限性,而.sync则可以随意起名字

多层级传递数据 $parent $children

  • 孙子组件直接更改父组件<button @click="$parent.$emit('addMoney',400)">更改父组件</button>
    像这样如果有多级孙子组件,就会一直$parent下去,这样会很麻烦,不如直接封装一个$dispatch
    $dispatch只会通知自己对父亲, 而eventBus是全局通知,通知所有的父亲或者儿子
  • 在main.vue里封装dispatch

向上通知

Vue.prototype.$dispatch = function (eventName,value){
    let parent = this.$parent;
    while(parent){
        parent.$emit(eventName,value);
        parent = parent.$parent
    }
}

使用时只需要 this.$dispatch('addMoney',400)
//666 亲测无误

向下传递

  • 在main.js里封装$broadcast
Vue.prototype.$broadcast = function (eventName,value){
    //获取当前组件下的所以孩子
    const broadcast = (children) => {
        children.forEach(child => {
            child.$emit(eventName,value);
            if(child.$children){
                broadcast(child.$children)
            }
        })
    }
    broadcast(this.$children)
}

使用时只需要 在父级 this.$broadcast('say',1111) 只要子组件中有绑say的都给我说1111

在son1.vue中        
    <Grandson :money = money @say='say1'></Grandson>

    methods:{
        say1(val){
            console.log("我很帅"+val)
        }
    }
<!--亲测控制台打印 我很帅1111-->

$attrs属性的集合

  • $attrs代表上级传过来的所有属性,当父组件给子组件需要穿过来很多值时,用props一个一个接收岂不是很累,使用$attrs就可以一次性把父级传过来当所有值接收到,完美~
  • 直接用$attrs接收完数据,打开控制台,你会发现如下情况,属性全部挂载在了dom上,我们并不想这样
    解决:在使用$attrs的子组件上,添加inheritAttrs:false,代码如下
<!--父组件-->
<son2 name="奔跑" address="北京"></son2>

<!--子组件-->
template>
    <div>
        <!-- $attrs代表上级传过来的所有属性 -->
        儿子2: {{$attrs}}
    </div>
</template>
<script>
export default {
    inheritAttrs:false
}
</script>
<!--页面显示:儿子2: { "name": "奔跑", "address": "北京" }-->
  • 如何子组件把获取到的数据再一次性传给孙子组件,只需要这么做:
    <grandson2 v-bind="$attrs"></grandson2>
    孙子组件接收时同样使用$attrs即可获得所有的数据

$listeners方法的集合

  • 父级在子组件绑定的方法,在子组件里都可以用$listeners来调用这些方法
  • 如果子组件不使用这些方法,孙子组件使用,则可以使用v-on="$listeners"来传递给孙子组件这些方法,孙子组件使用时,同样使用$listeners
<!--父组件-->
<son2 name="奔跑" address="北京" @look="console.log('look')"></son2>

<!--子组件-->
<grandson2 v-bind="$attrs" v-on="$listeners"></grandson2>

<!--孙子组件-->
<template>
    <div>
        孙子2:{{$attrs}}
        <button @click="$listeners.look()">看</button>
    </div>
</template>

<!--在孙子组件上点击按钮'看',则会在控制台打印'look'-->

provide提供变量,inject注册变量

  • 问题:导航栏的地址发生改变但是页面却不刷新 (用vue-router路由到当前页面,页面是不进行刷新的),例如下面的搜索功能

解决:

  • 1、this.$router.go(0);
  • 2、location.reload();
    上面两种方法会出现闪屏的问题,用户体验不好
    如何产品同意,我更喜欢window.open一下
  • 3、终极解决,哈哈哈
    在App.vue,声明reload方法,控制router-view的显示或隐藏,从而控制页面的再次加载。(provide /inject )

      【祖先组件(provide )向其所有子孙后代(inject )注入一个依赖】

  • app.vue中
    1.data中声明变量 2.绑定v-if 3.方法里写逻辑 4.向后代注入依赖

在需要的页面 调用方法
inject接受app传递的方法

$refs获取实例

  • ref被用来给元素或者子组件注册引用信息。引用信息将会注册在父组件的$refs对象上。
    如果在普通的Dom元素上使用,引用指向的就是DOM元素;
    如果用在子组件上,引用就指向组件实例,父级就可使用子组件上的方法,通过$refs
  • $refs相对document.getElementById的方法,会减少获取dom节点的消耗。
  • $refs只在组件渲染完成后才填充,并且它是非响应式当。它仅仅是一个直接操作子组件的应急方案---应当避免在模版或者计算属性中使用$refs
<!--父级-->
<son2 name="奔跑" address="北京" ref="son2"></son2>

    mounted(){
        this.$refs.son2.sleep()
    },
    
<!--son2子组件-->
    methods:{
        sleep(){
            console.log('我想睡觉')
        }
    }

跨组件通信 eventBus

  • eventBus定义到了全局上,通过$on(eventName,function)注册事件,通过$emit(eventName,参数)获取使用事件
<!--在mian.js上注册bus-->
Vue.prototype.$bus = new Vue();

<!--在恰当的页面 注册事件-->
    this.$bus.$on('有人找我了',function(val){
        console.log('呵呵'+val)
    })
<!--在恰当的页面 使用事件-->
    this.$nextTick(()=>{
        this.$bus.$emit('有人找我了','xxx')
    })
<!--控制台打印-->
    呵呵xxx
  • this.$nextTick的用途
    new Premise().then(()=>{
    //等待所有代码都同步完以后再执行
    })