vue2与vue3组件通信方式的差异

453 阅读1分钟

一、父-子传递

1. props

  • vue2
 //传递
  <Child :count="count" :userInfo="userInfo" :changeCount="changeCount"></Child>
 //接收 多种方式
  数组形式:props: ['count', 'userInfo', 'changeCount']
  对象形式:props: {count: Number,userInfo: Object,changeCount: Function }
  配置对象形式:
  props: { 
    count: {
        type: Number, 
        required: true, // 必填
        default: 4 // 默认值 
        // required 和 default 是互斥的,如果必填还设置默认值没有意义 }, 
    userInfo: {type: Object,  default() {  return { name: '老六',  age: 22} } },       
    changeCount: {type: Function,required: true}
   },
  • vue3
//传递
<child-components :list="list"></child-components>
//接收
<script setup>
import { defineProps } from 'vue'
const props = defineProps({  
    list: {    
    type: Array,    
    default: () => []
    }
})
</script>

2. v-model

  • vue2
 //传递
 <CustomInput v-model="keyword">   </CustomInput>
 //接收
<input type="text" :value="value" @input="changeValue">
props: ['value'],
methods: {
 changeValue(e) {
      this.$emit('input', e.target.value);
  }
}

3. .Sync 父子组件同步数据

  • vue2
 //传递
 <Child1 :msg.sync="string"></Child1>
 data() {
      return {
          string: "我爱你",
      };
},
接收(调用)
<button @click="changeParentMsg">修改父组件传过来的数据</button>
props: ['msg'],
     methods: {
     changeParentMsg() {
          this.$emit('update:msg', "666")
     }
}
  • vue3
 //传递
 <ChildComponent v-model:title="pageTitle" />
 //接收(调用)
 <script setup>
import { defineEmits } from 'vue'
const emits = defineEmits(['add'])
const handleSubmit = () => {  
    const arr = props.list 
    arr.push(value.value)  
    emits('update:list', arr)  
    value.value = ''
}
</script>

4. refs

  • vue2
    $refs可以获取到组件实例,拿到组件实例可以拿到组件中的数据,在标签上使用的时候获取到的是标签
  • vu3
//父组件
<div>{{chileRefs?.count}}/div>
<child-components ref="childRefs"></child-components>
<script setup>
    import { ref } from 'vue'
    import ChildComponents from './child.vue'
    const childRefs = ref(null)
</script>
//子组件
<script setup>
    import { ref, defineExpose } from 'vue'
    const count = ref(20)
    const handleAdd = () => {  
        count.value=50
    }
    defineExpose({ count })
 注意:默认情况下,setup 组件是关闭的,通过模板 ref 获取组件的公共实例。
 如果需要公开,需要通过defineExpose API 公开。
</script>

5.slot

  • vue2
// 子组件
<template>
    <div class="box">
        <slot>header</slot>
        <div>--------------------------------</div>
        <ul>
            <li v-for="(todo, index) in todos" :key="todo.id">
                <slot name="content" :row="todo" :$index="index" msg="hello">内容</slot>
            </li>
        </ul>
        <div>--------------------------------</div>
        <slot name="footer">footer</slot>
    </div>
</template>
// 父组件
<template>
    <div class="box">
        <List :todos="todos">
            <!--  v-slot:default 可以省略,是默认的普通插槽 -->
            <template v-slot:default>
                父组件传的Header
            </template>
            <!-- # 是v-slot指令的简写 -->
            <template #footer>
                父组件传的Footer
            </template>
            <!--
                data是一个对象,这个对象中放的数据是子组件绑定在slot标签上的属性组成的对象
            -->
            <template v-slot:content="data">
                <div :style="{ background: data.row.isComplete ? 'green' : '' }">
                    内容: {{ data.row.text }} - 
                    是否完成: {{ data.row.isComplete ? '完成' : '未完成' }} - 
                    msg: {{ data.msg }}
                </div>
            </template>
        </List>
    </div>
</template>

二、子-父传递

1.emit

  • vue2
 //传递
   <Event1 :count="count" @changeCount="changeCount"></Event1>
 //接收(调用)
  <button @click="$emit('changeCount')">修改父组件的count</button>
  $emit('changeCount', 50)
  参数一:触发的事件类型 参数二: 传递的参数
  • vue3
//传递
<child-components @add="handleAdd"></child-components>
//接收(调用)
<button @click="handleSubmit" class="btn btn-primary" type="button"> add</button>
//js
<script setup>
import { defineEmits } from 'vue'
const emits = defineEmits(['add'])
const handleSubmit = () => {  
    emits('add', value.value)  
    value.value = ''
}
</script>

三、跨组件

1. EvenBus 事件总线

  • vue2
//安装
new Vue({
    beforeCreate() {
        Vue.prototype.$bus = this; // 安装总线
    },
    mounted() {
         this.$bus.$on('receiveParams', this.receiveParams)
    },
    methods: {
         receiveParams(params) {
             console.log('接收到的参数', params);
         }
    }
}
//接收(调用)
<button @click="$bus.$emit('receiveParams', 25)">传给Child2参数</button>
  • vue3
 Vue 3 中移除了 eventBus,但可以借助第三方工具来完成。
 Vue 官方推荐使用 mitt 或 tiny-emitter。
 在大多数情况下,不建议使用全局事件总线来实现组件通信。
 虽然比较简单粗暴,但是维护事件总线从长远来看是 个大问题,这里就不解释了。

2. provide与inject

provide/inject是 Vue 中提供的一对 API。无论层级多深,API 都可以实现父组件到子组件的数据传递。

  • vue2
//传递
provide() {
    return {
        content1: this.content1,
        content2: this.content2,
        changeContent1: this.changeContent1,
        changeContent2: this.changeContent2
    }
},
//接收
inject: ['content1', 'content2', 'changeContent1', 'changeContent2']
  • vue3
//传递
<script setup>
    import { ref, provide } from 'vue'
    import ChildComponents from './child.vue'
    const list = ref(['JavaScript', 'HTML', 'CSS'])
    const value = ref('')
    provide('list', list.value)
    const handleAdd = () => {
      list.value.push(value.value)
      value.value = ''
    }
</script>
//接收
<template> 
    <ul class="parent list-group">    
        <li class="list-group-item" v-for="i in list" :key="i">{{ i }}</li>  
    </ul>
</template>
<script setup>
    import { inject } from 'vue'
    const list = inject('list')
</script>
注意:使用 provide 进行数据传输时,尽量使用 readonly 封装数据,避免子组件修改父组件传递的数据。

3. vuex/pinia

Vuex 和 Pinia 是 Vue 3 中的状态管理工具,使用这两个工具可以轻松实现组件通信。