props 、emits 、组件上的v-model(详细版)

props (父组件 -> 子组件通信)

一、声明props的三种方式

1.使用字符串数组的方式声明props

const props = defineProps(["num", "name", "isShow", "arrList", "objList"]);

2.使用对象的方式声明props

对象方式传递props 复制代码
const props = defineProps({
  num: Number,
  name: String,
  isShow: Boolean,
  arrList: Array,
  objList: Object,
});

其中,属性名为props的名称,属性值为props预期类型的构造函数

3.带有props校验和默认值的props声明

带有props校验和默认值的props声明 复制代码
const props = defineProps({
  num: {
    type: Number,
    //设置必传
    required: true,
  },
  name: {
    type: String,
    default: "张三",
  },
  isShow: {
    type: Boolean,
    default: true,
  },
  arrList: {
    type: Array,
    default: () => {
      return [1, 2, 3];
    },
  },
  objList: {
    type: Object,
    default: (rawProps: any) => {
      //此处可以拿到原始prop
      console.log(rawProps);
      return { name: "张三", age: 18 };
    },
  },
});

二、响应式解构props

props为Vue封装的响应式对象,当我们直接解构props时,会将属性值提取为普通变量,使得解构后的值失去了响应性,后续props更新时,解构后的变量不会发生响应式变化

因此,如果想要解构后的值依旧保持响应性,我们可以使用Vue提供的toRefs来解构,它可以将响应式对象的每个属性转换为对应的 ref对象,使得解构后仍保留响应性,值得注意的是,通过toRefs解构后的值,因为被转换为了ref,因此访问的时候需要带上.value

响应式解构props 复制代码
import { onMounted, toRefs } from "vue";
const props = defineProps({
  num: {
    type: Number,
    //设置必传
    required: true,
  },
  name: {
    type: String,
    default: "张三",
  },
  isShow: {
    type: Boolean,
    default: true,
  },
  arrList: {
    type: Array,
    default: () => {
      return [1, 2, 3];
    },
  },
  objList: {
    type: Object,
    default: (rawProps: any) => {
      //此处可以拿到原始prop
      console.log(rawProps);
      return { name: "张三", age: 18 };
    },
  },
});
const { num, name, isShow, arrList, objList } = toRefs(props);
onMounted(() => {
  console.log(num.value);
  console.log(name.value);
  console.log(isShow.value);
  console.log(arrList.value);
  console.log(objList.value);
});

三、props的命名规范

在子组件定义props时,使用小驼峰命名法(camelCase

在父组件传递props时,使用短线命名法(kebab-case

子组件使用小驼峰命名 复制代码
<script setup lang="ts">
const props = defineProps({
  arrList: {
    type: Array,
    default: () => {
      return [1, 2, 3];
    },
  },
  objList: {
    type: Object,
    default: () => {
      return { name: "张三", age: 18 };
    },
  },
});
</script>
父组件使用短线命名 复制代码
<template>
  <div>
    <TestCom :arr-list="arrList" :obj-list="objList" />
  </div>
</template>

四、单向数据流

<math xmlns="http://www.w3.org/1998/Math/MathML"> p r o p s 遵循单向数据流的原则,即数据通过父组件流向子组件 \color{red}{props遵循单向数据流的原则,即数据通过父组件流向子组件} </math>props遵循单向数据流的原则,即数据通过父组件流向子组件

1.当数据为原始类型时,如果我们尝试去修改一个props的值,Vue会在控制台抛出一个警告,并且修改不成功

直接修改porps中的原始类型的值 复制代码
<script setup lang="ts">
import { onMounted } from "vue";

const props = defineProps({
  num: Number,
  name: String,
  isShow: Boolean,
});
onMounted(() => {
  props.num = 456;
  props.name = "李四";
  props.isShow = false;
  console.log(props.num);
  console.log(props.name);
  console.log(props.isShow);
});
</script>

2.如果数据为对象或者数组时,我们能够直接修改数组或者对象内部的值,因为对象和数组是按引用传递的,即传递的是一个地址值,因此能够直接修改内部的值。但是这种修改会导致数据流变得混乱而难以理解

直接修改props中的对象或者数组内部的值 复制代码
<script setup lang="ts">
import { onMounted } from "vue";
const props = defineProps({
  arrList: Array,
  objList: Object,
});
onMounted(() => {
  props.arrList[0].salary = 400;
  props.objList.name = "王五";
  console.log(props.arrList[0].salary);
  console.log(props.objList.name);
});
</script>

3.重新定义一个ref或者computed来间接修改props的值

3.1 重新定义一个ref

重新定义一个ref 复制代码
<script setup lang="ts">
import { ref, onMounted } from "vue";
const props = defineProps({
  num: Number,
  name: String,
  isShow: Boolean,
  arrList: Array,
  objList: Object,
});
const newNum = ref(props.num);
const newName = ref(props.name);
const newIsShow = ref(props.isShow);
const newArrList = ref(props.arrList);
const newObjList = ref(props.objList);
onMounted(() => {
  newNum.value = 456;
  newName.value = "李四";
  newIsShow.value = false;
  newArrList.value[0].salary = 400;
  newObjList.value.name = "王五";
  console.log(newNum.value);
  console.log(newName.value);
  console.log(newIsShow.value);
  console.log(newArrList.value[0].salary);
  console.log(newObjList.value.name);
});
</script>

3.2 重新定义一个computed

定义一个computed 复制代码
<script setup lang="ts">
import { computed, onMounted } from "vue";
const props = defineProps({
  num: Number,
  isShow: Boolean,
});
const newNum = computed(() => {
  return props.num + 1;
});
const newIsShow = computed(() => {
  return !props.isShow;
});
onMounted(() => {
  console.log(newNum.value);
  console.log(newIsShow.value);
});
</script>

emits (子组件 -> 父组件通信)

一、模板表达式中直接使用emit

模板表达式中直接使用emit 复制代码
<template>
  <div>
    <button @click="$emit('test', testValue)">按钮</button>
  </div>
</template>

<script setup lang="ts">
import { ref } from "vue";
const testValue = ref("张三");
</script>

二、显式的通过defineEmits声明

通过defineEmits显式声明 复制代码
<template>
  <div>
    <button @click="handleTestEmit">按钮</button>
  </div>
</template>

<script setup lang="ts">
import { ref } from "vue";
const testValue = ref("张三");

const emit = defineEmits(["test"]);

const handleTestEmit = () => {
  emit("test", testValue.value);
};
</script>

三、defineEmits结合TS进行类型声明

TS类型声明 复制代码
<template>
  <div>
    <button @click="handleTestEmit">按钮</button>
  </div>
</template>

<script setup lang="ts">
import { ref } from "vue";
const testValue = ref("张三");

interface Emit {
  (e: "test", value: string): void;
}
const emit = defineEmits<Emit>();

const handleTestEmit = () => {
  emit("test", testValue.value);
};
</script>

组件上的v-model

一、绑定单个v-model

绑定单个v-model 复制代码
<template>
  <div>
    <TestCom v-model="testValue" />
    <!-- 等价于:<TestCom :modelValue="testValue" @update:modelValue="testValue = $event" /> -->
  </div>
</template>

在父组件上绑定v-model,等同于向子组件内部传入了一个名为modelValue的props,并且监听一个update:modelValue事件并赋值,因此需要在子组件内部定义一个props去接收这个modelValue,并且通过emit给父组件发送一个@update:modelValue事件

子组件定义prop和emit 复制代码
<template>
  <div>
    <input :value="modelValue" @input="handleEmit" />
  </div>
</template>

<script setup lang="ts">
const prop = defineProps(["modelValue"]);
const emit = defineEmits(["update:modelValue"]);

const handleEmit = (e) => {
  emit("update:modelValue", e.target.value);
};
</script>

二、绑定多个v-model

绑定多个v-model 复制代码
<template>
  <div>
    <TestCom
      v-model:one="testValue1"
      v-model:two="testValue2"
      v-model:three="testValue3"
    />
  </div>
</template>

<script setup lang="ts">
import TestCom from "./components/index.vue";
import { ref } from "vue";
const testValue1 = ref();
const testValue2 = ref();
const testValue3 = ref();
</script>

当我们需要在一个组件上绑定多个v-model时,需要我们自定义每个v-model传入的props名字

js 复制代码
<template>
  <div>
    <input :value="one" @input="handleEmitOne" />
    <input :value="two" @input="handleEmitTwo" />
    <input :value="three" @input="handleEmitThree" />
  </div>
</template>

<script setup lang="ts">
const prop = defineProps(["one", "two", "three"]);
const emit = defineEmits(["update:one", "update:two", "update:three"]);

const handleEmitOne = (e) => {
  emit("update:one", e.target.value);
};
const handleEmitTwo = (e) => {
  emit("update:two", e.target.value);
};
const handleEmitThree = (e) => {
  emit("update:three", e.target.value);
};
</script>
相关推荐
web小白成长日记8 小时前
企业级 Vue3 + Element Plus 主题定制架构:从“能用”到“好用”的进阶之路
前端·架构
APIshop8 小时前
Python 爬虫获取 item_get_web —— 淘宝商品 SKU、详情图、券后价全流程解析
前端·爬虫·python
风送雨8 小时前
FastMCP 2.0 服务端开发教学文档(下)
服务器·前端·网络·人工智能·python·ai
XTTX1108 小时前
Vue3+Cesium教程(36)--动态设置降雨效果
前端·javascript·vue.js
LYFlied9 小时前
WebGPU与浏览器边缘智能:开启去中心化AI新纪元
前端·人工智能·大模型·去中心化·区块链
Setsuna_F_Seiei9 小时前
2025 年度总结:人生重要阶段的一年
前端·程序员·年终总结
model200510 小时前
alibaba linux3 系统盘网站迁移数据盘
java·服务器·前端
han_11 小时前
从一道前端面试题,谈 JS 对象存储特点和运算符执行顺序
前端·javascript·面试
aPurpleBerry11 小时前
React 01 目录结构、tsx 语法
前端·react.js
jayaccc11 小时前
微前端架构实战全解析
前端·架构