likes
comments
collection
share

🔥 一篇学会vue3父子组件操作(通信、插槽、双向数据绑定)

作者站长头像
站长
· 阅读数 1

📌前言

关于一次性总结的文章比较少 , 加上最近写代码发现了父子双向绑定defineModel(),因此便写下这一篇。

和我其他的文章一样 , 我会根据我的理解把经常使用的简单的描述出来 , 比较冷门的可能不会写, 如有问题和缺少 , 还请大佬补充。

搭建父子组件

要进行父子组件的操作 , 首先先搭建起来 , 下面是简单搭建。

目录结构

🔥 一篇学会vue3父子组件操作(通信、插槽、双向数据绑定)

父组件/parent/index.vue

<template>
  <div>父组件</div>
  <Child> </Child>
</template>

<script setup lang="ts">
// 导出子组件
import Child from "./components/Child.vue";
</script>

<style lang="scss" scoped></style>

子组件/parent/components/Child.vue

<template>
  <div>
    <div>子组件</div>
  </div>
</template>

<script setup lang="ts"></script>

<style scoped></style>

这样最简单的搭建就完成了

🔥 一篇学会vue3父子组件操作(通信、插槽、双向数据绑定)

一、🍉通信

这是最基础的操作了 , 相信大家都会了 , 就简单描写过一遍。

1、父传子

  • 和使用内置组件一样 , 只需要在子组件的标签上打:冒号
  • 左侧子组件接收名称 , 右侧父组件传入的值就行。

父组件

<template>
<div>父组件</div>
<hr />
<!-- childValue子组件接收的名称 可以写成child-value -->
<Child :child-value="parentValue"></Child>
</template>

<script setup lang="ts">
// 导出子组件
import { ref } from "vue";
import Child from "./components/Child.vue";
// 定义一个父组件值
let parentValue = ref<string>("父组件值");
</script>

子组件

代码都是ts的写法 , 如果要js的需要自行转换一下

withDefaultsts特有的写法 , 使用js的话不需要写

  • ?的意思是非必传项
  • 如果没有传并且没有给默认值打印出来是undefined,页面渲染不会显示
  • html渲染的时候props.可以不写 , 不过建议还是写上
<template>
  <div>
    <div>子组件</div>
    <div>没有设置默认值: {{ props.notDefaultValue }}</div>
    <div>设置了默认值: {{ props.defaultValue }}</div>
    <div>接收到的父组件值: {{ props.childValue }}</div>
    <div>接收到的父组件值(不写props.): {{ childValue }}</div>
  </div>
</template>

<script setup lang="ts">
// 接收父组件值
const props = withDefaults(
  defineProps<{
    defaultValue?: string;
    notDefaultValue?: string;
    childValue: string;
  }>(),
  {
    defaultValue: "默认值"
  }
);
</script>

效果

🔥 一篇学会vue3父子组件操作(通信、插槽、双向数据绑定)

2、子传父(自定义事件)

  • 因为是事件所以不是使用:冒号而是@符号
  • 左侧子组件事件名称 , 右侧父组件接收函数。

父组件

使用childEmit接收子组件事件

<template>
  <div>父组件</div>
  <hr />
  <!-- childEmit可以写成child-emit -->
  <Child @child-emit="childEmit"></Child>
</template>

<script setup lang="ts">
// 导出子组件
import Child from "./components/Child.vue";
// 子组件事件
const childEmit = (value: string) => {
  console.log(value);
};
</script>

子组件

  • 使用defineEmits泛型定义一个对象 , 参数1:事件名 , 其他参数:事件参数 , 有几个传几个,可以不传
  • 使用事件的方式是调用emit并传参告诉它调用的事件和事件参数。
  • 可以设置事件返回值 , 一般没有 , 写void
<template>
  <div>
    <div>子组件</div>
    <!-- 参数1:事件名 , 参数2....:传输数据 -->
    <button @click="emit('childEmit', '哈哈')">发送子组件定义事件</button>
  </div>
</template>

<script setup lang="ts">
// 设置事件 参数1:事件名 , 参数2....:传输数据
const emit = defineEmits<{
  (e: "childEmit", value: string): void;
  (e: "childEmit2", value: string, value2: number): void;
  (e: "childEmit3"): void;
}>();
</script>

效果

🔥 一篇学会vue3父子组件操作(通信、插槽、双向数据绑定)

点击按钮 🔥 一篇学会vue3父子组件操作(通信、插槽、双向数据绑定)

3、defineExpose(暴露)

将子组件的属性和函数暴露给父组件调用 , 如果使用的灵活是非常方便的。

注意:vue3 3.2+新增

父组件

  • 拿到子组件的DOM元素
  • 直接点出暴露属性和函数

注意: 不能在setup函数下直接调用,DOM元素还未挂载

<template>
  <div>父组件</div>
  子组件属性: {{ ChildRef?.msg }}
  <hr />
  <Child ref="ChildRef"></Child>
</template>

<script setup lang="ts">
// 导出子组件
import { onMounted, ref } from "vue";
import Child from "./components/Child.vue";
// 获取子组件DOM元素
let ChildRef = ref<InstanceType<typeof Child> | null>(null);

onMounted(() => {
  // 调用子组件方法
  ChildRef.value?.childFn();
});
</script>

子组件

<template>
  <div>
    <div>子组件</div>
  </div>
</template>

<script setup lang="ts">
function childFn() {
  console.log("我是子组件函数");
}

const msg = "子组件msg";
defineExpose({
  msg,
  childFn
});
</script>

效果

🔥 一篇学会vue3父子组件操作(通信、插槽、双向数据绑定)

🔥 一篇学会vue3父子组件操作(通信、插槽、双向数据绑定)

二、🍈插槽

有时候组件会在不同的页面上产生不同的变化 , 为了组件的兼容性 , 插槽就不可或缺了

父组件

  • 写在子标签内部
  • 默认 : 直接写
  • 支持给具体名称,具体调用
  • 支持调用多次
<template>
  <div>父组件</div>
  <hr />
  <Child>
    默认数据
    <template #title>title</template>
    <template #body>body</template>
  </Child>
</template>

<script setup lang="ts">
// 导出子组件
import Child from "./components/Child.vue";
</script>

子组件

<template>
  <div>
    <div>子组件</div>
    <div><slot></slot></div>
    <div><slot name="title"></slot></div>
    <div><slot name="body"></slot></div>
  </div>
</template>

效果

🔥 一篇学会vue3父子组件操作(通信、插槽、双向数据绑定)

三、🍇双向数据绑定

这也是我最近写项目时发现的

因为使用普通的父子通信 , 我感到非常的麻烦

所以我在想能不能像input一样绑定一个值呢 , 通过查询资料也是成功发现并学会了。

注意:vue3 3.4+新增

1、基础使用

父组件

可以看到我使用v-model将值传给了子组件

<template>
  <div>父组件</div>
  {{ value }}
  <hr />
  <Child v-model="value"> </Child>
</template>

<script setup lang="ts">
// 导出子组件
import { ref } from "vue";
import Child from "./components/Child.vue";
// 定义值
let value = ref<string>("默认值");
</script>

子组件

可以使用默认直接接收 , 也可以对接收值进行处理。

<template>
  <div>
    <div>子组件</div>
    <input v-model="value" />
  </div>
</template>

<script setup lang="ts">
//接收父组件v-model的值
// 正常接收
let value = defineModel();

// 配置接收
let value = defineModel({
  // 是否必传
  required: true,
  // 默认值
  default: "默认内容",
  // 数据类型
  type: String,
  // 校验器 val-父组件传来的值
  validator: (val: any) => {
    // 返回false否则会发出警告
    return val;
  }
})
</script>

效果

接收到之后我使用input改变value的值 , 可以看到父组件的值也被改变了

🔥 一篇学会vue3父子组件操作(通信、插槽、双向数据绑定)

2、进阶使用

我们已经可以基本使用双向绑定了 , 但是它还有更多的功能

  • 绑定多个数据
  • 数据初始化

父组件

  • 需要绑定多个数据 v-model:[名称]直接写就行
  • 接收时第一个参数是[名称]
<template>
  <div>父组件</div>
  <div>{{ title }}</div>
  <div>{{ content }}</div>
  <hr />
  <Child v-model:title="title" v-model:content="content"> </Child>
</template>

<script setup lang="ts">
// 导出子组件
import { ref } from "vue";
import Child from "./components/Child.vue";
// 定义值
let title = ref<string>("标题");
let content = ref<string>("内容");
</script>

子组件

<template>
  <div>
    <div>子组件</div>
    <input v-model="title" />
    <input v-model="content" />
  </div>
</template>

<script setup lang="ts">
let title = defineModel("title");
let content = defineModel("content", {
  // 是否必传
  required: true,
  // 默认值
  default: "默认内容",
  // 数据类型
  type: String,
  // 校验器 val-父组件传来的值
  validator: (val: any) => {
    // 返回false否则会发出警告
    return val;
  }
});
</script>

效果

🔥 一篇学会vue3父子组件操作(通信、插槽、双向数据绑定)

📚总结

组件的应用还是很重要的 , 一个好的组件能不仅能加快开发时间 , 还能增强可维护性。