Vue3中父子组件的通信

392 阅读1分钟

我正在参加「掘金·启航计划」

父子组件通信时常用的属性:

  • 1、props和$emit;(接收父组件传入的数据,通过事件向父组件传递数据)
  • 2、$attrs;(接收未通过props声明时父组件传入的数据)
  • 3、defineProps和defineEmits;
  • 4、v-model:prop 和 $emit('update:prop', value);

1、父组件向子组件传递数据,子组件通过触发父组件事件从而向父组件回传数据;

写法一:官网参考:https://cn.vuejs.org/api/composition-api-setup.html
* a、在子组件中先通过props声明父组件要传入的属性;
* b、在子组件的setup钩子函数中通过props获取父组件传入的值;
写法二:<script setup>语法糖写法
* a、子组件通过defineProps,defineEmits提前声明父组件要传入的数据和要emit的方法;
* b、父组件使用子组件时传入数据
    // 写法1:
    // parent.vue的内容
    <template>
      <Child :msg="msg" :msg2="msg2" @updateEmit="updateEmit"></Child>
    </template>
    <script lang="ts">
        import { ref} from 'vue'
        import Child from './child2.vue'

        export default {
          components: {
            Child
          },
          setup () {
            const msg = ref('hello')
            const msg2 = ref('world')
            const updateEmit = (val)=>{
              msg.value = msg.value + '_' + val
            }
            return {
              msg,
              msg2,
              updateEmit
            }
          }
        }
        </script>
        
       //child.vue内容:
        <template>
          子组件: msg:{{msg}};---msg2:{{msg2}};---attrs: {{attrs}}
          <button @click="update">更新</button>
        </template>
        <script lang='ts'>
            export default {
               // 通过props提前声明父组件要传入的数据,如果没声明的话在setup中的props属性中获取不到,需通过attrs属性获取
               props: ['msg'],
               
               setup(props, {attrs, emit}) {
                // attrs中可以获取父组件使用子组件时传入的属性且该属性在子组件中未声明过;
                // 将 `props` 转为一个其中全是 ref 的对象,然后解构
                // const { msg2 } = toRefs(props)  // msg2: ''
                const { msg2 } = toRefs(attrs)  // msg2:world
                
                console.log('child的中接收到的props:', props);
                console.log('child的中接收到的attrs:', attrs);
                
                const update = () => emit('updateEmit', '我是子组件传递的内容')
                return {
                  msg2,
                  attrs,
                  update
                }
              }
            }
        </script>
    
  // 写法2:
    // parent.vue
    <template>
      <MyChild :msg="msg" :msg2="msg2" v-model:flag="flag" @update="handleUpdate"  />
    </template>

    <script lang="ts" setup>
        import MyChild from './child.vue'
        import { ref } from 'vue';

        const msg = ref('hello')
        const msg2 = ref('world')
        const flag = ref('false')
        
        const handleUpdate = (s) => {
          console.log(s)  // 我是子组件传递的内容
          msg.value = s
        }
    </script>
    
    // child.vue
    <template>
      msg: {{msg}}==={{flag}}<br/>
      <button @click="handleUpdate">更新</button>
    </template>

    <script lang='ts' setup>
      import { defineProps, defineEmits } from 'vue';
       // 子组件接收父组件传递的数据
       const props = defineProps({
         msg: String,
         flag: {
           type: Boolean,
           default: false
         }
       })
       const emit = defineEmits(['update', 'update:flag'])
       
       const handleUpdate = () => {
         //  子组件触发父组件的事件,并在父组件中修改传递的props 
         emit('update','我是子组件传递的内容')
         // 在当前组件修改父组件传入的数据
         emit('update:flag', true)
       }
    </script>

image.png

2、父组件触发子组件的事件

* a、在子组件中定义好事件,(如果是<script setup>语法糖写法,需要通过defineExpose向外暴露该方法)
* b、父组件在使用子组件时,通过ref属性定义对子组件的模板引用
* c、父组件通过子组件的模板引用触发子组件的事件;
// parent.vue文件
<script >
import Children from './components/child.vue'
import { defineComponent, ref } from 'vue'

export default defineComponent({
  components: {
    Children
  },
  setup() {
    const msg = ref('parent msg')
    const childRef = ref()

    // 父组件触发子组件的事件
    function handleValid() {
      const flag = childRef.value?.validForm()
      console.log('表单校验结果:', flag);
      return flag
    }
    return {
      msg,
      childRef,
      handleValid
    }
  }
})
</script>
<template>
  <Children ref="childRef" :msg="msg"></Children>
  <button @click="handleValid" >parent组件触发child事件</button>
</template>
    
// child.vue文件
<template>
  <div> child组件,接收到的msg为---{{ msg }} </div>
</template>

<script lang='ts'>
import { defineComponent, ref } from 'vue'
export default defineComponent({
  props: {
    msg: {
      type: String,
      default: ''
    }
  },
  setup(props) {
    // 表单校验,由父组件触发
    const validForm = () => {
      console.log('子组件表单校验触发了');
      return true
    }
    const b = ref('99999')
    return {
      validForm,
      b
    }
  }
})
</script>

// <script setup>语法糖写法:

// parent.vue文件
<template>
  <Children ref="childRef" :msg="msg" />
  <button @click="handleValid">parent组件触发child事件</button>
</template>
<script setup >
import Children from './components/child.vue'
import { ref } from 'vue'

const msg = ref('parent msg')
const childRef = ref()

// 父组件触发子组件的事件
function handleValid() {
  const flag = childRef.value?.validChild()
  console.log('校验结果:', flag)
  return flag
}
</script>

// child.vue文件
<template>
  <h3>我是子组件,接收到的信息:{{ msg }}</h3>
</template>

<script setup>
import { defineExpose } from 'vue'
// 接收父组件传递的数据
const props = defineProps({
  msg: String,
})

// 子组件校验,由父组件触发 
function validChild() {
  console.log('child 的valid校验触发了');
  return true
}

// 向外暴露的属性/方法;父组件可通过$refs模板引用进行访问
defineExpose({ 
  validChild
})
</script>

image.png