十多种Vue组件间通信,你知道几个?

Vue组件间通信完整篇

本文提到的Vue组件间通信前六种通常是大家熟知的,后面的几种是组件间通信的高级使用

一、props

使用场景:父子通信

1. 传递数据类型:函数

  • 实质子组件想给父亲传递数据

    • 在父组件定义好带参函数hander,通过props传递个子组件

      <TodoList updateChecked="hander" />
      
    • 在子组件通过props接收函数:

      • props:['hander']
    • 在需要传递参数给父组件的地方使用函数并携带参数过去即可

      this.hander(params)
      

2. 传递数据类型:非函数

  • 实质就是父亲给子组件传递数据

    • 父组件中传递数据给子组件

      <TodoList :age="123" name='xzq' />
      
    • 子组件接收数据

      • props: ["name", "age"]

      • props: {
            name: String,
            age: Number,
        },
        
      • props: {
            name: {
                type: String,
                required: true, // 必传项
            },
            age: {
                type: Number,
                default: 18, // 默认值
            }
        },
        
  • 注意:当父组件中传递了props时,而子组件没有使用props接收参数,则未被接收的参数会在组件实例的$attr

3. 特殊情况:路由传递props

  • 布尔值类型,把路由中params参数映射为组件props数据

  • 对象,静态数据,很少用

  • 函数,可以把路由中paramsquery参数映射为组件props数据

二、自定义事件

使用场景:子给父传递数据

1. 在父组件中为子组件绑定自定义事件

  • 使用@符号,即v-on指令绑定自定义事件

    <Student v-on:xzq="getStudentName" />
    <Student @xzq="getStudentName" @demo="m1" />
    
  • 使用ref绑定自定义事件

    • 父组件中为子组件打上ref标签

      <!-- .native:指定click事件是原生的事件 -->
      <Student ref="student" @click.native="show" />
      
    • 在父组件的mounted生命周期中,使用$on绑定自定义事件

      this.$refs.student.$on("xzq", (name, ...params) => {···}
      

2.子组件中触发自定义事件并传递参数

  • 在需要传递数据给父组件的地方,通过$emit触发自定义事件

    this.$emit("xzq", this.name, 666, 999, 888);
    

3. 卸载自定义事件

  • 卸载自定义事件

    在组件销毁前(beforeDestroy)使用$off卸载自定义事件

    this.$off("xzq"); //解绑一个自定义事件
    

三 、$bus全局事件总线

使用场景:任意组件间通讯都适用

1. 原理

  • 组件实例(vc)的原型(__proto__)的原型(__proto__)是Vue的原型对象(Vue.prototype)

    • 所有的组件对象都必须能看见他
      • $bus挂载到Vue.prototype上,所以组件都可以访问到$bus
    • 这个对象必须能够使用$on $emit $off 方法去绑定、触发和解绑事件
      • Vue实例(vm)

    在这里插入图片描述

2. 定义全局事件总线

  • main.js中,定义全局事件总线

    beforeCreate() {
    	Vue.prototype.$bus = this //最标准的安装全局事件总线
    },
    

3. 使用全局事件总线

  • 提供数据:this.$bus.$emit('xxx',data)

  • 接收数据:A组件想接收数据,则在A组件中给$bus使用$on绑定自定义事件,事件的回调留在A组件上

    this.$bus.$on('xxx',(data)=>{···})
    

4. 解绑当前组件所用到的事件

  • 使用$off

    this.$bus.$off("xxx");

四、pubsub-js【发布订阅消息】

在vue中根本不用,React常用

使用场合:任意组件间通讯都适用

1. 安装pubsub-js

npm i pubsub-js

2. 引入

import pubsub from 'pubsub-js'

3. 提供数据(发布消息)

pubsub.publish('xxx',data)

4. 接收数据

接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身

this.pid = pubsub.subscribe('xxx',(data)=>{···})

5. 在beforeDestroy中取消订阅

pubsub.unsubscribe(this.pid)

五、Vuex

使用场景:任意组件间通讯均适用

比较适合的场景:多个组件依赖于同一状态,来自不同组件的行为需要变更同一状态

1. 工作原理

  • state:存储数据
  • actions:响应组件的动作,可以进行异步操作,如发送AJAX请求
  • mutations:用于操作state中的数据
  • getters:相当于计算属性,根据的是state中的数据

在这里插入图片描述

2. 搭建Vuex环境

在这里插入图片描述

  • 下载安装vuex:npm i vuex
  • 通常在src目录下建立store文件夹用于存储vuex相关对的文件

3. 非模块化Vuex

  • index.js形式
import Vue from 'vue'
//引入vuex
import Vuex from 'vuex'
// 使用插件
Vue.use(Vuex)

const actions = {
    func(context, value) {
        context.commit('FUNC',value)
    },
}

const mutations = {
    FUNC(state, value) {
        state.value = value
    },
}

//state - 用于存储数据
const state = {
    value: '',
}

//准备getters---用于将state中的数据进行加工
const getters = {
    bigValue(state) {
        return state.value * 10
    }
}

//创建store并暴露
export default new Vuex.Store({
    actions: actions,
    //重名触发简写
    mutations,
    state,
    getters
})
  • Vuex的基本使用

    • 组件中读取state中的数据的方式

      🌈 方式一:直接读取

      this.$store.state.value

      🌈 方式二:mapState

      ​ 📃 数组形式:...mapState(['value'])

      ​ 📃 字符串对象形式:...mapState({value:'value'})

      ​ 📃 函数对象形式:...mapState({ value:state => state.value })

    • 组件中读取getters中的数据

      🌈 方式一:直接读取

      this.$store.getters.bigValue

      🌈 方式二:mapGetters (不支持函数对象形式)

      ​ 📃 数组方式:...mapGetters(['bigValue'])

      ​ 📃 字符串对象形式:...mapGetters({bigValue:'bigValue'})

    • 组件中使用actions中的方法

      🌈 方式一:dispatch

      this.$store.dispatch('func',value)

      🌈 方式二:mapActions

      ​ 📃 数组方式:...mapActions(['func'])

      ​ 📃 字符串对象形式:...mapActions({func:'func'})

      ​ 📃 函数形式:...mapActions({func(dispatch,value){dispatch('func',value)}})

    • 组件中使用mutations中的方法

      🌈 方式一:commit

      this.$store.commit('FUNC',value)

      🌈 方式二:mapMutations

      ​ 📃 数组方式:...mapMutations(['FUNC'])

      ​ 📃 字符串对象形式:...mapMutations({FUNC:'FUNC'})

    • 若没有网络请求或其他业务逻辑,组件中也可越过actions,直接mutations,即不写dispatch,直接编写commit

4. Vuex模块化+命名空间

这样做让代码更好维护,让多种数据分类明确

  • 为了解决不同模块命名冲突的问题,将不同模块的namespaced: true

  • index.js如下

    import Vue from 'vue'
    import Vuex from 'vuex'
    // 使用插件
    Vue.use(Vuex)
    
    const countAbout = {
        namespaced: true,
        actions: {
            func(context, value) {
            	context.commit('FUNC',value)
        	},
        },
        mutations: {
            FUNC(state, value) {
            	state.value = value
        	},
        },
        state: {
            value: '',
        },
        getters: {
        	bigValue(state) {
            	return state.value * 10
        	}
        }
    }
    const personAbout = {
        namespaced: true,
        actions: {···},
        mutations: {···},
        state: {
            value: '',
        },
        getters: {···}
    }
    //创建store并暴露
    export default new Vuex.Store({
        modules: {
            countAbout:countAbout,
            personAbout
        }
    })
    
  • 组件中读取state中的数据的方式

    🌈 方式一:直接读取

    this.$store.state.personAbout.value

    🌈 方式二:mapState

    ​ 📃 数组形式:...mapState('personAbout',['value'])

    ​ 📃 字符串对象形式:...mapState('personAbout',{value:'value'})

    ​ 📃 函数对象形式:...mapState('personAbout',{ value:state => state.value })

  • 组件中读取getters中的数据

    🌈 方式一:直接读取

    this.$store.getters['personAbout/bigValue']

    🌈 方式二:mapGetters (不支持函数对象形式)

    ​ 📃 数组方式:...mapGetters('personAbout',['bigValue'])

    ​ 📃 字符串对象形式:...mapGetters('personAbout',{bigValue:'bigValue'})

  • 组件中使用actions中的方法

    🌈 方式一:dispatch

    this.$store.dispatch('personAbout/func',value)

    🌈 方式二:mapActions

    ​ 📃 数组方式:...mapActions('personAbout',['func'])

    ​ 📃 字符串对象形式:...mapActions('personAbout',{func:'func'})

    ​ 📃 函数形式:

    ...mapActions('personAbout',{func(dispatch,value){dispatch('func',value)}})
    
  • 组件中使用mutations中的方法

    🌈 方式一:commit

    this.$store.commit('personAbout/FUNC',value)

    🌈 方式二:mapMutations

    ​ 📃 数组方式:...mapMutations('personAbout',['FUNC'])

    ​ 📃 字符串对象形式:...mapMutations('personAbout',{FUNC:'FUNC'})

5. main.js中挂在store

//创建vm
new Vue({
    el: '#app',
    //触发对象简写模式
    store,
    render: h => h(App),
    beforeCreate() {
        Vue.prototype.$bus = this
        // console.log(this)
    },
});

六、插槽slot

使用场景:父子通讯,通常用于传递结构

让父组件可以**向子组件指定位置插入html结构**,也是一种组件间通信的方式

1. 默认插槽

  • 父组件中

    <Category>
    	<div>html结构</div>
    </Category>
    
  • 子组件中

    <template>
      <div>
        <!-- 定义的一个插槽  父组件传递过来的结构将在下面呈现 -->
        <slot></slot>
      </div>
    </template>
    

2. 具名插槽

  • 父组件中

    父组件指明放入子组件的哪个插槽 slot=“footer”,如果是template可以写成v-slot:footer

    <Category>
        <template slot="center">
            <div>html结构1</div>
        </template>
        <template v-slot:footer>
            <div>html结构2</div>
        </template>
    </Category>
    
  • 子组件中

    <template>
        <div>
            <! -- 定义插槽-->
            <slot name="center">插槽默认认内容...</slot>
            <slot name="footer">插槽默认认内容...</slot>
        </div>
    </template>
    

3. 作用域插槽

  • scope:用于父组件往子组件插槽,放的html结构接收子组件的数据
  • 理解:数据在子组件,但根据==数据生成的结构需要组件的使用者(父组件)==来决定
    ( games 数据在Category 组件中,但使用数据所遍历出来的结构由App 组件决定)
  • 父组件中

    <Category>
        <template scope="scopeData">
        	<!-- 生成的是ul列表 -->
            <ul>
        		<li v-for="g in scopeData.games" :key="g">{{g}}</li>
            </ul>
        </template>
    </Category>
    <Category>
        <template slot-scope="scopeData">
        	<!-- 生成的是h4标题 -->
        	<h4 v-for="g in scopeData.games" :key="g">{{g}}</h4>
        </template>
    </Category>
    
  • 子组件中

    <template>
    	<div>
            <!-- 数据在子组件中,注意此处不是props,是子组件向父组件传递数据 -->
    		<slot :games="games" ></slot>
        </div>
    </template>
    
  • 注意点:<slot :games="games" ></slot>中,games不是传递的props,是传递给父组件的scope可以使用scopeData.games获取

七、v-model *

我们都知道v-model,通常是用来收集表单数据,下面将介绍如何用来实现组件通信

使用场景:用于实现父子数据同步

1. 使用步骤

  • 在父组件中绑定好传递的数据,实际上传递的是props属性为value的数据和自定义事件名为input的事件,我们不用自己写这个自定义事件input的回调,它会自动识别并实现数据的更改

    <CustomInput v-model="msg2"></CustomInput>
    
  • 子组件中首先使用props接收value,并在相应的地方使用,并通过触发自定义事件input传递数据

    <template>
      <div style="background: #ccc; height: 50px">
        <h2>input包装组件</h2>
        <!-- @input 是原生DOM事件 -->
        <input type="text" :value="value" @input="$emit('input', $event.target.value)" />
      </div>
    </template>
    
    <script type="text/ecmascript-6">
    export default {
      name: 'CustomInput',
      props:['value']
    }
    </script>
    

2. v-model实现组件通信的原理

  • 父组件中

    • 结构:传递props属性为value的数据给子组件,并且在子组件上绑定自定义事件input

      <CustomInput :value="msg2" @input="inputHandler"></CustomInput>

    • js:定义好自定义事件input的回调

      inputHandler(params){
      this.msg2  = params;
      }
      
  • 子组件中

    • props接收传递过来的value,并在对应的地方触发自定义事件input,传递的参数即设置为value的值

      <template>
        <div style="background: #ccc; height: 50px">
          <h2>input包装组件</h2>
          <!-- @input 是原生DOM事件 -->
          <input type="text" :value="value" @input="$emit('input', $event.target.value)" />
        </div>
      </template>
      
      <script type="text/ecmascript-6">
      export default {
        name: 'CustomInput',
        props:['value']
      }
      </script>
      

八、属性修饰符sync *

该属性可以实现父子组件数据同步

1. 使用

  • 父组件中

    <Child :money.sync="money"></Child>
    
  • 子组件中:使用props接收money,并在相应的地方触发自定义事件==“update:money”==

    <template>
      <div style="background: #ccc; height: 50px;">
        <span>小明每次花100</span>
        <button @click="$emit('update:money',money-100)">花钱</button>
        爸爸还剩{{money}}</div>
    </template>
    
    <script type="text/ecmascript-6">
      export default {
        name: 'Child',
        props:['money']
      }
    </script>
    

2. 原理说明

  • :money.sync :代表父组件给字符串传递 props【money】 且给当前子组件绑定一个自定义事件(‘update:money’)
  • 在子组件中触发自定义事件update:money,传递的参数就是money的值

九、$attrs和$listeners

1. $attrs

  • 组件实例的属性,可以获取到父组件传递的props数据前提子组件没有通过props接受

2. $listeners

  • 组件实例的属性,可以获取到父组件传递的自定义事件(对象形式呈现)

3. 案例

  • 父组件中:handler函数在父组件定义

    <!-- 
    	向我们封装的按钮的时候传递相应的参数 
    	@click:自定义事件
    -->
    <hintButton type="warning" icon="el-icon-s-help" size="mini" tip="提示信息" @click="handler"></hintButton>
    
  • 子组件中

    <!--
    	使用v-bind将所有$attrs的属性绑定到组件上
    	使用v-on将所有的父组件传过来的自定义事件绑定到组件上
    	下面v-bind不能用:替换  v-on不能用@替换
    -->
    <el-button v-bind="$attrs" v-on="$listeners">主要按钮</el-button>
    

十、$ref、$children与$parent

1. $refs

ref也算在一种通信手段:在父组件中可以获取子组件(属性|方法)

  • 可以在父组件内部获取子组件实例(VC)—实现父子通信
  • 先给子组件打上ref标签:ref='son'
  • 使用子组件的属性:this.$refs.son.money -= 100

2. $children

  • 可以获取当前组件的全部子组件
  • 注意点:这个属性在用的时候很少用索引值获取子组件,因为没有办法确定数组里面的元素到底是哪一个子组件,即子组件顺序在$children未知
  • 例如:this.$children.forEach(item => { item.money -= 200 })

3. $parent

  • 可以在子组件内部获取唯一的父组件【返回组件实例】
  • 例如:this.$parent.money += 50
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嘎嘎油

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值