Vue 7种组件通信的方式

1. props 和 $emit

1. 父传子—props

通过props(可以在组件上注册一些自定义的属性),向子组件传递数据
父组件

<template>
    <div class="home">
        <HelloWorld :parentMessage="message"/>
    </div>
</template>
<script>
import HelloWorld from "@/components/HelloWorld.vue"
export default{
    data() {
        return {
            message: "我是父组件传过来的数据"
        }
    },
    components: {
        HelloWorld
    }
</script>

子组件
子组件通过props接收数据

<template>
    <div class="hello">
        <p>{{parentMessage}}</p>
    </div>
</template>
<script>
export default {
    // 字符串数组
    // props: ["parentMessage"],
    // 对象(数据验证,类型验证)
    props: {
        parentMessage: {
            type: String, // 类型验证
            default: "" // 默认值
        }
    },
    data() {
        return {
            
        }
    }
    
}
</script>

2. 子传父—$emit 自定义事件

  • 通过自定义事件向父组件传递数据。
  • 自定义事件的流程:
    1. 在子组件中,通过$emit来触发事件,传递数据;

    2. 在父组件中,通过v-on去监听子组件的事件,获取传递过来的数据。

    3. 父组件通过 $off 来解绑自定义事件。

      this.$off('自定义事件名') // 解绑一个自定义事件
      this.$off(['自定义事件名1', '自定义事件名2']) // 解绑多个自定义事件
      this.$off() //解绑所有的自定义事件
      

子组件

<template>
    <div class="hello">
        <p>{{parentMessage}}</p>
        <button @click="sendChildren">点击给父组件传递数据</button>
    </div>
</template>
<script>
export default {
    // 子组件通过props接收数据
    // 字符串数组
    // props: ["parentMessage"],
    // 对象(数据验证,类型验证)
    props: {
        parentMessage: {
            type: String, // 类型验证
            default: "" // 默认值
        }
    },
    data() {
        return {
            age: 18
        }
    },
	methods: {
        sendChildren: function() {
            // 第一个参数:自定义事件名,第二个参数:传递的数据
            this.$emit("parentMessage", this.age)
        }
    }    
}
</script>

父组件

<template>
    <div class="home">
        <HelloWorld :parentMessage="message" @parentMessage="injectParent"/>
    </div>
</template>
<script>
import HelloWorld from "@/components/HelloWorld.vue"
export default{
    data() {
        return {
            message: "我是父组件传过来的数据"
        }
    },
    components: {
        HelloWorld
    },
    methods: {
        injectParent: function(value) {
            // value是自定义组件传过来的数据
            console.log(value)
        }
    }
</script>

2. $parent 和 $children

  1. 通过$parent获取父组件的数据

    <template>
        <div class="home">
        	<button @click="getParent">获取子组件的数据</button>
        </div>
    </teaplate>
    <script>
    export default {
    	data() {
            return {
                age: 20
            }
        },
        methods: {
        	getParent: function{
        		console.log(this.$parent.message)
                // 打印message的值
        	}
        },
    </script>
    
  2. 通过$children访问子组件的数据,this.$children是一个数组类型,包含所有的子组件对象(开发中比较少用)

    <template>
        <div class="home">
        	<Helloworld/>
        	<Helloworld/>
            <Helloworld/>
        	<button @click="getchildren">获取子组件的数据</button>
        </div>
    </teaplate>
    
    <script>
    export default {
    	data() {
            return {
                message: "我是父组件的数据"
            }
        },
        methods: {
        	getchildren: function{
        		console.log(this.$children[0].age)
        	}
        },
        components: { 
            Helloworld
        }
    </script>
    

3. $refs

通过$refsref一起使用访问子组件的数据(开发中常用)

<template>
    <div class="home">
    	<Helloworld/>
    	<Helloworld ref="two"/>
        <Helloworld/>
    	<button @click="getchildren">获取子组件的数据</button>
    </div>
</teaplate>

<script>
export default {
	data() {
        return {
            message: "我是父组件的数据"
        }
    },
    methods: {
    	getchildren: function{
    		console.log(this.$refs.two.age)
    	}
    },
    components: { 
        Helloworld
    }
</script>

4. provide 和 inject

父子之间的通信,跨级通信。provide/inject

  1. provide(提供):是一个对象或者一个返回对象的函数。

    <template>
        <div class="home">
        	<Parent/>
        	<hr/>
        	<h2>祖先组件</h2>
            <p>{{grandPa.message}}</p>
            <p>{{age}}</p>
            <button @click="changeMessage">改变message数据</button>
        </div>
    </template>
    <script>
    import Parent from "@/components/Parent.vue"
    export default {
        data() {
            return {
                // message: "我是祖先组件的数据", // 没有响应
                grandPa: {
                    message: "我是祖先组件的数据"
                },
                age: 18
            }
        },
        provide() {
            return {
                // message: this.message
                grandPa: this.gandPa, // 传入可响应的对象
                age: ()=>this.age // 通过计算属性计算注入的值
            }
        },
        methods: {
            changeMessage: function() {
                this.grandPa.message = "我是更改后的数据"
                this.age = 20
            }
        }
        components: {
            Parent
        }
    }
    </script>
    
    
  2. inject(注入):可以是一个字符串的数组,也可以是一个对象。在需要使用的地方注入

    <template>
        <div class="hello">
        <h2>孙子组件</h2>
        <!--<p>{{message}}</p>-->
        <p>{{grandPa.message}}</p>
        <p>{{newAge}}</p>
        </div>
    </template>
    <script>
    export default {
        data() {
            return {}
        },
        // inject: ['message'],
        inject: ['grandPa', 'age'],
        computed: {
            newAge: function() {
                return this.age()
    		}
        }
    }
    </script>
    
    
  3. provideinject绑定不是响应的。

  4. provideinject实现响应的方法:

    1. 传入可响应的对象。
    2. 通过计算属性计算注入的值。

5. vuex

1. vuex的基本使用

1. 安装vuex依赖包

npm install vuex@3 --save

目前vue3是默认的环境,所以下载vuex的时候也是默认下载vuex的版本4,而vue2使用的是vuex的版本3,所以在vue2中安装vuex的时候,下载语法是 vuex@3

2. 导入vuex包

import Vue from 'vue'
import Vuex from 'vuex'
// 使用vuex
Vue.use(Vuex)

3. 创建store对象

const store = new Vuex.Store({
    // state 存放所有的全局共享的数据
    state: {
        count: 0
    },
    mutations: {

    },
    actions: {

    }
})

export default store

4. 将store对象挂载到vue实例中

import store from './store'
new Vue({
  // 将创建的共享数据对象,挂载到vue实例中
  // 所有的组件,就可以直接从store中获取全局的数据了
  store,
  render: h => h(App)
}).$mount('#app')

2. Vuex的核心概念

Vuex中的主要核心概念如下:

  • State
  • Mutation
  • Action
  • Getter

1. State

State提供唯一的公共数据源,所有共享的数据都要统一放到Store的State中进行存储。

const store = new Vuex.Store({
    // 存储所有的共享数据
    state: {
        count: 0
    },
})

组件访问State中数据的第一种方式

this.$store.state.全局数据名称

组件访问State中数据的第二种方式

// 1. 从vuex中按需导入mapState函数
import { mapState } from 'vuex'

通过导入的mapState函数,将当前组件需要的全局数据,映射为当前组件的computed计算属性:

// 2. 将全局数据,映射为当前组件的计算属性
computed: {
    ...mapState(['count'])
}

2. Mutation

Mutation用于变更Store中的数据

  1. 只能通过mutation变更Store数据,不可以直接操作Store中的数据。
  2. 通过这种方式虽然操作起来稍微繁琐一些,但是可以集中监控所有数据的变化。

触发mutation的第一种方式

const store = new Vuex.Store({
    // 存储所有的共享数据
    state: {
        count: 0
    },
    // 定义Mutation
    mutations: {
        add(state) {
            // 变更状态
            state.count++
        },

    }
})
methods: {
    handle() {
        // 触发mutation的第一种方式
        this.$store.commit('add')
    }
}

可以在触发mutations时传递参数:

const store = new Vuex.Store({
    // 存储所有的共享数据
    state: {
        count: 0
    },
    // 定义Mutation
    mutations: {
        add(state) {
            // 变更状态
            state.count++
        },
        addN(state, step) {
            state.count += step
        }

    }
})
methods: {
    handle1() {
        // commit 的作用,就是为了调用某个mutations 的函数
        // 触发mutation的第一种方式
        this.$store.commit('add')
    },
        handle2() {
            // 触发mutations时携带参数
            this.$store.commit('addN', 3)
        }
}

this.$store.commit()是触发mutations的第一种方式,触发mutations的第二种方式

// 1. 从vuex中按需导入mapMutations函数
import { mapMutations } from 'vuex'

通过刚才导入的mapMutations 函数,将需要的 mutations函数,映射为当前组件的 methods方法:

methods: {
    // 2. 将指定的mutations函数,映射为当前组件的methods函数
    ...mapMutations(['sub', 'subN'])
}

Mutation中不能执行异步操作

3. Action

Action用于处理异步任务
如果通过异步操作变更数据,必须通过Action,而不能使用Mutation,但是在Action中还是要通过触发Mutation的方式间接变更数据。

const store = new Vuex.Store({
    // 存储所有的共享数据
    state: {
        count: 0
    },
    // 定义Mutation
    mutations: {
        add(state) {
            // 变更状态
            state.count++
        },
        addN(state, step) {
            state.count += step
        },
        sub(state) {
            state.count--
        },
        subN(state, step) {
            state.count -= step
        }

    },
    // 负责实现异步的操作,不能直接修改state中的数据,
    // 需要通过commit调用mutation中的函数来修改state中的数据
    actions: {
        addAsync(context) {
            // context相当于store
            setTimeout(() => {
                context.commit('add')
            }, 1000)
        }
    }
})
methods: {
    handle1() {
        // commit 的作用,就是为了调用某个mutations 的函数
        // 触发mutation的第一种方式
        this.$store.commit('add')
    },
        handle2(step) {
            this.$store.commit('addN', step)
        },
            handle3() {
                // dispatch 的作用,就是为了调用某个actions 的函数
                // 触发action的第一种方式
                this.$store.dispatch('addAsync')
            }
}

触发actions异步任务时携带参数:

actions: {
    addAsync(context) {
        // context相当于store
        setTimeout(() => {
            context.commit('add')
        }, 1000)
    },
        addNAsync(context, step) {
            setTimeout(() => {
                context.commit('addN', step)
            }, 1000)
        }
}
handle4(step) {
    // 触发actions时携带参数
    this.$store.dispatch('addNAsync', step)
}

this.$store.dispatch()是触发actions的第一种方式,触发actions的第二种方式

// 1. 从vuex中按需导入mapActions函数
import { mapActions } from 'vuex'

通过刚才导入的mapActions 函数,将需要的 Actions函数,映射为当前组件的 methods方法:

methods: {
    // 2. 将指定的actions函数,映射为当前组件的methods函数
    ...mapActions(['subAsync'])
}

4. Getter

Getter用于对Store中的数据进行加工处理形成新的数据。

  1. Getter可以对Store 中已有的数据加工处理之后形成新的数据,类似Vue的计算属性。
  2. Store 中数据发生变化,Getter的数据也会跟着变化。
const store = new Vuex.Store({
    // 存储所有的共享数据
    state: {
        count: 0
    },
    getters: {
        showNum(state) {
            return '当前数量为' + state.count
        }
    },
})

使用getters的第一种方法

this.$store.getters.名称

使用getters的第二种方法

// // 1. 从vuex中按需导入mapGetters函数
import { mapGetters } from 'vuex'
// 2. 将全局数据,映射为当前组件的计算属性
computed: {
    ...mapGetters(['showNum'])
},

6. eventBus

  1. 主要通过Vue.$emitVue.$on来实现兄弟组件之间的传值

  2. 实现步骤

    1. 创建 eventBus.js 文件,导出Vue实例
    2. 发送数据组件调用eventBus.$emit("事件名", 传递的数据)来触发事件。
    3. 接收数据组件调用eventBus.$on("事件名", 回调函数)来绑定事件。

    注意:发送数据组件触发的事件名与接收数据组件绑定的事件名相同

  3. 代码

    1. 发送方

      <template>
          <div>
              <h1>this is brother send</h1>
              <button @click="send">向接收方发送消息</button>
              <hr>
              <brother-receive></brother-receive>
          </div>
      </template>
      
      <script>
      import eventBus from './eventBus'
      export default {
          data() {
              return {
                  message: '我是发送方的消息'
              }
          },
          methods: {
              send () {
                  eventBus.$emit("sendMessage", this.message)
              }
          },
          components: {
              brotherReceive: () => import("./brotherReceive.vue")
          }
      }
      </script>
      
    2. 接收方

      <template>
          <div>
              <h1>this is brother receive</h1>
              <p>{{receiveMessage}}</p>
          </div>
      </template>
      
      <script>
      import eventBus from './eventBus'
      export default {
          data() {
              return {
                  receiveMessage: ''
              }
          },
          created() {
              eventBus.$on("sendMessage", e => {
                  this.receiveMessage = e
              })
          }
      }
      </script>
      

7. $attrs 和 $listeners

  1. 概念

    • $attrs

      当父组件传数据给子组件的时候,如果子组件的props没有进行接收,那数据就会被收集到子组件的$attrs里面,在子组件上使用v-bind="$attrs"可以直接将值传给当前组件的子组件(也就是孙组件)

    • $listeners

      包含了父作用域中的(不含.native修饰器的)v-on事件监听器。它可以通过v-on="$listeners”传入内部组件――在创建更高层次的组件时非常有用。

    包括组件propsemit property中未包含的所有属性。即除了组件内props已经声明了的所有给组件传的值

  2. 使用场景

    当需要给多层嵌套父子组件传值时,可以使用这个。

  3. 实现

    父组件 parent.vue

    <template>
        <div>
            <h1>this is parent</h1>
            <p>父组件中将要传给子组件的数据</p>
            <b>a: {{a}}</b>
            <br>
            <b>b: {{b}}</b>
            <br>
            <b>c: {{c}}</b>
            <br>
            <b>接收到的数据:{{message}}</b>
            <hr>
            <son :a="a" :b="b" :c="c" @funGrandson="fun1"></son>
        </div>
    </template>
    
    <script>
    export default {
        data() {
            return {
                a: 'aaaaa',
                b: 'bbbbb',
                c: 'ccccc',
                message: ''
            }
        },
        components: {
            son: () => import('./son.vue')
        },
        methods: {
            fun1(e) {
                this.message = e
            }
        },
    }
    </script>
    

    子组件son.vue

    <template>
        <div>
            <h1>this is son</h1>
            <p>父组件传给子组件,并被子组件通过props接收的数据</p>
            <b>a: {{a}}</b>
            <hr>
            <grandson v-bind="$attrs" v-on="$listeners"></grandson>
        </div>
    </template>
    
    <script>
    export default {
        data() {
            return {}
        },
        created() {
            console.log(this.$attrs) // {b: 'bbbbb', c: 'ccccc'}
            console.log(this.$listeners)  // {funGrandson: f}
        },
        props: {
            a: {
                type: String,
                default: ''
            }
        },
        components: {
            grandson: () => import('./grandson.vue')
        }
    }
    </script>
    

    孙子组件grandson.vue

    <template>
        <div>
            <h1>this is grandson</h1>
            <p>子组件通过<b>$attrs</b>将剩余未接收的数据传给孙子组件</p>
            <b>b: {{b}}</b>
            <br>
            <b>c: {{c}}</b>
            <br>
            <button @click="funGrandson">孙子组件发送数据</button>
        </div>
    </template>
    
    <script>
    export default {
        data() {
            return {
                b: '',
                c: ''
            }
        },
        created() {
            console.log(this.$attrs) // {b: 'bbbbb', c: 'ccccc'}
            this.b = this.$attrs.b
            this.c = this.$attrs.c
        },
        methods: {
            funGrandson() {
                this.$emit("funGrandson", "孙子组件")
            }
        },
    }
    </script>
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值