学习此内容前假定你已了解组件基础及注册,对组件的使用,组件全局注册,组件局部注册已有一定了解;
prop的大小写
HTML中的attribute(属性)名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用DOM中的模板时,camelCase(驼峰命名法)的prop名需要使用其等价的kebab-case(短横线分割法)命名;
vue2
//vue2 子组件 BlogPost.vue
<template>
<h3>{{postTitle}}</h3>
</template>
<script>
export default {
props:['postTitle'],
data(){
return { }
}
}
</script>
BlogPost为自定义子组件名;
postTitle为自定义prop属性名;
< blog-post post-title=“hello!”></ blog-post>;post-title属性设置静态数据hello!;如需设置动态值,可使用v-bind指令
//vue2 父组件index.vue
/*父组件*/
<template>
<div class="index-main">
<blog-post post-title="hello!"></blog-post>
</div>
</template>
<script>
import blog-post from '../components/BlogPost';//普通组件
export default {
components:{
'blog-post': blog-post
},
data(){
return {}
}
}
</script>
vue3
//vue3子组件使用setup BlogPost.vue
<script setup>
defineProps(['postTitle'])
</script>
<template>
<h3>{{postTitle}}</h3>
</template>
//vue3 不使用setup
<script>
export default {
props: ['postTitle'],
setup(props) {
console.log(props.postTitle)
}
}
</script>
<template>
<h3>{{postTitle}}</h3>
</template>
传递给 defineProps() 的参数和提供给 props 选项的值是相同的,两种声明方式背后其实使用的都是 prop 选项
//父组件 index.vue
<script setup>
import {ref, reactive } from 'vue';
import BlogPost '@/components/BlogPost.vue'
</script>
<template>
<div class="index-main">
<BlogPost post-title="hello!"></BlogPost>
</div>
</template>
prop类型及验证
当只定义prop名称,不定义类型时,我们常以数组的形式列出prop;
- 一个组件可以有任意数量的prop
- prop被注册后,就可以把数据作为一个自定义属性传递进来;例如父组件的title、num、show等
- 父组件可以传递静态数据,也可以传递动态prop;
- prop类型可以是原生构造函数中的一个(String、Number、Boolean、Array、Object、Date、Function、Symbol),还可以是一个自定义的构造函数;
- 所有prop默认都是可选的,除非声明了required:true
- 除 Boolean 外的未传递的可选 prop 将会有一个默认值 undefined
- 当 prop 的校验失败后,Vue 会抛出一个控制台警告 (在开发模式下)
数组形式的多个prop
//vue2子组件 MyComponent.vue
props:['title','likes','isShow'...]
//vue3子组件 MyComponent.vue
defineProps(['btnColor','title','msg']);
//在父组件中使用
<my-component title="传递静态标题" :is-show="true"></my-component>
prop对象的形式指定单个类型
//vue2
props:{
title: String,//字符串类型
likes: Number,//Number类型
isShow: Boolean,//布尔类型
commentIds: Array,
author: Object,
callback: Function,
}
//vue3
<script setup>
defineProps({
title: String,//字符串类型
likes: Number,//Number类型
isShow: Boolean,//布尔类型
commentIds: Array,
author: Object,
callback: Function,
})
</script>
设置多个类型
//vue2
props:{
title: [String, Number]
}
//vue3
defineProps({
title: [String, Number]
})
设置必填
//vue2
props:{
title: {
type: [String, Number],
required: true
}
}
//vue3
defineProps({
title: {
type: [String, Number],
required: true
}
})
设置默认值
//vue2
props:{
title: {
type: [String, Number],
default: '默认标题'
}
//对象或数组的默认值必须从一个工厂函数获取
obj:{
type: Object,
detault: function(){
return {
message:'hello'
}
}
}
}
//vue3
defineProps({
title: {
type: [String, Number],
default: '默认标题'
},
//对象或数组的默认值必须从一个工厂函数获取
obj:{
type: Object,
detault(){
return {
message:'hello'
}
}
}
})
自定义检验函数
//vue2
props:{
state: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].includes(value)
}
}
}
//vue3
<script setup>
defineProps({
// 在 3.4+ 中完整的 props 作为第二个参数传入
state: {
validator(value, props) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].includes(value)
}
}
})
</script>
type设置自定义构造函数
//vue2
function Person (firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
//来验证 author prop 的值是否是通过 new Person 创建的
props: {
author: Person
}
布尔类型转换
当一个 prop 被声明为允许多种类型时,Boolean 的转换规则也将被应用。然而,当同时允许 String 和 Boolean 时,有一种边缘情况——只有当 Boolean 出现在 String 之前时,Boolean 转换规则才适用
//vue2 MyComponent.vue子组件
props:{
isShow: Boolean,//布尔类型
}
//vue2 父组件
<!-- 等同于传入 :disabled="true" -->
<my-component disabled></my-component>
<!-- 等同于传入 :disabled="false" -->
<my-component></my-component>
//vue3 MyComponent.vue子组件
defineProps({
disabled: Boolean
})
//vue3 父组件中使用子组件
<!-- 等同于传入 :disabled="true" -->
<MyComponent disabled />
<!-- 等同于传入 :disabled="false" -->
<MyComponent />
// disabled 将被转换为 true
defineProps({
disabled: [Boolean, Number]
})
// disabled 将被转换为 true
defineProps({
disabled: [Boolean, String]
})
// disabled 将被转换为 true
defineProps({
disabled: [Number, Boolean]
})
// disabled 将被解析为空字符串 (disabled="")
defineProps({
disabled: [String, Boolean]
})
prop应用实例及特点
prop使得父组件与子组件之间形成一个单项下行绑定;父组件绑定的属性值更新时会向下流动到子组件,但反过来不行;
外的,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
子组件中的prop仅用于展示,而不能更改;
注意在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变变更这个对象或数组本身将会影响到父组件的状态。
注意那些 prop 会在一个组件实例创建之前进行验证,所以实例的 property (如 data、computed 等) 在 default 或 validator 函数中是不可用的。
1.1、vue2
/*子组件*/
<template>
<div class="test-child" title="标题">
<p>字符串:{{tit}}</p>
<!-- 默认非prop属性在根元素继承,即div,如果其他标签想要继承,需要使用v-bind="$attrs" -->
<p>数字:{{num}}</p>
<p>布尔:{{show}}
<span v-if="show">显示</span>
<span v-else>隐藏</span>
</p>
<p>数组:{{arr}}</p>
<p>对象:{{obj}}</p>
</div>
</template>
<script>
export default {
inheritAttrs: true, //禁用 Attribute 继承,如果为true,根元素的div的title="标题"会被父组件的值替换
props:{
title: {
type: String,
default: '字符串'
},
num: {
type: Number,
default: 1024
},
show:Boolean,
arr: Array,
obj: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
}
},
data(){
return {
}
}
}
</script>
/*父组件*/
<template>
<div class="main">
<P>组件的传值 父组件传值</P>
<test-child title="标题">传递静态值</test-child>
<test-child :title="tit">传递data中动态值</test-child>
<test-child :num="100">传递静态值</test-child>
<test-child :num="num">传递静态值</test-child>
<test-child :show="false"></test-child>
<test-child :show="isshow"></test-child>
<!-- 传入数组和对象 -->
<test-child title="标题" :num='1000' show :arr="['a','b']" :obj="{id:1,title:'标题'}">子组件</test-child>
<!-- 传入一个对象的所有属性值,post中的属性对应子组件的prop -->
<test-child v-bind="post"></test-child>
<!--
等价于
<test-child v-bind:tit="post.tit" v-bind:num="post.num"></test-child>
-->
</div>
</template>
<script>
import testChild from '../components/test-child';//普通组件
export default {
components:{
'test-child': testChild
},
data(){
return {
tit: '动态标题,可修改',
num:520,
isshow:true,
post:{
num: 1,
tit: '标题'
}
}
}
}
</script>
1.2、vue3
/*子组件*/
<template>
<div class="test-child" title="标题">
<p>字符串:{{title}}</p>
<p>数字:{{num}}</p>
<p>布尔:{{show}}
<span v-if="show">显示</span>
<span v-else>隐藏</span>
</p>
<p>数组:{{arr}}</p>
<p>对象:{{obj}}</p>
</div>
</template>
<script setup>
const props = defineProps({
title:{
type: String,
// required: true,
default: "字符串"
},
num:{
type: Number,
default: 1024
},
show: Boolean,
arr: Array,
obj:{
type: Object,
default: ()=>{
return { message: 'hello' }
}
}
});
</script>
/*父组件*/
<script setup>
import {ref} from 'vue';
const tit = ref('动态字符串');
const num = ref(25421);
const isshow = ref(true);
const post = ref({
title:'组件标题',
num:987654321,
show: true,
arr:['12','34'],
obj:{name:'张三',age:18}
});
onMounted(()=>{
console.log('mounted生命周期');
})
</script>
<template>
<div class="index-main">
<!-- <TestChild title="标题">传递静态值</TestChild> -->
<!-- <TestChild :title="tit">传递data中动态值</TestChild> -->
<!-- <TestChild :num="100">传递静态值</TestChild> -->
<!-- <TestChild :num="num">传递动态值</TestChild> -->
<!-- <TestChild :show="false"></TestChild> -->
<!-- <TestChild :show="isshow"></TestChild> -->
<!-- 传入数组和对象 -->
<!-- <TestChild title="标题" :num='1000' show :arr="['a','b']" :obj="{id:1,title:'标题'}">子组件</TestChild> -->
<!-- 传入一个对象的所有属性值,post中的属性对应子组件的prop -->
<TestChild v-bind="post"></TestChild>
<!--
等价于
<TestChild v-bind:tit="post.tit" v-bind:num="post.num"></TestChild>
-->
</div>
</template>
prop单向数据流举例
当子组件需要使用prop值并进行修改时,可在data中定义一个属性,将prop的值传递给本地属性修改本地属性值;
//vue2 错误写法
props: ['initialCounter'],
data: function () {
return { }
},
methods:{
btnHandle(){
this.title = '修改title'
},
},
//vue2 正确写法
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
},
methods:{
btnHandle(){
//正确写法
this.msg = '修改msg';
},
},
//vue2 当需要使用原始传入值进行转换,建议使用计算属性
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
//vue3
<script setup>
const props = defineProps(['initialCounter'])
const counter = ref(props.initialCounter)
</script>
//vue3
const props = defineProps(['size'])
// 该 prop 变更时计算属性也会自动更新
const normalizedSize = computed(() => props.size.trim().toLowerCase())