Skip to content

Props 传参

父传子

vue
<script setup lang="ts">
import User from "@/components/User.vue";
</script>

<template>
    <User name="iGma" />
</template>
vue
<script setup lang="ts">
defineProps(['name'])
</script>

<template>
    <p>{{ name }}</p>
</template>

子传父

vue
<script setup lang="ts">
import User from "@/components/User.vue";

function getUserData(data: string) {
    console.log('子组件返回的数据', data);
}
</script>

<template>
    <User name="iGma" :sendData="getUserData" />
</template>
vue
<script setup lang="ts">
defineProps(['name', 'sendData'])
</script>

<template>
    <p>{{ name }}</p>
    <button @click="sendData('子组件数据')">发送数据</button>
</template>

自定义事件

自定义事件是由子组件向父组件发射的事件,在发射事件的同时也可传递参数。

vue
<script setup lang="ts">
import User from "@/components/User.vue";

function getUserData(data: string) {
    console.log('子组件返回的数据', data);
}
</script>

<template>
    <User @getUserData="getUserData" />
</template>
vue
<script setup lang="ts">
const event = defineEmits(['getUserData']);

function sendData() {
    event('getUserData', 'iGma')
}
</script>

<template>
    <button @click="sendData">发送数据</button>
</template>

mitt

mitt 适用于任何组件间传递数据,类似于 Vue2 中的全局事件总线

安装 mitt

sh
npm i mitt

创建资源站 @/utils/emitter.ts

ts
import mitt from "mitt";

export default mitt();

发送数据组件:App.vue;接收数据组件:User.vue

vue
<script setup lang="ts">
    import User from "@/components/User.vue";
    import emitter from "@/utils/emitter.ts";

    function sendData() {
        emitter.emit('appSendData', 'iGma');
    }
</script>

<template>
    <button @click="sendData">发送数据</button>
    <User />
</template>
vue
<script setup lang="ts">
    import emitter from "@/utils/emitter.ts";
    import {onUnmounted} from "vue";

    emitter.on('appSendData', (data) => {
        console.log('data', data);
    })

    onUnmounted(() => {
        emitter.off('appSendData');
    })
</script>

<template></template>

v-model

双向绑定语法,即可以子传父,也可父传子。

vue
<script setup lang="ts">
import iGmaInput from "@/components/iGmaInput.vue";
import {ref} from "vue";

let msg = ref();
</script>

<template>
    <iGmaInput v-model="msg" />
</template>
vue
<script setup lang="ts">
defineProps(['modelValue']);
const emit = defineEmits(['update:modelValue']);
</script>

<template>
    <input
        :value="modelValue"
        @input="emit('update:modelValue', (<HTMLInputElement>$event.target).value)"
    />
</template>

在自定义组件中使用 v-model 默认会给子组件传入一个 modelValue 属性,同时通过 update:modelValue 返回数据。也可以通过 v-model:xxx 传递多组双向绑定数据。

vue
<script setup lang="ts">
import iGmaInput from "@/components/iGmaInput.vue";
import {ref} from "vue";

let account = ref();
let password = ref();
</script>

<template>
    <iGmaInput v-model:account="account" v-model:password="password" />
</template>
vue
<script setup lang="ts">
defineProps(['account', 'password']);
const emit = defineEmits(['update:account', 'update:password']);
</script>

<template>
    <input
        :value="account"
        @input="emit('update:account', (<HTMLInputElement>$event.target).value)"
    />
    <input
        :value="password"
        @input="emit('update:password', (<HTMLInputElement>$event.target).value)"
    />
</template>

$attrs

适用于祖孙之间传递数据,在父组件中实现过度即可。

vue
<script setup lang="ts">
import A from "@/components/A.vue";
import {reactive} from "vue";

let data = reactive({a: 1, b: 2, c: 3});
</script>

<template>
    <A v-bind="data" />
</template>
vue
<script setup lang="ts">
import B from "@/components/B.vue";
</script>

<template>
    <B v-bind="$attrs" />
</template>
vue
<script setup lang="ts">
defineProps(['a', 'b', 'c']);
</script>

<template>
    <p>a: {{ a }}</p>
    <p>b: {{ b }}</p>
    <p>c: {{ c }}</p>
</template>

$refs 和 $parent

父操作子数据 $refs

vue
<script setup lang="ts">
import User from "@/components/User.vue";

function updateUserData(refs:any) {
    refs['userRef'].info.age += 5;
}
</script>

<template>
    <User ref="userRef" />
    <button @click="updateUserData($refs)">修改子组件的数据</button>
</template>
vue
<script setup lang="ts">
import {reactive} from "vue";

let info = reactive({name: 'iGma', age: 18});

defineExpose({info});	// 对外暴露
</script>

<template>
    <p>姓名: {{info.name}}</p>
    <p>年龄: {{info.age}}</p>
</template>

子修改父数据 $parent

vue
<script setup lang="ts">
import User from "@/components/User.vue";
import {reactive} from "vue";

let data = reactive({name: 'iGma', age: 18});

defineExpose({data})
</script>

<template>
    <p>姓名: {{data.name}}</p>
    <p>年龄: {{data.age}}</p>
    <User />
</template>
vue
<script setup lang="ts">
function updateParentData(parent: any) {
    parent.data.age += 5;
}
</script>

<template>
    <button @click="updateParentData($parent)">修改父组件数据</button>
</template>

Provide 和 Inject

适用于祖孙之间传递数据,不同于 $attrs 的是,通过 provideinject 可以不打扰中间的组件,同时只要组件中使用 provide 发送数据后,组件的所有后辈组件都可以使用 inject 接收。

vue
<script setup lang="ts">
import A from "@/components/A.vue";
import {provide, ref} from "vue";

let name = ref('iGma');
let age = ref(18);

// 向后代提供数据
provide('name', name);
provide('age', age);
</script>

<template>
    <A />
</template>
vue
<script setup lang="ts">
import B from "@/components/B.vue";
</script>

<template>
    <B />
</template>
vue
<script setup lang="ts">
import {inject} from "vue";

// 接收祖辈传来的数据
let name = inject('name', '无名氏');
let age = inject('age', 20);
</script>

<template>
    <p>姓名: {{name}}</p>
    <p>年龄: {{age}}</p>
    <button @click="age++">修改年龄</button>
</template>

插槽

默认插槽

vue
<script setup lang="ts">
    import User from "@/components/User.vue";
</script>

<template>
    <User>
        <h2>iGma</h2>
    </User>
</template>
vue
<script setup lang="ts">

</script>

<template>
    <div class="user">
        <slot />
    </div>
</template>

具名插槽

vue
<script setup lang="ts">
import User from "@/components/User.vue";
</script>

<template>
    <User>
        <template #s1>
            <h2>姓名:iGma</h2>
        </template>
        <template #s2>
            <h2>年龄:18</h2>
        </template>
    </User>
</template>
vue
<script setup lang="ts">

</script>

<template>
    <div class="user">
        <slot name="s1"/>
        <slot name="s2"/>
    </div>
</template>

作用域插槽

vue
<script setup lang="ts">
import User from "@/components/User.vue";
</script>

<template>
    <User>
        <template #s1="{hobby}">
            <ul>
                <li v-for="(item, index) in hobby" :key="index">{{item}}</li>
            </ul>
        </template>
    </User>
    <User>
        <template #s1="{hobby}">
            <ol>
                <li v-for="(item, index) in hobby" :key="index">{{item}}</li>
            </ol>
        </template>
    </User>
</template>
vue
<script setup lang="ts">
import {reactive} from "vue";

let hobby = reactive(['抽烟', '喝酒', '烫头']);
</script>

<template>
    <div class="user">
        <slot name="s1" :hobby="hobby"/>
    </div>
</template>

基于 MIT 许可发布