组件不仅仅是要把模板的内容进行复用,更重要的是组件间要进行通信。通常父组件的模板中包含子组件,父组件要正向地向子组件传递数据或参数,子组件接收到后根据参数的不同来渲染不同的内容或执行操作。这个正向传递数据的过程就是通过 props 来实现的。
在组件中,使用选项 props 来声明需要从父级接收的数据, props 的值可以是两种, 一种是字符串数组,一种是对象.
基本用法
<div id="app">
<my-component message="来自父组件的数据"></my-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
Vue.component('my-component',{
props: ['message'],
template:'<div>{{message}}</div>'
});
var app = new Vue({
el:'#app'
})
</script>
props 中声明的数据与组件 data 函数 return 的数据主要区别就是 props 的来自父级,而 data 中的是组件自己的数据,作用域是组件本身,这两种数据都可以在模板 template 及计算属性 computed和方法 methods 中使用。上例的数据 message 就是通过 props 从父级传递过来的,在组件的自定义标签上直接写该 props 的名称,如果要传递多个数据,在 props 数组中添加项即可。
由于 HTML 特性不区分大小写,当使用 DOM 模板时,驼峰命名 (camelCase)的 props 名称要转为短横分隔命名 (kebab-case)
有时候,传递的数据并不是直接写死的,而是来自父级的动态数据,这时可以使用指令 v-bind来动态绑定 props 的值,当父组件的数据变化时,也会传递给子组件。
<div id="app">
<input type="text" v-model="parentMessage">
<my-component :message="parentMessage"></my-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
Vue.component('my-component',{
props: ['message'],
template:'<div>{{message}}</div>'
});
var app = new Vue({
el:'#app',
data:{
parentMessage:''
}
})
</script>
注意,如果要直接传递数字、布尔值、数组、对象,而且不使用v-bind ,传递的仅是字符串
<div id="app">
<my-component message="[1,2,3]"></my-component>
<my-component :message="[1,2,3]"></my-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
Vue.component('my-component',{
props: ['message'],
template:'<div>{{message.length}}</div>'
});
var app = new Vue({
el:'#app'
})
</script>
单向数据流
Vue2.x与Vuel.x比较大的一个改变就是,Vue2.x通过props传递数据是单向的了,也就是父组件数据变化时会传递给子组件,但是反过来不行。而在Vuel.x里提供了.sync修饰符来支持双向绑定。之所以这样设计,是尽可能将父子组件解稿,避免子组件无意中修改了父组件的状态。
业务中会经常遇到两种需要改变 prop 的情况,一种是父组件传递初始值进来,子组件将它作为初始值保存起来,在自己的作用域下可以随意使用和修改。这种情况可以在组件 data 内再声明一个数据,引用父组件的 prop
<div id="app">
<my-component :init-count="1"></my-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
Vue.component('my-component',{
props:['initCount'],
template:'<div>{{count}}</div>',
data:function(){
return{
//初始化时获取来自父组件的initCount,之后与之无关只维护count
count:this.initCount
}
}
});
var app = new Vue({
el:'#app'
})
</script>
另一种情况就是 prop 作为需要被转变的原始值传入。这种情况用计算属性就可以了
<div id="app">
<my-component :width="100"></my-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
Vue.component('my-component',{
props:['width'],
template:'<div :style="style">组件内容</div>',
computed:{
style:function(){
//因为用css传递宽度要带单位px,但每次写太麻烦,而数值计算一般不带单位的,故统一在组件内使用计算属性。
return {
width:this.width+'px'
}
}
}
});
var app = new Vue({
el:'#app'
})
</script>
数据验证
props选项的值都是一个数组,一开始也介绍过,除了数组外,还可以是对象,当prop需要验证时,就需要对象写法。
一般当你的组件需要提供给别人使用时,推荐都进行数据验证,比如某个数据必须是数字类型,如果传入字符串,就会在控制台弹出警告。
Vue.component('my-component',{
props:{
//必须是数字类型
propA:Number,
//必须是字符串或数字类型
propB:[String,Number],
//布尔值,如果没有定义,默认值是true
propC:{
type:Boolean,
default:true
},
//数字,而且是必传
propD:{
type:Number,
required:true
},
//如果是数组或对象,默认值必须是一个函数来返回
propE:{
type:Array,
default:function(){
return [];
}
},
//自定义一个验证函数
propF:{
validator:function(value){
return value>10;
}
}
}
});
验证的type类型可以是:
•String
•Number
•Boolean
•Object
•Array
•Function
type也可以是一个自定义构造器,使用instanceof检测。当prop验证失败时,在开发版本下会在控制台抛出一条警告。