「这是我参与2022首次更文挑战的第26天,活动详情查看:2022首次更文挑战」。
父传子 - props 对象用法
前面 props
选项使用一个字符串数组时,只能说明传入的 attribute
的名称,并不能对其进行任何形式的限制,接下来我们来看一下使用对象的写法是如何让 props
变得更加完善的。
当使用对象语法时,我们可以对传入的内容做更多限制:
- 比如指定传入的
attribute
值的类型; - 比如指定传入的
attribute
值是否要求必传; - 比如设置某个
attribute
的值没有传入时的(该attribute
的)默认值;
所以在真实开发中,我们注册 props
时,一般不写字符串数组,而是会写对象:
<template>
<div>
<h2>{{ title }}</h2>
<p>{{ content }}</p>
</div>
</template>
<script>
export default {
// props: ['title', 'content']
props: {
// 1. 指定类型(null 和 undefined 值会通过任何类型验证)
title: String,
// 2. 指定多个可能的类型
propA: [String, Number],
// 3. 指定类型,同时指定是否要求必传
content: {
type: String,
required: true
},
// 4. 指定类型,同时指定默认值
propB: {
type: String,
default: '我是内容'
}
}
}
</script>
<style scoped>
</style>
required
和default
一般设置其中一个即可,要么是必传的,如果没有传就会报警告,要么不传值,那么就会使用默认值。
props
对应的值写成对象时,还有几点细节:
-
type
的类型可以是下面原生构造函数中的一个:String
Number
Boolean
Array
Object
Date
Function
Symbol
还可以是一个自定义的构造函数。
-
还有一些其它写法:
<script> export default { props: { // 指定为对象类型,同时指定默认值 propC: { type: Object, // 对象或数组的默认值必须从一个工厂函数返回 default() { return { message: '你好~' } } }, // 自定义验证函数 propD: { validator(value) { // 传入的值必须是下列字符串中的其中一个 return ['success', 'warning', 'danger'].includes(value) } }, // 指定为函数类型,同时指定默认函数 propE: { type: Function, // 与对象或数组的默认值不同,这不是一个工厂函数,而是一个用作默认值的函数 default() { return '我是默认函数' } } } } </script>
注意:
prop
指定为对象类型同时指定默认值时,默认值不能直接是一个对象:<script> export default { props: { info: { type: Object, // 存在问题的写法:default 直接用一个对象赋值 default: { name: 'zhj' } } } } </script>
像上面这样写虽然不会报错,但是会存在问题:假如上面的这个
info
属性是在ShowMessage.vue
组件中注册的,而我们很有可能会多次使用到这个组件,比如我们使用了3
次ShowMessage.vue
组件,那就意味着到时候会根据该组件创建出3
个该组件的实例对象,而这3
个组件实例中其实都会有一个props
属性,而且props
属性中还都有一个info
属性。那么,如果这3
次使用ShowMessage.vue
组件时都没有给组件的info
属性(attribute
)传值,此时它们就都会使用默认值对应的这个对象,这时相当于引用赋值,即这3
个组件实例的props.info
都指向了同一个对象,那么这时假如修改了其中一个组件实例中的props.info
对象中的name
属性的值,其它两个组件实例中的props.info
对象中的name
属性的值也会被修改掉。简单来说,
default
直接用一个对象赋值的问题是:我们明明修改的是ShowMessage.vue
组件的实例A
中的某个prop
的值,结果却连同ShowMessage.vue
组件的其它实例中的相应prop
的值(前提是没有传值,使用了默认值对象)也都被修改掉了。因此,
type
为引用类型(这里指Object
或Array
)时,如果要指定默认值,默认值应该从一个工厂函数返回(默认值要写成一个函数,然后在函数中返回真正的默认值):<script> export default { props: { info: { type: Object, // 对象或数组的默认值必须从一个工厂函数返回 default() { return { name: 'zhj' } } } } } </script>
像上面这样将
default
属性设置成一个函数,再在函数中返回默认值,这样就没问题了。因为之后在多次使用该组件时,如果组件上没有给info
属性传值,就会执行组件内info
prop
中的这个default()
函数,而每次执行该函数,都会返回一个新的对象({ name: 'zhj' }
),也就意味着多次使用该组件时,各个组件实例中使用的都是自己的对象({ name: 'zhj' }
),所以修改时就只会修改自己的对象。 -
prop
的大小写(camelCase
vskebab-case
)-
因为
HTML
中的attribute
名称是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符;-
举个例子:
<div class="container"></div> <div CLASS="container"></div> <div ClAsS="container"></div>
在
HTML
中,上面的三个<div>
元素中大小写各不相同的class
属性经过浏览器解析后都会变成小写形式:
-
-
这意味着当你使用
DOM
中的模板时,驼峰命名法
(camelCase
)的prop
名称需要使用其等价的短横线分隔命名法
(kebab-case
)命名;-
DOM
中的模板指的是直接在DOM
中编写的模板,比如:<template id="my-app"> <show-message message-info="abc"></show-message> </template>
而不包括下面三种情况:
- 字符串模板(比如直接把模板内容以字符串的形式赋值给
template
选项); - 模板编写在单文件组件(即
.vue
文件)中; - 模板编写在
<script type="x-template"></script>
中;
- 字符串模板(比如直接把模板内容以字符串的形式赋值给
-
举个例子:
假如你在
ShowMessage
组件中注册了一个名为messageInfo
的属性:<script src="../js/vue.js"></script> <script> const ShowMessage = { props: { messageInfo: { type: String } }, template: '#show-message' }; const App = { template: '#my-app', // 注册局部组件 components: { ShowMessage }, data() { return { message: 'Hello World' } } }; const app = Vue.createApp(App) app.mount('#app'); </script>
那么在将
Vue
模板直接编写在DOM
中时,你需要这样使用:<template id="my-app"> <!-- messageInfo 需要写成小写字母并用短横线分隔的形式 --> <show-message message-info="abc"></show-message> </template>
这样被
Vue
解析后,ShowMessage
组件内部接收到的就会是messageInfo
,之后才能正常使用。而如果使用ShowMessage
组件时,给messageInfo
属性传值时还是写成messageInfo
,那么由于浏览器原生的HTML
解析行为,HTML
attribute
名不区分大小写,因此浏览器会将所有大写字符解释为小写,所以ShowMessage
组件内部接收到的就会是messageinfo
,而不是messageInfo
,这就会导致messageInfo
prop
没有成功接收到传入的值。当然,开发中我们一般不会直接在
DOM
中编写模板,而是会在.vue
文件中编写模板。这时假如你在ShowMessage.vue
组件中注册了一个名为messageInfo
的属性:<script> export default { props: { messageInfo: { type: String } } } </script>
那么在使用这个组件时,在给
messageInfo
属性传值时,messageInfo
这个属性名既可以写成messageInfo
(驼峰命名法),也可以写成message-info
(短横线分隔命名法),因为这里.vue
文件会交给vue-loader
解析,而不是交给浏览器解析(浏览器解析的话就会把大写字母转换成小写字母)。但在开发中,如果注册的属性名是驼峰格式的,给其传值时建议将其改写成短横线分隔的格式(如message-info
),因为这种写法也是直接在DOM
中编写模板时的正确写法。<template> <div> <show-message title="哈哈哈" content="我是哈哈哈" :message-info="'hhh'"></show-message> <show-message title="哈哈哈" content="我是哈哈哈" :messageInfo="'hhh'"></show-message> </div> </template>
-
-