我们知道子组件是不能引用父组件或者Vue实例的数据的。但是,在开发中,往往一些数据确实需要从上层传递到下层:比如在一个页面中,我们从服务器请求到了很多的数据。其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。这个时候,并不会让子组件再次发送一个网络请求 ,而是直接让大组件(父组件)将数据传递给小组件(子组件)。
props的值有两种方式:
我们说过,除了数组之外,我们也可以使用对象,当需要对props进行类型等验证时,就需要对象写法了。
验证都支持哪些数据类型呢?
当我们有自定义构造函数时,验证也支持自定义的类型。
Vue.component('my-component',
{
props: {
//基础的类型检查( 'null’匹配任何类型)
propA: Number ,
//多个可能的类型
propB: [string, Number],
//必填的字符串
propc: {
type: string,
required: true
},
//带有默认值的数字
propD: {
type: Number,
default: 100
},
//带有默认值的对象
propE: {
type: object,
//对象或数组默认值必须从-个工厂函数获取
default: function() {
return {message: 'he1lo'}
}
},
//自定义验证函数
propF: {
validator: function(value) {
//这个值必须匹配下列字符串中的一个
return [' success', 'warning', ' danger'].indexOf(value) !== -1
}
}
}
})
// 自定义类型
function Person(firstName, lastName){
this.firstName = firstName;
this.lastName = lastName;
}
Vue.component('blog-post', {
props: {
author: Person
}
})
当props中变量用驼峰命名时,那么父组件向子组件传值时,必须化为短线 “-”,例如:
// 父传子 props
const cpn = {
template: '#cpn',
props: {
cMessage: {
type: String,
default: ' ',
required: true
}
},
data(){
return{}
}
}
const app = new Vue({
el: '#app',
data: {
message: '你好',
},
components: {
cpn: cpn
}
})
那么用子组件传值的时候,应该这样写:
<div id="app">
<cpn :c-message="message"></cpn>
</div>
一种比较常见的是 子组件 传递数据或事件到父组件中,这个时候,我们需要使用自定义事件来完成。
什么时候需要自定义事件呢?
自定义事件的流程:
案例-点击加减:
<!--父组件模板-->
<div id="app">
<cpn @add-click="add" @sub-click="sub"></cpn>
<H2>{{counter}}</H2>
</div>
<!--子组件模板-->
<template id="cpn">
<div>
<button @click="increament"> +1 </button>
<button @click="decreament"> -1 </button>
</div>
</template>
// 子组件
const cpn = {
template: '#cpn',
methods: {
increament() {
// 发射,向外派发给父组件
this.$emit('add-click')
},
decreament() {
this.$emit('sub-click')
}
}
}
// 父组件
const app = new Vue({
el: '#app',
data(){
return{
counter: 0
}
},
components:{
cpn
},
methods: {
add(){
this.counter++;
},
sub(){
this.counter--;
}
}
})
案例二:父组件中引用upload子组件,当上传文件的时候,需要把上传的文件值传递给父组件。这里就用到了$emit()事件进行派发
子组件中:
<template>
<div>
<div class="file">{{tip}}
<input type="file" @change="change" :accept="accept">
</div>
</div>
</template>
<script>
export default {
props: {
tip:{
type: String,
default: '上传文件',
},
taskId: {
type: Number
},
accept: {
type: String
},
},
created(){},
methods: {
change(){
this.$emit('updateFile',event);
},
}
};
</script>
<style lang="scss">
.el-button{
padding: 9px 15px;
}
.file {
position: relative;
display: inline-block;
background: #2C68FF;
border: 1px solid #99D3F5;
border-radius: 4px;
padding: 4px 12px;
overflow: hidden;
color: #ffffff;
text-decoration: none;
text-indent: 0;
line-height: 20px;
}
.file input {
position: absolute;
font-size: 100px;
right: 0;
top: 0;
opacity: 0;
}
.file:hover {
background: #5686ff;
border-color: #5686ff;
color: #ffffff;
text-decoration: none;
}
</style>
这个change方法,通过$emit("暴露在父组件中绑定的方法名 updateFile",需要传递给父组件的值: event);把这个值——上传的文件,派发给父组件。
在父组件中:
<template>
<div>
<div class="file">{{tip}}
<upload :tip="instructions" :accept="docAccept" ref="document" :taskId="taskId" @updateFile="changeIn"/>
</div>
</div>
</template>
<script>
export default {
data(){
return{
instructions: '上传任务说明书',
docAccept: '.pdf,.zip,.rar,.doc,.docx',
taskId: 0,
document: '',
};
methods: {
changeIn(event){
this.document = event.target.files[0];
},
}
};
</script>
PS: 第二个案例中props和$emit其实都用到了,可以自行理解。