组件注册
<!-- 组件使用 -->
<div id="exp">
<my-component></my-component>
</div>
// 1. 全局注册 任何地方都可使用
Vue.component('my-component', {
props: ['msg'],
template: '<span>this is my component!</span>',
data: function(){
return {
...
}
}
});
// 2. 创建根实例
new Vue({
el: '#exp',
data: {}
});
注意:
组件中的 data 必须是一个函数,否则 Vue 会停止工作。这样做是为了避免多个组件实例引用同一个 data 对象,导致彼此相互影响。换句话说,是为了保证组件实例间的相互独立,而这是通过每次调用 data 函数返回一个新对象来实现的。
Props
1. 字面量语法
组件实例的作用域是孤立的,不能在子组件的模板内直接引用父组件的数据。如果子组件要引用父组件的数据,需要使用 props
属性。
Vue.component('child', {
props: ['myName'], // 声明 props
template: '<span>{{ myName }}</span>'
});
这里的 props
就如同实例中的 data
对象,也可以在实例中以 this.myName
形式使用。
<child my-name="hello!"></child>
注意:
HTML 特性不区分大小写。使用驼峰式命名的 prop 需要转换为相对应的短横线隔开式命名。
myName --> my-name
2. 动态语法:v-bind
类似于用 v-bind
绑定 HTML
特性,我们也可用 v-bind
将动态 props
绑定到父组件中的数据。实现父组件与子组件数据变化的传递。
<div id="exp-1">
<input v-model="parentMsg">
<child :msg="parentMsg"></child>
</div>
new Vue({
el: '#exp-1',
data: {
parentMsg: 'parent message'
},
components: {
child: {
props: ['msg'],
template: '<button>{{msg}}</button>'
}
}
});
注意:
这种数据绑定是单向的,父组件数据会影响子组件数据,但子组件数据不会影响父组件。
3. Prop验证
我们可以为组件的 props
指定验证规格。如果传入的数据不符合规格,Vue
会发出警告。
Vue.component('my-component', {
props: {
prop1: 'null', // 任何类型皆可
prop2: [String, Number], // 指定多种类型
prop3: {
type: Number,
reuqired: true, // 属性必需且为数值型
default: 20, // 指定默认值
twoway: true, // 指定此prop为双向绑定 2.0移除
validator: function(v){ // 自定义验证器
return v > 10
}
}
}
});
当 props
验证失败时, Vuejs
将拒绝在子组件上设置此值。
组件通信 / 2.0+
1. v-on 绑定自定义事件
$on
监听事件:vm.$on('eventName', handler)
$emit
触发事件:vm.$emit('eventName', args)
<div id="exp-2">
<h4>Total:{{total}}</h4>
<my-btn @customEvt="reduceNum">{{counter}}</my-btn>
<my-btn @customEvt="reduceNum">{{counter}}</my-btn>
</div>
在使用子组件的地方直接用 v-on:customEvt
来监听子组件事件的触发,来实现父子组件的通信。
Vue.component('my-btn', {
template: '<button @click="addNum">{{counter}}</button>',
data: function(){
return {
counter: 0
}
},
methods: {
addNum: function(){
this.counter++;
this.$emit('customEvt');
}
}
});
new Vue({
el: '#exp-2',
data: {
total: 0
},
methods: {
reduceNum: function(){
this.total++;
}
}
});
2. 双向绑定的两种简单实现
# v-model + watch
- 由于子组件内部
props
不可写,因此我们需要在data
对象中创建一个副本并将其初始为props
属性的值。 - 组件外修改
props
并不会同步到副本中,因此我们设置一个侦听器watch
将props
与其副本数据同步。 - 监听副本状态变化,触发事件回调
this.$emit('input', val)
。 - 最后结合
v-model
将子组件的数据传递到外部,父组件数据响应式改变。
<template id="temp">
<div>
<input v-model="myname">
<button>{{myname}}</button>
</div>
</template>
<div id="exp-3">
<h3>父组件</h3>
<input v-model="name">
<button>{{name}}</button>
<h3>子组件</h3>
<child :sname="name" v-model="name"></child>
</div>
new Vue({
el: '#exp',
data: {
name: 'zz'
},
components: {
'child': {
template: '#temp',
props: ['sname'],
data: function(){
return {
myname: this.sname // 1
}
},
watch: {
sname: function(val){
this.myname = val; // 2
},
myname: function(val){
this.$emit('input', val); // 3
}
}
}
}
});
# props + obj
将一个引用类型传递给 props
属性,由于对象的地址引用特性,子组件修改对象属性父组件数据也相应改变。借此实现组件数据的双向绑定。
<template id="temp">
<div>
<input v-model="obj.name">
<button>{{obj.name}}</button>
</div>
</template>
<div id="exp">
<h3>父组件</h3>
<input v-model="obj.name">
<button>{{obj.name}}</button>
<h3>子组件</h3>
<child :obj="obj"></child>
</div>
new Vue({
el: '#exp',
data: {
obj:{
name: 'oo'
}
},
components:{
'child':{
template: '#temp',
props: ['obj']
}
}
});