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>