一、如何使用组件
1.全局注册一个组件
Vue.component('my-component',{
template:'<div>这是一个组件</div>'
});
// 注: 全局组件在任何实例中都可以使用。要在实例中使用全局组件,必须在初始化根实例之前注册组件,否则会报错。
2.注册一个局部组件
/**html**/
<div id='app1'>
<my-component></my-component>
</div>
// js
var app1 = new Vue({
el: '#app1',
data: {
msg: 'app1的内容'
},
components: {
'my-component': {
template: "<div>这是一个局部组件</div>"
}
}
});
//注:局部注册的组件只能在其实例中使用,当全局组件与实例组件重名时,会优先使用局部组件。
3.组件的data选项必须是一个函数
当一个组件被定义,data必须声明为一个初始数据对象的函数,因为组件可能用来创建多个实例。如果data仍然是一个纯粹的对象,则所有的实例将共享引用同一个数据对象。通过提供data函数,每次创建一个新实例后,我们能够调用data函数,从而返回初始数据的一个全新副本数据对象。如:
<div id="example1">
<simple-counter></simple-counter>
<simple-counter></simple-counter>
<simple-counter></simple-counter>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
var data = { counter: 0 }
Vue.component('simple-counter', {
template: '<button v-on:click="counter += 1">{{ counter }}</button>',
// 技术上 data 的确是一个函数了,因此 Vue 不会警告,
// 但是我们却给每个组件实例返回了同一个对象的引用
data: function () {
return data
}
})
new Vue({
el: '#example1'
})
</script>
// 这时点击 button ,每个按钮的 counter 都是一样的,因为三个组件实例共享了同一个 data 对象,因此递增一个 counter 会影响所有组件。
修改一下: 可以通过为每个组件返回全新的数据对象来修复这个问题,每个counter组件都有自己的状态。
二、组件组合
组件最常见的关系就是父子组件关系:组件A在其模板中使用了组件B,它们之间必然需要相互通信:父组件给子组件下发数据,子组件则将内部的事件告知父组件
Vue中,父子组件的关系总结为:父组件通过prop向子组件传递数据,子组件通过事件向父组件发送消息。
1.使用prop传递数据
//组件的作用域不是孤立的,不能再子组件模块内直接使用父组件的数据,父组件的数据需要通过prop才能下发到子组件中。
// 子组件要显示地使用prop选项声明他预期的数据
<div id="example1">
<!--给子组件传递了一个字符串-->
<child message='hello world'></child>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
Vue.component('child',{
props:['message'],//子组件预期希望的数据
template:'<span>{{message}}</span>'
});
var example1 = new Vue({
el: '#example1'
});
</script>
2.prop命名
a.camelCase(驼峰式) b. kebab-case(短线分割式)
// html特性是不区分大小写的。所以camelCase(驼峰式命名)的prop需要转换为相对应的kebab-case(短线分割式命名)。
<div id="example1">
<!-- messageMsg需要转化为message-Msg -->
<child message-Msg='hello world'></child>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
Vue.component('child',{
props:['messageMsg'],
template:'<span>{{messageMsg}}</span>'
});
var example1 = new Vue({
el: '#example1'
});
</script>
// 注:对于字符串模板,没有这个限制
3.动态prop
a.prop静态的传值
<blog-post title="My journey with Vue"></blog-post>
b.prop动态传值
与绑定到任何普通的html特性相类似,可以用v-bind来动态的将prop绑定到父组件的数据。每当父组件的数据变化时,该变化也会传递给子组件。
<div id="example1">
<input type="text" v-model="msg" />
<child v-bind:message-Msg='msg'></child>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
Vue.component('child',{
props:['messageMsg'],
template:'<span>{{messageMsg}}</span>'
});
var example1 = new Vue({
el: '#example1',
data: {
msg: '动态的数据'
}
});
</script>
4.字面量语法 vs 动态语法
要传递javascript表达式,而不是字符串时,需要动态语法 v-bind,prop可以接受任何类型的值。
a.传入一个数字
<!-- 即便 `42` 是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
<!-- 这是一个 JavaScript 表达式而不是一个字符串。-->
<blog-post v-bind:likes="42"></blog-post>
<!-- 用一个变量进行动态赋值。-->
<blog-post v-bind:likes="post.likes"></blog-post>
b.传入一个布尔值
<!-- 包含该 prop 没有值的情况在内,都意味着 `true`。-->
<blog-post favorited></blog-post>
<base-input v-bind:favorited="false">
<base-input v-bind:favorited="post.currentUserFavorited">
c. 传入一个数组
<blog-post v-bind:comment-ids="[234, 266, 273]"></blog-post>
<blog-post v-bind:comment-ids="post.commentIds"></blog-post>
d.传入一个对象
<blog-post v-bind:comments="{ id: 1, title: 'My Journey with Vue' }"></blog-post>
<blog-post v-bind:post="post"></blog-post>
e. 传入一个对象的所有属性
<div id="parent">
<Asyncexample v-bind="post"></Asyncexample>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
Vue.component('Asyncexample',{
props:['id','title'],
template: '<div>{{id}}<div>{{title}}</div></div>'
});
var parent = new Vue({
el: '#parent',
data: {
bool:false,
post:{
id:1,
title:'hello world'
}
}
});
</script>
5.单向数据流
prop是单向绑定的:当父组件属性变化时,将传导到子组件,但反过来不会。这是为了防止组件无意间修改了父组件的状态,避免应用的数据难以理解。
每次父组件更新时,子组件的所有prop都会更新为最新值,这意味不应该在子组件内部改变prop,如果做了,vue会在控制台发出警告。
有两种情况很容易去修改prop的数据
(1)prop作为初始值传入后,子组件想把它作为局部数据来用
<div id="example1">
<child v-bind:order='num'></child>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
Vue.component('child',{
props:['order'],
template:'<input v-model="order">',//如果直接在页面input修改数据,控制台会报错,提示避免直接修改prop,父组件更新时会覆盖修改的数据
data: function () {
return {
}
}
});
var example1 = new Vue({
el: '#example1',
data: {
num: 100
}
});
</script>
// 正确的应对方法是:定义一个局部变量,并用prop的值初始化它:
// 修改为:
<div id="example1">
<child v-bind:order='num'></child>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
Vue.component('child',{
props:['order'],
template:'<input v-model="counter">',
data: function () {
return {
counter: this.order //把prop的值赋给counter,此时修改input的值就不会报错了
}
}
});
var example1 = new Vue({
el: '#example1',
data: {
num: 100
}
});
</script>
(2)prop作为原始数据传入后,由子组件处理成其他数据输出:
// 正确的做法是:定义一个计算属性,处理prop的值并返回
<div id="example1">
<child v-bind:message='msg'></child>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
Vue.component('child',{
props:['message'],
template:'<span>{{format}}</span>',
computed: {
format: function () {
return this.message.toLowerCase()
}
}
});
var example1 = new Vue({
el: '#example1',
data: {
msg: 'HELLO,WORLD'
}
});
</script>
// 注:在javascript中对象和数组是通过引用传递的,所以对于一个数组或者对象类型的prop来说,
// 在子组件中改变对象或者数组本身,将会影响到父组件的状态。
6.prop验证
组件的prop可以指定验证规则,如果传入组件的数据不符合要求,vue会抛出警告
要指定验证规则,需要用对象的形式来定义prop,而不能用字符串数组。
<div id="example1">
<child v-bind:order='num'></child>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script>
Vue.component('child', {
props: {
'order': {// 自定义验证函数
validator: function(value) {
if(value == 1){
console.log('验证成功');
this.order = '第一名';//修改不起作用,因为 prop 会在组件实例创建之前进行校验
return true
}
}
}
// 'order':{
// type: Number,
// default: 100
// }
// propNameA: Number,// 基础类型检测 (null指允许任何类型)
// propNameB: [String, Number],// 可能是多种类型
// propNameC: {// 必传且是字符串
// type: String,
// required: true
// },
// propNameD: {// 数值且有默认值
// type: Number,
// default: 100
// },
// propNameE: {// 数组/对象的默认值应当由一个工厂函数返回
// type: Object,
// default: function() {
// return {
// message: 'hello'
// }
// }
// },
},
template: '<span>排序:{{order}}</span>',
});
var example1 = new Vue({
el: '#example1',
data: {
num:1
}
});
// prop 的类型type可以是原生构造器,string,number,Boolean,function,object,array,symbol
// 注:prop会在组件实例创建之前进行校验,所以字default或validator函数里,诸如data,computed,或methods等实例属性还无法使用。
</script>