Vue3 的 7 种和 Vue2 的 12 种组件通信,值得收藏

2022-11-26 0 840

Vue2.x模块通讯12种形式写在前面了,嘿嘿 Vue3 的

曼威给!

Vue3 模块通讯形式

props$emitexpose / ref$attrsv-modelprovide / injectVuexmitt

Vue3 通讯采用读法

1. props

用 props 传统计数据给子模块有三种形式,如下表所示

形式一,混和读法

// Parent.vue 传输<child :msg1=“msg1” :msg2=“msg2”><

/child>

import child from “./
child.vue

import { ref, reactive } from ”
vue

export default {

data(){

        return {

            msg1:”
这是传级子模块的重要信息1

        }

    },

    setup(){

        // 创建一个响应式统计数据

        // 读法一 适用于基础类型  ref 还有其他用处,下面章节有介绍

const msg2 = ref(“
这是传级子模块的重要信息2

“)

        // 读法二 适用于复杂类型,如数组、对象

        const msg2 = reactive([”
这是传级子模块的重要信息2

“])

        return {

            msg2

        }

    }

}

// Child.vue 接收

export default {

  props: [”
msg1“, “msg2

“],// 如果这行不写,下面就接收不到

  setup(props) {

console.log(props) // { msg1:”
这是传给子模块的重要信息1“, msg2:”这是传给子模块的重要信息2

” }

  },

}

形式二,纯 Vue3 读法

// Parent.vue 传输<child :msg2=“msg2”><

/child>

    import child from “./
child.vue

    import { ref, reactive } from ”
vue

    const msg2 = ref(”
这是传给子模块的信息2

“)

    // 或者复杂类型

    const msg2 = reactive([”
这是传级子模块的重要信息2

“])

// Child.vue 接收

    // 不需要引入 直接采用

// import { defineProps } from “
vue

    const props = defineProps({

        // 读法一

        msg2: String

        // 读法二

        msg2:{

            type:String,

            default:”

        }

    })

console.log(props) // { msg2:”
这是传级子模块的重要信息2

” }

注意:

如果父模块是混和读法,子模块纯 Vue3 读法的话,是接收不到父模块里 data 的属性,只能接收到父模块里 setup 函数里传的属性

如果父模块是纯 Vue3 读法,子模块混和读法,可以通过 props 接收到 data 和 setup 函数里的属性,但是子模块要是在 setup 里接收,同样只能接收到父模块中 setup 函数里的属性,接收不到 data 里的属性

官方也说了,既然用了 3,就不要写 2 了,所以不推荐混和读法。下面的例子,一律只用纯 Vue3 的读法,就不写混和读法了

2. $emit

// Child.vue 派发    // 读法一    <button @click=“emit(myClick)”>按钮</buttom>    // 读法二    <button @click=“handleClick”>按钮</buttom><

/template>

    /

/ 形式一 适用于Vue3.2版本 不需要引入

    /

/ import { defineEmits } from “vue”

    /

/ 对应读法一

const emit = defineEmits([“myClick”,”myClick2″])

    /

/ 对应读法二

    const handleClick = ()=>{

        emit(“myClick”, “这是发送给父模块的重要信息”)

    }

    /

/ 方法二 不适用于 Vue3.2版本,该版本 useContext()已废弃

    import { useContext } from “vue”

    const { emit } = useContext()

const handleClick = ()=>{

        emit(“myClick”, “这是发送给父模块的重要信息”)

    }

</

script>

// Parent.vue 响应<template>    <child @myClick=“onMyClick”></child></template><script setup>    import child from “./child.vue”    const onMyClick = (msg) =>

{

        console.log(msg) // 这是父模块收到的重要信息

    }

</script>

3. expose / ref

// Child.vue    // 形式一 不适用于Vue3.2版本,该版本 useContext()已废弃    import { useContext } from “vue”    const

 ctx = useContext()

    // 对外暴露属性形式等都可以

ctx.expose({

        childName“这是子模块的属性”

,

        someMethod(){

            console.log(“这是子模块的形式”

)

        }

    })

    // 形式二 适用于Vue3.2版本, 不需要引入    // import { defineExpose } from “vue”

    defineExpose({

        childName“这是子模块的属性”

,

        someMethod(){

            console.log(“这是子模块的形式”

)

        }

})

<

/script>

/

/ Parent.vue  注意 ref=”comp”

    <child ref=”comp”></

child>

    <button @click=“handlerClick”>按钮</button><

/template>

    import child from “./
child.vue

    import { ref } from ”
vue

    const comp = ref(null)

const handlerClick = () => {

comp.value.someMethod() // 调用子组件对外暴露的形式

    }

4. attrs

attrs:包含父作用域里除 class 和 style 除外的非 props 属性集合

// Parent.vue 传输<child :msg1=“msg1”:msg2=“msg2” title=“3333”><

/child>

    import child from “./
child.vue

    import { ref, reactive } from ”
vue

const msg1 = ref(“
1111

“)

    const msg2 = ref(”
2222

“)

// Child.vue 接收

import { defineProps, useContext, useAttrs } from “
vue

    // 3.2版本不需要引入 defineProps,直接用

    const props = defineProps({

        msg1: String

    })

// 形式一 不适用于 Vue3.2版本,该版本 useContext()已废弃

    const ctx = useContext()

    // 如果没有用 props 接收 msg1 的话就是 { msg1: ”
1111“, msg2:”2222“, title: “3333

” }

    console.log(ctx.attrs) // { msg2:”
2222“, title: “3333

” }

    // 形式二 适用于 Vue3.2版本

const attrs = useAttrs()

    console.log(attrs) // { msg2:”
2222“, title: “3333

” }

5. v-model

可以支持多个统计数据双向绑定

// Parent.vue<child v-model:key=“key” v-model:value=“value”><

/child>

    import child from “./
child.vue

import { ref, reactive } from “
vue

    const key = ref(”
1111

“)

    const value = ref(”
2222

“)

// Child.vue

<button @click=”
handlerClick

“>按钮

    // 形式一  不适用于 Vue3.2版本,该版本 useContext()已废弃

    import { useContext } from ”
vue

const { emit } = useContext()

    // 形式二 适用于 Vue3.2版本,不需要引入

    // import { defineEmits } from ”
vue

const emit = defineEmits([“
key“,”value

“])

    // 用法

    const handlerClick = () => {

        emit(”
update:key“, “新的key

“)

        emit(”
update:value“, “新的value

“)

    }

6. provide / inject

provide / inject 为依赖注入

provide:可以让我们指定想要提供给后代模块的统计数据或

inject:在任何后代模块中接收想要添加在这个模块上的统计数据,不管模块嵌套多深都可以直接拿来用

// Parent.vue    import { provide } from “vue”    provide(“name”“沐华”

)

<

/script>

/

/ Child.vue

    import { inject } from “vue”

    const name = inject(“name”)

    console.log(name) /

/ 沐华

</

script>

7. Vuex

// store/index.jsimport { createStore } from “vuex”export default

 createStore({

    state:{ count1

 },

    getters

:{

        getCountstate =>

 state.count

    },

    mutations

:{

        add(state){

            state.count++

        }

    }

})

// main.jsimport { createApp } from “vue”import App from “./App.vue”import store from “./store”createApp(App).use(store).mount(“#app”

)

// Page.vue// 形式一 直接采用    <div>{{ $store.state.count }}</div>    <button @click=“$store.commit(add)”>按钮</button><

/template>

/

    import { useStore, computed } from “vuex”

    const store = useStore()

console.log(store.state.count) /

/ 1

    const count = computed(()=>store.state.count) /

/ 响应式,会随着vuex统计数据改变而改变

console.log(count) /

/ 1 

</

script>

8. mitt

Vue3 中没有了 EventBus 跨模块通讯,但是现在有了一个替代的方案 mitt.js,原理还是 EventBus

先安装 npm i mitt -S

然后像以前封装 bus 一样,封装一下

mitt.js

import mitt from mitt

const mitt = mitt()

export

 default mitt

然后两个模块之间通讯的采用

// 组件 A

import mitt from ./mitt

const handleClick = () => {

    mitt.emit(handleChange

)

}

// 模块 B 

import mitt from ./mittimport { onUnmounted } from vue

const someMethed = () => { … }

mitt.on(handleChange

,someMethed)

onUnmounted(()=>{

    mitt.off(handleChange

,someMethed)

})

Vue2.x 模块通讯形式

Vue2.x 模块通讯共有12种

props$emit / v-on.syncv-modelrefparentlistenersprovide / injectEventBusVuex$rootslot

父子模块通讯可以用:

props$emit / v-onlistenersref.syncv-modelparent

兄弟模块通讯可以用:

EventBusVuex$parent

跨层级模块通讯可以用:

provide/injectEventBusVuexlisteners$root

Vue2.x 通讯采用读法

下面把每一种模块通讯形式的读法一一列出

1. props

父模块向子模块传输统计数据,这应该是最常用的形式了

子模块接收到统计数据之后,不能直接修改父模块的统计数据。会报错,所以当父模块重新渲染时,统计数据会被覆盖。如果子模块内要修改的话推荐采用 computed

// Parent.vue 传输

    <child :msg=“msg”

>

// Child.vue 接收

export

 default {

// 读法一 用数组接收

  props:[msg

],

  // 读法二 用对象接收,可以限定接收的统计数据类型、设置默认值、验证等

  props:{

      msg:{

          type

:String,

          default:这是默认统计数据

      }

  },

  mounted

(){

      console.log(this.msg)

  },

}

2. .sync

可以帮我们实现父模块向子模块传递的统计数据 的双向绑定,所以子模块接收到统计数据后可以直接修改,并且会同时修改父模块的统计数据

// Parent.vue

    <child :page.sync=“page”

>

export

 default {

    data

(){

        return

 {

            page:1

        }

    }

}

// Child.vue

export

default {

    props:[“page”

],

    computed

(){

        // 当我们在子模块里修改 currentPage 时,父模块的 page 也会随之改变

        currentPage {

            get

(){

                return

 this.page

            },

            set

(newVal){

                this.$emit(“update:page”

, newVal)

            }

        }

    }

}

3. v-model

和 .sync 类似,可以实现将父模块传给子模块的统计数据为双向绑定,子模块通过 $emit 修改父模块的统计数据

// Parent.vue

    <child v-model=“value”

>

export

 default {

    data

(){

        return

 {

value:1

        }

    }

}

// Child.vue

    <input :value=“value” @input=“handlerChange”

>

export

 default {

    props:[“value”

],

// 可以修改事件名,默认为 input

    model:{

        event:“updateValue”

    },

    methods:{

        handlerChange(e){

            this.$emit(“input”

, e.target.value)

// 如果有上面的重命名就是这样

            this.$emit(“updateValue”

, e.target.value)

        }

    }

}

4. ref

ref 如果在普通的DOM元素上,引用指向的就是该DOM元素;

如果在子

// Child.vue

export

 default {

    data

(){

        return

 {

            name:“沐华”

        }

    },

    methods:{

        someMethod(msg){

            console.log(msg)

        }

    }

}

// Parent.vue

    <child ref=“child”

>

export

default {

    mounted

(){

        const child = this.$refs

.child

        console.log(child.name) // 沐华

        child.someMethod(“调用了子模块的形式”

)

}

}

5. $emit / v-on

子模块通过派发事件的形式给父模块统计数据,或者触发父模块更新等操作

// Child.vue 派发

export

 default {

  data

(){

      return { msg: “这是发给父模块的重要信息”

 }

  },

  methods: {

      handleClick

(){

          this.$emit(“sendMsg”

,this.msg)

      }

  },

}

// Parent.vue 响应

<child v-on:sendMsg=“getChildMsg”

>

    // 或 简写

    <child @sendMsg=“getChildMsg”

>

export

 default {

    methods:{

        getChildMsg(msg){

console.log(msg) // 这是父模块接收到的消息

        }

    }

}

6. listeners

多层嵌套模块传递统计数据时,如果只是传递统计数据,而不做中间处理的话就可以用这个,比如父模块向孙子模块传递统计数据时

$attrs:包含父作用域里除 class 和 style 除外的非 props属性集合。通过 this.attrs”

$listeners:包含父作用域里 .native 除外的监听事件集合。如果还要继续传给子模块内部的其他模块,就可以通过 v-on=”$linteners”

采用形式是相同的

// Parent.vue

    <child :name=“name” title=“1111”

 >

export

default{

    data

(){

        return

 {

            name:“沐华”

        }

    }

}

// Child.vue

    // 继续传给孙子模块

    <sun-child v-bind=$attrs

>

export

 default{

    props:[“name”

], // 这里可以接收,也可以不接收

    mounted

(){

        // 如果props接收了name 就是 { title:1111 },否则就是{ name:“沐华”

, title:1111 }

console.log(this.$attrs

)

    }

}

7. parent

// Parent.vue

export

 default{

    mounted

(){

        this.$children

[0].someMethod() // 调用第一个子模块的形式

        this.$children

    }

}

// Child.vue

export

 default{

    mounted

(){

        this.$parent

.someMethod() // 调用父模块的形式

        this.$parent

    }

}

8. provide / inject

provide / inject 为依赖注入,说是不推荐直接用于应用程序代码中,但是在一些插件或模块库里却是被常用,所以我觉得用也没啥,还挺好用的

provide:可以让我们指定想要提供给后代模块的统计数据或形式

inject:在任何后代模块中接收想要添加在这个模块上的统计数据或形式,不管模块嵌套多深都可以直接拿来用

要注意的是 provide 和 inject 传递的统计数据不是响应式的,也就是说用 inject 接收来统计数据后,provide 里的统计数据改变了,后代模块中的统计数据不会改变,除非传入的就是一个可监听的对象

所以建议还是传递一些常量或者形式

// 父模块

export

 default{

hods 中的形式

    provide:{

        name:“沐华”

,

        age: this.data中的属性

    },

    provide

(){

        return

 {

            name:“沐华”

,

someMethod:this.someMethod // methods 中的形式

        }

    },

    methods:{

        someMethod

(){

            console.log(“这是注入的形式”

)

        }

    }

}

// 后代模块

export

 default{

    inject:[“name”,“someMethod”

],

    mounted

(){

        console.log(this.name)

        this.someMethod()

    }

}

9. EventBus

EventBus 是中央事件总线,不管是父子模块,兄弟模块,跨层级模块等都可以采用它完成通讯操作

定义形式有三种

// 形式一

// 抽离成一个单独的 js 文件 Bus.js ,然后在需要的地方引入

// Bus.js

import Vue from “vue”export

 default new Vue()

// 形式二 直接挂载到全局

// main.js

import Vue from “vue”Vue.prototype.$bus

 = new Vue()

// 形式三 注入到 Vue 根对象上

// main.js

import Vue from “vue”

new Vue({

    el:“#app”

,

    data:{

Bus: new Vue()

    }

})

采用如下表所示,以形式一按需引入为例

// 在需要向外部发送自定义事件的模块内

    <button @click=“handlerClick”

>按钮

import Bus from “./Bus.js”export

 default{

    methods:{

        handlerClick

(){

            // 自定义事件名 sendMsg

            Bus.$emit(“sendMsg”“这是要向外部发送的统计数据”

)

        }

    }

}

// 在需要接收外部事件的模块内

import Bus from “./Bus.js”export

 default{

    mounted

(){

        // 监听事件的触发

        Bus.$on(“sendMsg”

, data => {

            console.log(“这是接收到的统计数据:”

, data)

        })

    },

    beforeDestroy

(){

        // 取消监听

        Bus.$off(“sendMsg”

)

    }

}

10. Vuex

Vuex 是状态管理器,集中式存储管理所有模块的状态。这一块内容过长,如果基础不熟的话可以看这个Vuex,然后大致用法如下表所示

比如创建这样的文件结构

微信图片_20210824003500.jpg

index.js 里内容如下表所示

import Vue fromvueimport Vuex from vueximport getters from ./gettersimport actions from ./actionsimport mutations from./mutationsimport state from ./stateimport user from ./modules/user

Vue.use(Vuex)

const store = new Vuex.Store({

  modules: {

    user

  },

  getters,

  actions,

  mutations,

  state

})

export

 default store

然后在 main.js 引入

import Vue from “vue”import store from “./store”

new Vue({

    el:“#app”

,

    store,

    render: h => h(App)

})

然后在需要的采用模块里

import { mapGetters, mapMutations } from “vuex”export

 default{

    computed:{

        // 形式一 然后通过 this.属性名就可以用了

…mapGetters([“引入getters.js里属性1”,“属性2”

])

        // 形式二

        …mapGetters(“user”, [“user模块里的属性1”,“属性2”

])

    },

methods:{

        // 形式一 然后通过 this.属性名就可以用了

        …mapMutations([“引入mutations.js里的形式1”,“形式2”

])

        // 形式二

        …mapMutations(“user”,[“引入user模块里的形式1”,“形式2”

])

    }

}

this.$store

.state.xxx

this.$store

.state.user.xxx

11. $root

$root 可以拿到 App.vue 里的统计数据和形式

12. slot

就是把子模块的统计数据通过插槽的形式传给父模块采用,然后再插回来

// Child.vue

        <slot :user=“user”

>

export

default{

    data

(){

        return

 {

            user:{ name:“沐华”

 }

        }

    }

}

// Parent.vue

        <child v-slot=“slotProps”

>

{{ slotProps.user.name }}

结语

写作不易,你的一赞一评,就是我前行的最大动力。

– EOF –

推荐阅读  点击标题可跳转

1、Vue 项目前端多语言方案

2、采用 Vue3 + AR 撸猫,才叫好玩

3、Vue 这个透传技巧,治好了我的重度代码洁癖(珍藏!)

觉得本文对你有帮助?请分享给更多人

关注「大前端技术之路」加星标,提升前端技能

点赞和在看就是最大的支持❤️

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务