为什么需要 props 类型呢?就比如我们子组件需要用到父组件的数据,我们到底该使用何种方式传递进去呢?我们都知道在原生 DOM 中有一种 data- 属性,可以将数据绑定,所以类似这种方式,props 就应运而生了。
我们还是接着上节课的例子,在 src/views/TestCom.vue
,接收父组件传递进来的属性 title
:
<template>
<div class="test-com-wrap">{{title}}</div>
</template>
<script>
export default {
name: "TestCom",
props: {
title: {
type: String,
default: "",
},
},
};
</script>
然后我们在 src/views/TemplateM.vue
来使用 TestCom
组件,向 TestCom
组件传递 title
属性:
<template>t
<div class="template-m-wrap">
<test-com title="这个是组件"></test-com>
</div>
</template>
<script>
import TestCom from './TestCom'
export default {
name: "TemplateM",
components: {
TestCom
},
data() {
return {
searchText: "",
};
},
};
</script>
我们访问 http://localhost:8080/template_m
浏览实际效果如下:
从上面我们知道我们直接像下面这段代码,就是我们所说的静态属性:
<test-com title="这是组件"></test-com>
如果我们把这个 title
属性绑定一个变量,那就是动态绑定属性:
<template>t
<div class="template-m-wrap">
<test-com :title="searchText"></test-com>
</div>
</template>
<script>
import TestCom from './TestCom'
export default {
name: "TemplateM",
components: {
TestCom
},
data() {
return {
searchText: "这个是组件",
};
},
};
</script>
效果还是跟上面一样:
假如子组件需要接收父组件的一个数值,那么我们可以给 props
传递一个数字类型,接下来我们在 src/views/TestCom.vue
来添加一个数字类型的 props
:
<template>
<div class="test-com-wrap">{{title}}-{{num}}</div>
</template>
<script>
export default {
name: "TestCom",
props: {
title: {
type: String,
default: "",
},
num: {
type: Number,
default: 0
}
},
};
</script>
在 src/views/TemplateM.vue
来传递一个数字:
<template>
<div class="template-m-wrap">
<test-com title="这个是组件" :num="42"></test-com>
</div>
</template>
<script>
import TestCom from './TestCom'
export default {
name: "TemplateM",
components: {
TestCom
},
data() {
return {
searchText: "",
};
},
};
</script>
我们来看看效果如何:
我们同样在 src/views/TestCom.vue
设置一个布尔类型的 props:
<template>
<div class="test-com-wrap">{{title}}-{{num}}-{{bool}}</div>
</template>
<script>
export default {
name: "TestCom",
props: {
title: {
type: String,
default: "",
},
num: {
type: Number,
default: 0
},
bool: {
type: Boolean,
default: false
}
},
};
</script>
在 src/views/TemplateM.vue
来使用:
<template>
<div class="template-m-wrap">
<test-com title="这个是组件" :num="42" :bool="true"></test-com>
</div>
</template>
<script>
import TestCom from './TestCom'
export default {
name: "TemplateM",
components: {
TestCom
},
data() {
return {
searchText: "",
};
},
};
</script>
最后我们来查看下效果:
我们同样在 src/views/TestCom.vue
接收一个数组类型的 props:
<template>
<div class="test-com-wrap">
{{ title }}-{{ num }}-{{ bool }}
<ul v-if="arr.length">
<li v-for="(item, index) in arr" :key="index">{{ item }}</li>
</ul>
</div>
</template>
<script>
export default {
name: "TestCom",
props: {
title: {
type: String,
default: "",
},
num: {
type: Number,
default: 0,
},
bool: {
type: Boolean,
default: false,
},
arr: {
type: Array,
default: () => ([]),
},
},
};
</script>
在 src/views/TemplateM.vue
来使用:
<template>
<div class="template-m-wrap">
<test-com title="这个是组件" :num="42" :bool="true" :arr="arr"></test-com>
</div>
</template>
<script>
import TestCom from './TestCom'
export default {
name: "TemplateM",
components: {
TestCom
},
data() {
return {
searchText: "",
arr: ['code', 'Ken', '理财投资']
};
},
};
</script>
来看看效果如下:
我们同样在 src/views/TestCom.vue
接收一个 Object 类型的 props:
<template>
<div class="test-com-wrap">
{{ title }}-{{ num }}-{{ bool }}
<ul v-if="arr.length">
<li v-for="(item, index) in arr" :key="index">{{ item }}</li>
</ul>
{{obj.name}}
</div>
</template>
<script>
export default {
name: "TestCom",
props: {
title: {
type: String,
default: "",
},
num: {
type: Number,
default: 0,
},
bool: {
type: Boolean,
default: false,
},
arr: {
type: Array,
default: () => ([]),
},
obj: {
type: Object,
default: () => ({})
}
},
};
</script>
在 src/views/TemplateM.vue
使用:
<template>
<div class="template-m-wrap">
<test-com title="这个是组件" :num="42" :bool="true" :arr="arr" :obj="obj"></test-com>
</div>
</template>
<script>
import TestCom from './TestCom'
export default {
name: "TemplateM",
components: {
TestCom
},
data() {
return {
searchText: "",
arr: ['code', 'Ken', '理财投资'],
obj: {
name: "Ken"
}
};
},
};
</script>
来看看效果如下:
如果你想要将一个对象的所有 property 都作为 prop 传入,你可以使用不带参数的 v-bind
(取代 v-bind
:prop-name
)。例如,对于一个给定的对象 post
:
post: {
id: 1,
title: 'My Journey with Vue'
}
下面的模板:
<test-com v-bind="post"></test-com>
等价于:
<test-com v-bind:id="post.id" v-bind:title="post.title"></test-com>
所有的 prop 都使得其父子 prop 之间形成了一个「单向下行绑定」:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。
另外,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你「不」应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
这里有两种常见的试图变更一个 prop 的情形:
props: ['initialCounter'],
data() {
return {
counter: this.initialCounter
}
}
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
我们可以为组件的 prop 指定验证要求,例如你知道的这些类型。如果有一个需求没有被满足,则 Vue 会在浏览器控制台中警告你。这在开发一个会被别人用到的组件时尤其有帮助。
为了定制 prop 的验证方式,你可以为 props
中的值提供一个带有验证需求的对象,而不是一个字符串数组。例如:
app.component('my-component', {
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function() {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function(value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
},
// 具有默认值的函数
propG: {
type: Function,
// 与对象或数组默认值不同,这不是一个工厂函数 —— 这是一个用作默认值的函数
default: function() {
return 'Default function'
}
}
}
})
type
可以是下列原生构造函数中的一个:
HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:
const app = Vue.createApp({})
app.component('blog-post', {
// camelCase in JavaScript
props: ['postTitle'],
template: '<h3>{{ postTitle }}</h3>'
})
使用如下:
<blog-post post-title="hello!"></blog-post>
重申一次,如果你使用字符串模板,那么这个限制就不存在了。