什么是组件?
vue官方文档是这样写的:
组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能。在有些情况下,组件也可以表现为用 is 特性进行了扩展的原生 HTML 元素。
下面我就以实际的例子来阐述我对vue使用的理解。接触vue不久,希望有bug的地方多多指教。
先上图,左侧是订单结算页面,会有地址选择,点击后会请求地址列表。右侧是地址列表页面,我封装了地址列表选择的组件。
组件的注册
vue提供了2种注册组件的方式:
(1)全局注册
// 注册
Vue.component('my-component', {
template: '<div>A custom component!</div>'
})
// 创建根实例
new Vue({
el: '#example'
})
注意要确保在初始化根实例之前注册组件。
(2)局部注册
局部注册的实现是在vue实例/组件的实例选项components注册,注册后仅在注册下的组件作用域范围内可用。
var Child = {
template: '<div>A custom component!</div>'
}
new Vue({
// ...
components: {
// <my-component> 将只在父组件模板中可用
'my-component': Child
}
})
本文中的例子也是采用局部注册的方式实现的。与官方文档中的示例不同的是,采取了ES6的import语法,将整个组件作为一个模块引入进来。
//订单结算页面orderSettle.vue
import ChooseAdress from '../components/component/chooseAddress.vue'
export default {
name:'orderSettle',
components:{
ChooseAdress:ChooseAdress
//其它引用组件 .......
}
}
组件间的数据传递
在 Vue 中,组件实例的作用域是孤立的。父子组件的关系可以总结为 prop 向下传递,事件向上传递。父组件通过 prop 给子组件下发数据,子组件通过事件给父组件发送消息。
在例子中,父组件订单结算页面(orderSettle)需要将请求后的地址列表传递给子组件(chooseAddress),子组件再将选择后的地址在传递给父组件。
(1)使用props声明预期数据
子组件要显式地用 props 选项声明它预期的数据,例如在ChooseAdress.vue中声明
export default {
name: "chooseAddress",
props: {
isDefault: Boolean,
contactName: String,
contactPhone: String,
contactAddress: String,
isSelect: Boolean,
id:Number
},
methods:{
//组件方法
}
}
}
除了数据类型,也可以有其它规则
Vue.component('example', {
props: {
// 基础类型检测 (`null` 指允许任何类型)
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 value > 10
}
}
}
})
(2)将父组件数据动态绑定到子组件中
与绑定到任何普通的 HTML 特性相类似,我们可以用 v-bind 来动态地将 prop 绑定到父组件的数据。每当父组件的数据变化时,该变化也会传导给子组件。
<popup v-model="showMoreAddress" class="full_height" position="right">
<div class="MoreAddressPopup">
<chooseAdress v-for="(add,index) in adressList" @setChooseAddress="parentCall"
:contactName="add.contactName" :contactPhone="add.contactPhone"
:contactAddress="add.contactAddress" :isDefault="add.isDefault" :id="add.id" :isSelect="add.isSelect" :key="index"
>
</chooseAdress>
</div>
</popup>
(3)子组件和父组件通信
父组件使用 prop 传递数据给子组件。但子组件怎么跟父组件通信呢?这个时候 Vue 的自定义事件系统就派得上用场了。我们在子组件内注册一个事件,将选择后的id传递给父组件。
<template>
<div class="address" @click="childCall(id)">
<x-icon type="ios-checkmark-empty" class="checked_icon" size="30" v-show="isSelect"></x-icon>
<h4 v-show="isDefault">默认地址</h4>
<h4>
<small>{{contactName}}</small> <small>{{contactPhone}}</small>
</h4>
<h4>
<small>{{contactAddress}}</small>
</h4>
</div>
</template>
在子组件的方法内使用$emit将事件通知出去,并把选择后的id传递给出去。
export default {
name: "chooseAddress",
props: {
//数据校验....
},
methods:{
childCall:function(addId){
this.$emit('setChooseAddress',addId);
}
}
}
}
//在父组件的模板中,进行关联
<chooseAdress v-for="(add,index) in adressList" @setChooseAddress="parentCall"
:contactName="add.contactName" :contactPhone="add.contactPhone"
:contactAddress="add.contactAddress" :isDefault="add.isDefault" :id="add.id" :isSelect="add.isSelect" :key="index"
>
</chooseAdress>
//在父组件的method对象里响应子组件事件
export default {
components: {
//......
},
name: 'orderSettle',
methods: {
parentCall(addId) {
this.adressList.forEach( (e,i)=>{
if(addId=e.id){
e.isSelect=true;
this.$set(this.adressList,i,e);
this.defaultAdress=e;
}else{
e.isSelect=false;
this.$set(this.adressList,i,e);
}
});
this.showMoreAddress = false;
}
}
}
自此,我们就完成了一个最简单的组件书写,并实现了子组件动态绑定父组件数据,子组件通过$emit将自身事件通知外部功能。