实现 Vue/React 组件通信

Vue

父子组件通信

父传子(Props 传值)

使用 defineProps 声明接收的 props

//父组件
<template>
    <div class="container">
        <h1>父传子---父组件</h1>
        <p>输入的值:{{ inputValue }}</p>
        <input type="text" v-model="inputValue" />
        <ChildComponent :title="inputValue" />
    </div>
</template>

<script setup>
import { ref } from 'vue'
import ChildComponent from '../components/ChildComponent.vue'

// 父传子
const inputValue = ref('')
</script>
//子组件
<template>
    <div class="child">
        <h2>父传子---子组件</h2>
        <div class="title">{{ title }}</div>
    </div>
</template>

<script setup>
import { ref, defineModel } from 'vue'

// 父传子
const props = defineProps({
    title: {
        type: String,
    },
})
</script>

子传父(Emit 事件)

使用 defineEmits 声明事件

//父组件
<template>
    <div class="container">
        <h1>子传父---父组件</h1>
        <ChildComponent @update="handleUpdate" />
        <div>{{ valueParent }}</div>
    </div>
</template>

<script setup>
import { ref } from 'vue'
import ChildComponent from '../components/ChildComponent.vue'

// 子传父
const valueParent = ref('')
const handleUpdate = (res) => {
    valueParent.value = res
}
</script>
<template>
    <div class="child">
        <h2>子传父---子组件</h2>
        <button @click="sendData">点击传值</button>
        <button @click="sendData2">点击传值2</button>
    </div>
</template>

<script setup>
import { ref, defineModel } from 'vue'

// 子传父
// 定义一个名为 update 的自定义事件
const emit = defineEmits(['update'])
const sendData = () => {
    // 触发 update 事件,并传递参数,事件名可以根据需要自定义
    emit('update', '子传父')
}
const sendData2 = () => {
    // 触发 update 事件,并传递一个对象作为参数
    emit('update', {
        name: '子传父',
        age: 18,
    })
}
</script>

跨层级通信

Provide / Inject(基础版)

  1. 先使用 Provide 在祖先组件中声明共享数据
  2. 之后使用 Inject 在后代组件中获取数据
  3. 使用响应式数据会自动更新所有注入组件
  4. 数据流向:单向(祖先 → 后代)

祖先组件向任意深层后代传递数据

//父组件
<template>
    <div class="container">
        <h1>跨层级通信---祖先组件</h1>
        <ChildComponent />
        <p>祖先的counter: {{ counter }}</p>
    </div>
</template>

<script setup>
import { ref, provide } from 'vue'
import ChildComponent from '../components/ChildComponent.vue'

// 跨层级通信  爷孙传值
const counter = ref(0)
// 通过provide函数提供名称为 counterName 的数据给后代组件
provide('counterName', counter) // 提供响应式数据
</script>
<template>
    <div class="child">
        <h2>跨层级通信---后代组件</h2>
        <button @click="increment">后代修改: {{ counter }}</button>
    </div>
</template>

<script setup>
import { inject } from 'vue'

// 跨层级通信  爷孙传值
// 通过inject函数注入名称为 counterName 的数据
const counter = inject('counterName') // 注入数据
const increment = () => counter.value++
</script>

Provide / Inject + 方法(进阶版)

跨层级修改数据(类似全局状态)
封装业务逻辑,避免直接修改数据

//父组件
<template>
    <div class="container">
        <h1>跨层级通信Provide / Inject + 方法(进阶版)---祖先组件</h1>
        <ChildComponent />
        <p>祖先的name: {{ user.name }}</p>
    </div>
</template>

<script setup>
import { ref, provide, reactive } from 'vue'
import ChildComponent from '../components/ChildComponent.vue'

// 跨层级通信 Provide / Inject + 方法(进阶版)
const user = reactive({ name: 'Alice' })
provide('userContext', {
    // 提供数据+方法
    user,
    updateName: (newName) => {
        if (user.name != newName) {
            user.name = newName
        } else {
            user.name = 'Alice'
        }
    },
})
</script>
//子组件
<template>
    <div class="child">
        <h2>跨层级通信Provide / Inject + 方法(进阶版)---后代组件</h2>
        <p>当前用户: {{ user.name }}</p>
        <button @click="updateName('Bob')">改名</button>
    </div>
</template>

<!-- 跨层级组件通信 -->
<script setup>
import { inject } from 'vue'

// 跨层级通信 Provide / Inject + 方法(进阶版)
const { user, updateName } = inject('userContext')

</script>

Pinia

Pinia 在任意组件中使用(无需关心层级)
使用前要创建一个 js/ts 文件来存储数据信息
在需要使用的地方,引入数据文件来获取或修改数据信息

import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
    state: () => ({
        profile: { name: 'Alice', age: 25 },
    }),
    actions: {
        updateProfile(newProfile) {
            this.profile = { ...this.profile, ...newProfile }
        },
    },
})

<template>
    <div class="container">
        <div>Pinia---任意组件(无需关心层级)</div>
        <p>{{ userStore.profile.name }}</p>
        <p>{{ userStore.profile.age }}</p>
        <button @click="userInfo">修改年龄</button>
    </div>
</template>

<script setup>
import { useUserStore } from '../../stores/user'

const userStore = useUserStore()
const userInfo = () => {
    userStore.updateProfile({ age: 26 })
}
</script>

模板引用通信

v-model 双向绑定

v-model:modelValue + @update:modelValue 的语法糖:

//父组件
<template>
    <div class="container">
        <h1>父组件 v-model 双向绑定</h1>
        <ChildComponent v-model="inputValue" />
        <div>输入值:{{ inputValue }}</div>
    </div>
</template>
<script setup>
import { ref } from 'vue'
import ChildComponent from '../components/ChildComponent.vue'
const inputValue = ref('')
</script>
//子组件
<template>
    <div class="child">
        <h2>子组件 v-model 双向绑定</h2>
        <input
            :value="modelValue"
            @input="$emit('update:modelValue', $event.target.value)"
        />
    </div>
</template>
<script setup>
import { ref } from 'vue'
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>

ref + Expose 模版引用

调用子组件内的方法
访问子组件中的DOM

<template>
    <ChildComponent ref="childRef" />
    <button @click="handleClick">播放</button>
</template>

<script setup>
import { ref } from 'vue'
import ChildComponent from '../components/ChildComponent.vue'
const childRef = ref(null)
const handleClick = () => {
    childRef.value.play()
}
</script>
<template>
    <input type="text" :value="valueUse" />
</template>
<script setup>
import { ref } from 'vue'
const valueUse = ref('')
const play = () => {
    console.log('播放视频')
    valueUse.value = '播放视频'
}
defineExpose({ play }) // 暴露方法
</script>

React

父子组件通信

父传子(props传值)

//父组件
import React, { useState } from 'react'
import Child01 from '../components/child01'

export default function index() {
    const [name, setName] = useState('张三')
    const [age, setAge] = useState(18)
    const changeInfo = () => {
        setName('李四')
        setAge(20)
    }
    return (
        <div>
            <h1>react01</h1>
            <Child01 name={name} age={age}>
                {/* <div>123</div> */}
            </Child01>
            <button
                onClick={() => {
                    changeInfo()
                }}
            >
                点击
            </button>
        </div>
    )
}

//子组件
import React from 'react'

//多个参数的 函数
interface IProps {
    name: string
    age: number
    // children: React.ReactNode
}
export default function index(props: IProps) {
    return (
        <div>
            <div>{props.name}</div>
            <div>{props.age}</div>
            //<div>{props.children}</div>
        </div>
    )
}

如果需要在封装的组件内嵌套内容,则需要在子组件中声明一个 children: React.ReactNode 属性,就可以接收并渲染其内部嵌套的任何内容。

子传父(回调函数)

//父组件
import React, { useState } from 'react'
import Child02 from '../components/child02'

export default function index() {
    const [msg, setMsg] = useState('')
    const handleEvent = (msg: string) => {
        console.log(msg)
        setMsg(msg)
    }
    return (
        <div>
            <h1>react02</h1>
            <Child02 onEvent={handleEvent} />
            <div>{msg}</div>
        </div>
    )
}

//子组件
import React from 'react'

interface IProps {
    onEvent: (msg: string) => void
}
export default function index(props: IProps) {
    return (
        <div>
            <button onClick={() => props.onEvent('点击了子组件')}>点击</button>
        </div>
    )
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值