VUE的组件通信

1.组件通信的解决方案

  组件通信的方案一般与组件之间的关系有关,组件之间的关系不同,所对应的通信方案也不同。组件之间的关系一般分为两种:1.父子关系,2.非父子关系

 (1)父子关系 

     父子关系的组件之间通信一般使用props$emit

       1.父传子  

           1.1 给子组件绑定自定义属性,并给其传值

           1.2 父组件使用props接收该属性的值

         父组件代码示例:

<template>
<!-- 1.给子组件绑定自定义属性,给子组件的自定义属性msg传值myMsg-->
  <Son :msg="myMsg"></Son>
</template>

<script>
//导入子组件
import Son from /components/Son
export default {
data(){
    
    return {
        myMsg:'hello world!'
        }
    },
    //注册子组件
    components:{
        Son
    }
}
</script>

<style>

</style>

        子组件代码示例:

<template>
<!-- 3.使用props接收的msg-->
  <div>{{msg}}</div>
</template>

<script>
export default {
// 2.子组件通过props接收传递过来的msg
props:['msg']
}
</script>

<style>

</style>

       2.子传父

            2.1 子组件使用$emit给父组件发送消息通知

            2.2 父组件添加子组件的监听

            2.3 监听之后,添加方法处理监听到的数据

         父组件代码示例

<template>
   <!--2.对子组件传递的消息进行监听,并绑定函数处理传递的值 -->
  <Son :msg="myMsg" @changeMsg="handleChange"></Son>
</template>

<script>
//导入子组件
import Son from /components/Son
export default {
data(){
    
    return {
        myMsg:'hello world!'
        }
    },
    //注册子组件
    components:{
        Son
    },
    methods:{
       // 3.将监听到子组件传递过来的新值赋值给父组件的myMsg
        handleChange(newMsg){
          this.myMsg=newMsg;
        }
    }
}
</script>

<style>

</style>

    子组件代码示例

<template>
  <div>
    <div>{{msg}}</div>
    <button @click="changeFn">修改msg</button>
  </div>
  
</template>

<script>
export default {
props:['msg'],
methods:{
    changeFn(){
    // 1.通过$emit,向父组件发起通知
    this.$emit('changeMsg','newMsg')
    }
}
}
</script>

<style>

</style>

 (2)非父子关系

    非父子关系的组件之间一般才用provide injectenventbus

    通用的解决方案Vuex (适用于复杂的业务场景)         

   2.1 enventbus 事件总线

       (1)创建一个都能访问的事件总线(空的vue实例)EventBus.js

import Vue from "vue";

const Bus =new Vue();

export default Bus;

       (2)B组件导入事件总线,并利用其发送消息

<template>
  <div>B组件
    <button @click="send">发送消息</button>
  </div>
  
</template>

<script>
// 导入事件总线
import Bus from '../utils/EnventBus'
export default {
 methods:{
    send(){
   //发送消息
        Bus.$emit('sendMsg','I am newMsg')
    }
 }
}
</script>

<style>

</style>

        (3)A组件导入事件总线监听B组件发送的消息,并将消息做回调处理

<template>
  <div>BaseA组件:{{msg}}</div>
</template>

<script>
import { util } from 'vue/types/umd'
//导入事件总线
import Bus from '../utils/EnventBus';

export default {
    data(){
        return {
            msg : ''
        }
    },
    created(){
     //监听sendMsg方法传递过来的值,并利用回调函数将其赋值给当前的msg
     Bus.$on('sendMsg',(msg)=>{
          this.msg = msg;
     })
    }
}
</script>

<style>

</style>

    2.2 provide & inject :跨层级共享数据

         2.2.1 父组件使用 provide共享数据

export default {
   //使用provide共享数据
    provide(){
       return{
        msg:this.msg, //简单类型   非响应式
        student:this.student//复杂类型  响应式
       }
    },
    data(){
        return {
            msg : '',
            student:{
                id:001,
                name:'张三',
                grade:3,
                class:9,
                age:18,
                gender:'男'
            }
        }
    }
}

         2.2.2 子孙组件使用inject接收数据

<template>
    <div>
    <!--使用数据进行渲染-->
        <span>{{msg}}</span>
        <span>{{student.name}}</span>
        <span>{{student.age}}</span>
        <span>{{student.gender}}</span>
    </div>
</template>
export default {
//使用inject接收provide所共享的数据
inject:['msg','student']
}

(3)复杂业务场景 (vuex)

        3.1 vuex概述:

         vuex是一个vue的状态管理工具。(状态就是数据,vuex可管理vue的通用数据,即多组件             共享的数据)

        3.2 使用场景:

         (1)某个状态在很多个组件中都要使用,如个人信息

         (2)多个组件共同维护同一份数据,如购物车

        3.3 优势

         (1)数据集中化管理

         (2)响应式变化

         (3)操作简洁(vuex提供了一些辅助函数简化操作)

        3.4 使用步骤

         (1)安装vuex插件

             使用yarn安装 ,在项目的终端输入  yarn add vuex@3

             使用npm安装,在项目的终端输入  npm i vuex@3

         (2)新建vuex模块文件

           即在src目录下创建一个store文件夹,并新建一个index.js定义vuex

        (3)创建仓库 (编写store\index.js代码,代码如下)

// 这里存放的是vuex的核心代码
import Vue from 'vue'
import Vuex from 'vuex'

// 插件安装
Vue.use(Vuex)

// 创建仓库
const store = new Vuex.Store()

// 导出
export default store

          (4)将创建的仓库在main.js挂载到Vue中

import Vue from 'vue'
import App from './App.vue'
// 导入store
import store from '@/store/index'

Vue.config.productionTip = false

new Vue({
render: h =>h(App),
// 挂载store
store
}).$mount('#app')

         (5)给仓库提供数据

// 创建仓库
const store = new Vuex.Store({
    // 在仓库通过state提供数据(所有组件共享)
    state:{
        name:'张三',
        id: 1001
    }
})

         (6)使用数据

<template>
  <div class="header">header
 //模版中使用 $store.state.xx获取数据
  <div>{{ $store.state.name }}</div>
  <div>{{ $store.state.id }}</div>
  </div>
</template>
<script>
export default {
  created () {
 //组件中使用 this.$store.state.xx获取数据
    console.log(this.$store.state.name)
  }
}
</script>

        或者使用vuex的mapState辅助函数获取数据

import { mapState } from 'vuex'
export default {
  created () {
    console.log(this.$store.state.name)
    // 使用vuex的mapState辅助函数获取数据
    console.log(mapState(['name', 'id']))
  }

           (7)修改数据(由于vue默认是单向数据流的,使用数据的组件无法直接更改仓库中的数据,所以需要使用mutations去修改数据)

                7.1 定义mutations对象,对象中存放修改state的方法

const store = new Vuex.Store({
  // 在仓库的state中提供数据
  state: {
    name: '张三',
    id: 1001,
    num: 100,
    list: [1,2,3,4,5,6,7,8,9,10]
  },
  // 定义mutations对象并添加数据的方法
  mutations: {
    //无参方法
    addNum (state) {
      state.num++
    },
    //有参方法,除了本身的state外只能传一个参数,如果需要传多个,可以将参数封装成数组或对象    
    changeName (state, name) {
        state.name = name
    },
    changeNameAndNum(state,obj){
        state.name = obj.name
        state.num = obj.num
    }
   },
//由于mutations是必须同步的(便于监听数据变化和调试),如果需要异步处理数据则需要用到actions
  actions: {
   //在action中定义一个方法,用于异步修改数据,context是上下文,num是传递的参数
   changeNumAsync(context,num){
     // 设置定时器 一秒之后修改数据
     setTimeout(()=>{
      // action中不能直接修改state中的数据,需要调用mutations中的方法
      context.commit('changeNum',num)
      },1000)
    }
   },
  //getters类似于computed计算属性,依赖于state中的数据进行处理展示
  getters: {
    // 如需要展示list数组中大于5的元素,则可以在getters中添加以下方法
    // 注意 1:第一个参数为state  2. 必须要有返回值,返回值就是getters的值    
    filterList(state){
      return state.list.filter(item => item > 5 )
    }
   }
  
})

                 7.2 组件中提交调用mutations中的方法

//导入vuex中的辅助函数
import {mapState,mapMutations,mapActions,mapGetters} from 'vuex'


// 调用无参方法
this.$store.commit('addNum')

// 调用有参方法
this.$store.commit('changeName','李四')

// mutations不支持传多个参数,如有多个参数,用对象封装传参
this.$store.commit('changeName',{name:'李四',num:'1'})

// mapMutations辅助函数将方法添加到了methods中,可直接用方法名调用,无需this.$store.commit()
this.changeName('李四')
this.addNum('1')

// mapState辅助函数将属性添加到了computed中,可直接使用,无需this.$store.state.name
console.log(this.name)
console.los(this.id)

//需要异步处理数据,调用仓库中actions中的方法
this.$store.dispatch(['changeNumAsync'])

//mapActions辅助函数将actions方法,映射到了methods中,就可以直接调用,无需this.$store.dispatch
this.changeNumAync(1)

// 获取getters中的数据
console.log(this.$store.getters.filterList)

// mapGetters辅助函数将getters中的数据,映射到了computed属性中,可以直接使用
console.log(this.filterList)


methods: {
// 使用mapMutations辅助函数,将仓库提供的mutations中的方法,展开映射到methods中,就可以直接调用
...mapMutations(['changeName','addNum']),

// 使用mapActions辅助函数,将仓库提供的actions中的方法,展开映射到methods中,就可以直接调用
...mapActions('changeNumAsync',1)
},
computed:{
//使用mapState辅助函数,将仓库提供的数据,展开映射到computed属性中,就可以直接使用
...mapState(['name','id']),

//使用mapGetters辅助函数,将仓库中依赖于state的派生属性,映射到computed属性中,就可以直接使用
...mapGetters(['filterList'])
}

        (8)分模块

          如果所有数据都在store/index.js中的仓库中维护,那么随着数据越来越多将会变得难以维护,可以将数据根据模块进行拆分,最后将这些模块利用modules属性挂载到仓库中。

                8.1 ,首先在store中新建一个modules文件夹,不同模块的数据写在不同的js中,如用户模块,写在user.js中。在user.js中定义是state,mutations,getters,actions等等属性

                8.2 在store/index.js中导入对应模块的js,并挂载到仓库中

import user from '../store/modules/user'

// 插件安装
Vue.use(Vuex)

// 创建仓库
const store = new Vuex.Store({
    // 将user.js中的数据注入到仓库的子模块中
    modules: {
    user
  }
})

                8.3 使用user子模块中的中的数据

//方式一 原生js
this.$store.state.user.age
//方式二 使用mapState('模块名',['属性名']) 注册到computed中,直接使用
//注意 使用这种方式需要在user.js中开启命名空间(在user.js export default中加上 namespaced: true)
console.log(this.age)

// 不光是state可以这样使用 getters,actions,mutations都可以这样使用
// this.$store.getters('模块名/属性名')
this.$store.getters(['user/firstName'])
// 使用mapGetters('模块名',['属性名']) 也需开启命名空间
console.log(this.firstName)

// 调用子模块的mutations  this.$store.commit('模块名/方法名','额外参数')
this.$store.commit('user/changeName','李四')
//使用mapMutations
this.changeName('李四')

//调用子模块的actions this.$store.dispatch('模块名/方法名','额外参数')
this.$store.dispatch('user/setNameAsync','李四')
// 使用mapActions('模块名',['方法名']) 注册到methods中,可通过方法名直接调用
this.setNameAsync('李四')



methods:{
...mapMutations('user',['changeName']),
...mapActions('user',['setNameAsync'])
},
computed: {
...mapState('user',['age']),
...mapGetters('user',['firstName'])
}
const state ={ 
    userName: '张三',
    age: 20,
    gender: '男'
}

const mutations = {
    changeName(state,newname) {
        state.userName = newname
    }
}

const gatters = {
firstName(state) {
  return state.userName.substr(0,1)
}

const actions = {
  setNameAsync(context,newName){
    setTimeout(()=>{context.commit('changeName',newName)},1000)
  }
}

export default {
    // 开启命名空间
    namespaced: true,
    state,
    mutations,
    getters,
    actions
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值