Vue文档阅读笔记.Props

81 阅读3分钟

Props声明

Props声明主要有2种方式

  • 基于数组的声明
  • 基于对象的声明

基于数组的声明

<script setup>
const props = defineProps(['arg1, arg2']);

</script>

基于对象的声明

基于对象的声明提供了更灵活的配置,可配置项包含:

  • type: 数据类型
  • required: 参数是否必须
  • default: 默认值(对于对象类型,必须使用函数返回对象的默认值)
  • validator: 自定义校验器,返回true/false
<script setup>
const props = defineProps({
    title: String, // 指定类型
    likes: { // 更细致化的描述
        type: Number, 
        required: true // 默认为false
    },
    isPublished: {
        type: Boolean,
        default: false // 默认值为undefined,对于Boolean会转化为false
    },
    author: {
        type: Object,
        default: ()=>{ // 必须使用函数返回
            name: '匿名',
            company: '未知'
        }
    },
    articleType: {
        validator: (v)=>{
            return ["技术", "人文"].includes(v);
        }
    }
});

</script>

利用Typescript进行类型指定

<script setup lang="ts">
interface Props{
    title: string;
    likes: number;
    isPublished?: boolean;  // ?表示可选
    author: {name: string; company: string};
    articleType: "技术" | "人文";
}  // 或 type Props = {...}

const props = defineProps<Props>();
</script>

Typescript中指定默认值

方法一:withDefaults

<script setup lang="ts">
const props = withDefaults(defineProps<Props>(), {
    likes: 0,
    isPublished: false,
});
</script>

方法二:Props解构默认值(仅支持v3.5以上)

<script setup lang="ts">
const { likes = 0, isPublished = false } = defineProps<Props>();
</script>

响应式Props解构

const { foo } = defineProps(['foo'])

watchEffect( () => {
    console.log(foo);
})

上面这段代码在3.5版本之前只会执行一次,foo被视为一个常量。3.5版本之后,编译器会自动为Props解构的内容前面添加props,等效于下面这段代码:

const props = defineProps(['foo'])

watchEffect( () => {
    console.log(props.foo);
})

需要注意foo并没有被修改为响应式数据源,于是watch(foo, /* ... */)依旧是无效的,应该用getter进行包装:watch(() => foo, /* ... */)

传递Props的细节

Prop名字格式

应该在script代码中使用camelCase命名,如greetingMessage。而在传递props时,以下两种方法的代码均能正常工作:

<MyComponent greetingMessage="hello" />
<MyComponent greeting-message="hello" />

但是更推荐第二种,因为和html的attribute对齐。

静态 vs. 动态 Props

:v-bind的缩写,用于支持props动态绑定

<BlogPost :title="post.title" />
<BlogPost :title="post.title + ' by ' + post.author.name" />

也就是说,会将""中的内容解析为script代码。

传递不同的值类型

如果不使用v-bind,传入的值永远是字符串类型,比如下面的代码会出错:

<BlogPost title="42" />

所以即使是传入数字常量,也需要用v-bind,同样的情况对其他类型也适用:

  • Boolean
  • Array
  • Object

使用一个对象绑定多个prop

<BlogPost :id="post.id" :title="post.title" />

利用没有参数的v-bind,可以达到与上面代码相同的效果:

<BlogPost v-bind="post" />

单向数据流

props的更新仅会从父组件传递到子组件,而不会反向传递,因此不应该在子组件中更改prop。

prop用于初始值

这种情况下应该定义一个新的变量

const props = defineProps(['initialCounter'])

const counter = ref(props.initialCounter)

需要基于prop的值进行变换

这种情况应该使用computed

const props = defineProps(['size])

const normalizedSize = computed(() => props.size.trim().toLowerCase())

数组/对象类型

由于数据/对象类型的prop传入的是引用,所以修改可以在原数据上生效,但是不应该这样做。

Prop校验

这部分列举了所有可以对prop做的校验方式

defineProps({
    propA: Number,
    propB: [String, Number],
    propC: {
        type: String,
        required: true
    },
    propD: {
        type: [String, null],
        required: true
    },
    propE: {
        type: Number,
        default: 100
    },
    propF: {
        type: Object,
        default(rawProps){
            return { message: 'hello' }
        }
    },
    propG: {
        validator(value, props){
            return ['success', 'warning', 'danger'].includes(value)
        }
    },
    propH: {
        type: Function,
        // default函数本身会作为默认值,而非字符串
        default(){
            return 'Default Function'
        }
    }
})

补充

default的实现原理

当指定传入参数的值为undefined时,即使是显性指定,也会被修改为default所指定的值。

Boolean类型

defineProps({
    disabled: Boolean
})

对于Boolean类型的prop可以这样写:

<!-- 等同于:disabled="true" -->
<MyComponent disabled />

<!-- 等同于:disabled="false" -->
<MyComponent />