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>
相关推荐
三年三月2 小时前
Tailwind CSS 入门介绍
前端
余生H2 小时前
前端技术新闻(WTN-1):React.js & Next.js 爆出 CVSS 10.0 级严重漏洞,历史风险回顾与代码级深度分析
前端·javascript·react.js
1024肥宅2 小时前
JavaScript 原生方法实现:数学与数字处理全解析
前端·javascript·ecmascript 6
烟袅2 小时前
深入理解 JavaScript 内存机制与闭包原理
前端·javascript
烟袅2 小时前
JavaScript 内存三空间协同机制:代码空间、栈空间与堆空间如何联合运行
前端·javascript
lqj_本人2 小时前
DevUI高频组件(Form 组件)深度用法与避坑指南
前端·javascript
live丶2 小时前
从零实现一个低代码 H5 页面编辑器(Vue3 + 拖拽)
前端·vue.js
黑臂麒麟2 小时前
华为云 DevUI初体验:如何快速入门项目搭建
前端·ui·华为云·devui
翔云 OCR API3 小时前
企业工商信息查验API-快速核验企业信息-营业执照文字识别接口
前端·数据库·人工智能·python·mysql