组件传值
1.defineProps()
Vue3中父组件向子组件传值扔仍和Vue2一样,如下所示
<Child :num="123" name="坤坤"></Child>但子组件接受的方式不一样
在Vue3中需要使用defineProps(),其传入的参数与选项式Api的props一致
defineProps 是只能在 <script setup> 中使用的编译器宏。他们不需要导入,且会随着 <script setup> 的处理过程一同被编译掉。
1.1 props校验
1.1.1 数组
defineProps()可接收一个字符串类型的数组,数组中的字符串代表父组件穿来的数据
const props=defineProps(['num','name'])1.1.2 对象-简单
defineProps()可接收对象,对象中每一个键对应父组件传来的数据,其值可以是一个类型
const props=defineProps({
num:Number,
name:String
})如果穿来的值可能有多个类型,可以用一个数组存放这些类型
const props=defineProps({
id: [Number,String]
})1.1.2 对象-复杂
defineProps()接受一个对象,其中每一个键也可以是一个对象,可以更严格进行限制,对象中包含以下键
- type:接受数据的类型,可以是一个类型,也可以是一个类型数组
- default:默认值,可以是一个字面量,也可以是一个函数返回一个字面量
- require:
Boolean,限制是否必传 - validator:校验函数,返回一个布尔值
详细对象形式-可限制类型、必要性、默认值
const props=defineProps({
num:{
type:Number,
default:0
},
name:{
type:String,
require:true
}
})详细对象形式还可以校验
const props = defineProps({
num:{
validator(value, props) {
return value > 60
}
},
})validator()参数
- value:父组件传过来的值
- props:当前
defineProps()返回的对象
1.2 props解构
上述写法我们使用props中的值需要使用props.键使用
通过解构可以方便使用
const {num, name} = defineProps(['num','name'])1.3 传递props细节
使用一个对象可以绑定多个 prop
如果你想要将一个对象的所有属性都当作 props 传入,你可以使用没有参数的 v-bind,即只使用 v-bind 而非 :prop-name。例如,这里有一个 post 对象:
const post = {
id: 1,
title: 'My Journey with Vue'
}以及下面的模板:
<BlogPost v-bind="post" />而这实际上等价于:
<BlogPost :id="post.id" :title="post.title" />1.4 Boolean类型转换
当defineProps()中接受一个布尔类型的数据时
defineProps({
disabled: Boolean
})在父组件中只需这样传
<!-- 等同于传入 :disabled="true" -->
<MyComponent disabled />
<!-- 等同于传入 :disabled="false" -->
<MyComponent />当一个 prop 被声明为允许多种类型时,Boolean 的转换规则也将被应用。然而,当同时允许 String 和 Boolean 时,有一种边缘情况——只有当 Boolean 出现在 String 之前时,Boolean 转换规则才适用:
// disabled 将被转换为 true
defineProps({
disabled: [Boolean, Number]
})
// disabled 将被转换为 true
defineProps({
disabled: [Boolean, String]
})
// disabled 将被转换为 true
defineProps({
disabled: [Number, Boolean]
})
// disabled 将被解析为空字符串 (disabled="")
defineProps({
disabled: [String, Boolean]
})2.defineEmits()
defineEmits()用于组件的自定义事件,defineEmits()也不需要导入就可以直接使用
defineEmits()接受一个数组,数组中包含多个自定义事件名的字符串
:::code-goup
<template>
<Child @emitEvent="add"></Child>
</template>
<script setup>
const add=(num,num1)=>{
console.log(num+num1)
}
</script><script setup>
const emit = defineEmits(['emitEvent'])
emit('emitEvent',11,22)
</script>:::
在模版中可以直接使用$emit调用父组件中的事件,而且不需要在js中使用defineEmits()
<button @click="$emit('add')">add</button>3.defineExpose()
当父组件通过ref获取子组件实例时,默认子组件中的数据时获取不到的,需要使用defineExpose()将数据暴露出去
defineExpose()接受一个对象,对象中包含需要暴露出去的数据
<template>
{{ num }}
</template>
<script setup>
import { ref } from 'vue'
const num = ref(666)
const add = () => {
console.log(num.value * 100)
}
defineExpose({
num,
add
})
</script><template>
<div>
<Child ref="child"></Child>
<button @click="show">click</button>
</div>
</template>
<script setup>
import Child from './views/Child.vue'
import { ref } from 'vue'
const child = ref(null)
const show = () => {
child.value.num = 777
child.value.add()
}
</script>4.defineModel()
defineModel()在3.4+引入,在v-model绑定组件时使用
defineModel()可获取v-model传过来的值,并不需要导入就可以使用
defineModel() 返回的值是一个 ref。它可以像其他 ref 一样被访问以及修改,不过它能起到在父组件和当前变量之间的双向绑定的作用:
<template>
<div>
<Child v-model="num"></Child>
</div>
</template>
<script setup>
import Child from './views/Child.vue'
import { ref } from 'vue'
const num = ref(123)
</script><template>
<div>
{{ num }}
<button @click="add">add</button>
</div>
</template>
<script setup>
import { defineModel } from 'vue'
const num = defineModel()
const add = () => {
num.value++
}
</script>4.1 底层机制
defineModel 是一个便利宏。编译器将其展开为以下内容:
- 一个名为
modelValue的 prop,本地 ref 的值与其同步; - 一个名为
update:modelValue的事件,当本地 ref 的值发生变更时触发。
在 3.4 版本之前,你一般会按照如下的方式来实现上述相同的子组件:
<!-- Child.vue -->
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>
<template>
<input
:value="modelValue"
@input="emit('update:modelValue', $event.target.value)"
/>
</template>然后,父组件中的 v-model="foo" 将被编译为:
<!-- Parent.vue -->
<Child
:modelValue="foo"
@update:modelValue="$event => (foo = $event)"
/>4.2 v-model参数
组件上的 v-model 也可以接受一个参数:
<MyComponent v-model:title="bookTitle" />在子组件中,我们可以通过将字符串作为第一个参数传递给 defineModel() 来支持相应的参数:
<!-- MyComponent.vue -->
<script setup>
const title = defineModel('title')
</script>
<template>
<input type="text" v-model="title" />
</template>4.3 多个v-model
利用刚才在 v-model 的参数小节中学到的指定参数与事件名的技巧,我们可以在单个组件实例上创建多个 v-model 双向绑定。
组件上的每一个 v-model 都会同步不同的 prop,而无需额外的选项:
<UserName
v-model:first-name="first"
v-model:last-name="last"
/><script setup>
const firstName = defineModel('firstName')
const lastName = defineModel('lastName')
</script>
<template>
<input type="text" v-model="firstName" />
<input type="text" v-model="lastName" />
</template>而在3.4以下的版本需要使用以下写法
<script setup>
defineProps({
firstName: String,
lastName: String
})
defineEmits(['update:firstName', 'update:lastName'])
</script>
<template>
<input
type="text"
:value="firstName"
@input="$emit('update:firstName', $event.target.value)"
/>
<input
type="text"
:value="lastName"
@input="$emit('update:lastName', $event.target.value)"
/>
</template>4.4 修饰符
通过如下方式获取v-model修饰符
<Child v-model.trim="num"></Child><script setup>
const [num, opt] = defineModel()
console.log(opt) // {trim: true}
</script>当然也可以自定义修饰符
<Child v-model.abcd="num"></Child><script setup>
const [num, opt] = defineModel()
console.log(opt) // {abcd: true}
</script>4.5 转换器
当存在修饰符时,我们可能需要在读取或将其同步回父组件时对其值进行转换。我们可以通过使用 get 和 set 转换器选项来实现这一点:
const [modelValue, modelModifiers] = defineModel({
// get() 省略了,因为这里不需要它
set(value) {
// 如果使用了 .trim 修饰符,则返回裁剪过后的值
if (modelModifiers.trim) {
return value.trim()
}
// 否则,原样返回
return value
}
})4.6 带参数的修饰符
这里是另一个例子,展示了如何在使用多个不同参数的 v-model 时使用修饰符:
<UserName
v-model:first-name.capitalize="first"
v-model:last-name.uppercase="last"
/><script setup>
const [firstName, firstNameModifiers] = defineModel('firstName')
const [lastName, lastNameModifiers] = defineModel('lastName')
console.log(firstNameModifiers) // { capitalize: true }
console.log(lastNameModifiers) // { uppercase: true }
</script>3.4 之前的用法
<script setup>
const props = defineProps({
firstName: String,
lastName: String,
firstNameModifiers: { default: () => ({}) },
lastNameModifiers: { default: () => ({}) }
})
defineEmits(['update:firstName', 'update:lastName'])
console.log(props.firstNameModifiers) // { capitalize: true }
console.log(props.lastNameModifiers) // { uppercase: true }
</script>useSlots()
useSlots()可以在script setup中获取组件的插槽数据
但是插槽数据可以在模板中直接访问$slots来获取
<script setup>
import { useSlots } from 'vue'
const slots = useSlots()
</script>useAttrs()
useAttrs()可以在script setup中获取父组件传来但没被props接受的数据
但是插槽数据可以在模板中直接访问$attrs来获取
<script setup>
import { useAttrs } from 'vue'
const att = useAttrs()
</script>