信息发布→ 登录 注册 退出

Vue3常用的通讯方式总结与实例代码

发布时间:2026-01-11

点击量:
目录
  • 前言
  • props
  • $emit
  • EventBus
  • v-model
  • expose / ref
  • provide / inject
  • 插槽 slot
    • 普通插槽
    • 具名插槽
    • 作用域插槽
  • 结语

    前言

    Vue3更新了很久了,但是之前项目都是用Vue2写的,最近去官网look了一波,在这里总结一下Vue3常用的通讯方式。
    尤大大,不要再更新了,真的学不动了。

    本文用的是Vue3.2版本的setup语法糖官网

    props

    父组件

        <template>
          <div>
              <Child :msg="msg" :obj="obj" />
          </div>
        </template>
    
        <script setup>
            import { ref, reactive } from 'vue'
            // Vue3.2版本setup语法糖引入组件不需要注册便可以使用组件
            import Child from './child.vue'
    
            const msg = ref('一只豆豆')
            // 传递复杂类型数据
            const obj = reactive({name: '豆豆'})
        </script>

    子组件

        <template>
          <div></div>
        </template>
    
        <script setup>
            // Vue3.2版本setup语法糖 defineProps 不需要引入可以直接使用
            const props = defineProps({
                msg: {
                    type: String,
                    default: ''
                },
                obj: {
                    type: Object,
                    default: () => {}
                }
            })
            console.log('msg', props.msg); // 一只豆豆
            console.log('obj', props.obj.name); // 豆豆
    </script>

    $emit

    父组件

        <template>
          <div>
              <Child @myClick="myClick" />
          </div>
        </template>
    
        <script setup>
            import { ref, reactive } from 'vue'
            import Child from './child.vue'
    
            const myClick = val => {
                console.log('val', val); // emit传递信息
            }
    </script>

    子组件

        <template>
          <div>
              <button @click="handleClick">click me</button>
          </div>
        </template>
    
        <script setup>
            // Vue3.2版本setup语法糖 defineEmits 不需要引入可以直接使用
            const emit = defineEmits(['myClick']) // 如果有多个emit事件可以往数组后边添加即可
            
            const handleClick = ()=>{
                emit("myClick", "emit传递信息")
            }
        </script>

    EventBus

    在Vue3中就没有EventBus了,可以使用mitt.js来替代
    安装

    $ npm install --save mitt

    bus.js

        import mitt from 'mitt'
        export default mitt()

    兄弟组件A emit触发

        <template>
          <div>
            <button @click="handleClick">click me</button>
          </div>
        </template>
    
        <script setup>
            import bus from './bus'
            const handleClick = () => {
              bus.emit('foo', '豆豆')
            }
        </script>

    兄弟组件B on接收

        <template>
          <div></div>
        </template>
    
        <script setup>
            import bus from './bus'
            bus.on('foo', e => {
              console.log('e', e) // '豆豆'
            })
        </script>

    v-model

    Vue2版本是可以通过修饰符.sync让子组件修改父组件的值,但是Vue3就取消这个修饰符,融合到v-model里边去了

    父组件

        <template>
          <div>
            <div>{{ name }}</div>
            <div>{{ age }}</div>
            <Child v-model:name="name" v-model:age="age" />
          </div>
        </template>
    
        <script setup>
            import Child from './child.vue'
            import { ref } from 'vue'
    
            const name = ref('豆豆')
            const age = ref(20)
        </script>

    子组件

        <template>
          <div>
            <div></div>
            <button @click="handleChange">click me</button>
          </div>
        </template>
    
        <script setup>
            // 'update:name' 这样写在console里面就不会有告警了
            const emit = defineEmits(['update:name', 'update:age'])
            const handleChange = () => {
              emit('update:name', '一只豆豆')
              emit('update:age', 18)
            }
        </script>

    expose / ref

    子组件通过 expose 暴露属性和方法出去
    父组件通过 ref 来获取子组件的值和调用方法

    父组件

        <template>
          <div>
            <Child ref="myRef" />
            <button @click="handleClick">click me</button>
          </div>
        </template>
    
        <script setup>
            import Child from './child.vue'
            import { ref } from 'vue'
            const myRef = ref(null)
            const handleClick = () => {
              console.log('myRef', myRef.value.name) // 豆豆
              myRef.value.fn() // 一只豆豆
            }
        </script>

    子组件

        <template>
          <div>
            <div></div>
          </div>
        </template>
    
        <script setup>
            // Vue3.2版本setup语法糖 defineExpose 不需要引入可以直接使用
            defineExpose({
              name: '豆豆',
              fn () {
                console.log('一只豆豆')
              }
            })
        </script>

    provide / inject

    provide / inject 可以给后代组件传参,嵌套多少层都没问题

    父组件

        <template>
          <div>
            <Child />
          </div>
        </template>
    
        <script setup>
            import Child from './child.vue'
            import { ref, provide } from 'vue'
            const name = ref('豆豆')
            provide('name', name)
        </script>

    后代组件

        <template>
          <div>
            <div>后代组件name {{ name }}</div>
          </div>
        </template>
    
        <script setup>
            import { inject } from 'vue'
            const name = inject('name')
            console.log('name', name.value) // 豆豆
        </script>

    Vue2使用 provide / inject 传递数据不是响应式的,所以只能通过传递一个对象数据才能变成响应式
    Vue3使用 provide / inject传递数据就是响应式了,这就很便捷

    插槽 slot

    普通插槽

    父组件

        <template>
          <div>
            <Child>豆豆</Child>
          </div>
        </template>
    
        <script setup>
            import Child from './child.vue'
        </script>

    子组件

        <template>
          <div>
            <slot></slot>
          </div>
        </template>
    
        <script setup></script>

    具名插槽

        <template>
          <div>
            <Child>
              豆豆
              <template #name>
                <div>
                  <button>一只豆豆</button>
                </div>
              </template>
            </Child>
          </div>
        </template>
    
        <script setup>
            import Child from './child.vue'
        </script>

    子组件

        <template>
          <div>
            // 普通插槽
            <slot></slot>
            // 具名插槽
            <slot name="name"></slot>
          </div>
        </template>
    
        <script setup></script>

    效果图

    作用域插槽

    父组件

        <template>
          <!-- v-slot="{scope}" 子组件返回的每一项数据 -->
          <Child v-slot="{ scope }" :arr="arr">
            <div class="box">
              <div>名字:{{ scope.name }}</div>
              <div>年龄:{{ scope.age }}</div>
              <div>爱好:{{ scope.like }}</div>
            </div>
          </Child>
        </template>
    
        <script setup>
            import { reactive } from 'vue'
            import Child from './child.vue'
    
            const arr = reactive([
              { name: '张三', age: 18, like: '篮球' },
              { name: '李四', age: 19, like: '排球' },
              { name: '王五', age: 20, like: '足球' }
            ])
        </script>
    
        <style lang="less">
            .box {
              display: inline-block;
              width: 200px;
              border: dashed blue 1px;
              margin-right: 15px;
              padding: 10px;
            }
        </style>

    子组件

        <template>
          <div>
            <!-- :scope="item" 返回每一项 -->
            <slot v-for="item in arr" :scope="item" />
          </div>
        </template>
    
        <script setup>
            const props = defineProps({
              arr: {
                type: Array,
                default: () => []
              }
            })
        </script>

    效果图

    结语

    在线客服
    服务热线

    服务热线

    4008888355

    微信咨询
    二维码
    返回顶部
    ×二维码

    截屏,微信识别二维码

    打开微信

    微信号已复制,请打开微信添加咨询详情!