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

📌前言

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

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

搭建父子组件

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

目录结构

父组件/parent/index.vue

html 复制代码
<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

html 复制代码
<template>
  <div>
    <div>子组件</div>
  </div>
</template>

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

<style scoped></style>

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

一、🍉通信

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

1、父传子

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

父组件

html 复制代码
<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的需要自行转换一下

withDefaults是ts特有的写法 , 使用js的话不需要写

  • ?的意思是非必传项
  • 如果没有传并且没有给默认值打印出来是undefined,页面渲染不会显示
  • 在html渲染的时候props.可以不写 , 不过建议还是写上
html 复制代码
<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>

效果

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

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

父组件

使用childEmit接收子组件事件

html 复制代码
<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
html 复制代码
<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>

效果

点击按钮

3、defineExpose(暴露)

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

注意:vue3 3.2+新增

父组件

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

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

html 复制代码
<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>

子组件

html 复制代码
<template>
  <div>
    <div>子组件</div>
  </div>
</template>

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

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

效果

二、🍈插槽

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

父组件

  • 写在子标签内部
  • 默认 : 直接写
  • 支持给具体名称,具体调用
  • 支持调用多次
html 复制代码
<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>

子组件

html 复制代码
<template>
  <div>
    <div>子组件</div>
    <div><slot></slot></div>
    <div><slot name="title"></slot></div>
    <div><slot name="body"></slot></div>
  </div>
</template>

效果

三、🍇双向数据绑定

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

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

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

注意:vue3 3.4+新增

1、基础使用

父组件

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

html 复制代码
<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>

子组件

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

html 复制代码
<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的值 , 可以看到父组件的值也被改变了

2、进阶使用

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

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

父组件

  • 需要绑定多个数据 v-model:[名称]直接写就行
  • 接收时第一个参数是[名称]
html 复制代码
<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>

子组件

html 复制代码
<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>

效果

📚总结

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

相关推荐
深蓝AI1 小时前
45 万星的开源逆向 MCP:REA 让 Agent 无源码读懂任意应用,把证据和边界写进设计
人工智能·程序员
卸任11 小时前
Vibe coding工具使用心得
前端·ai编程
brien912 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance12 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M12 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写12 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李12 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易13 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马13 小时前
前沿视界
前端
kyriewen13 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员