1.创建组件构造器
const myComponent = Vue.extend({
template:`
组件标题
我是子组件
`
})
调用 Vue.extend 创建的是一个组件构造器,在创建组件构造器时,传入template 代表自定义组件模板。
2.注册组件
Vue.component('cpn',myComponent)
调用Vue.component 将刚才的组件构造器注册为一个组件,并起一个组件标签名称。
3.使用组件
<div id="app">
<cpn><cpn/>
div>
全局组件
当通过 Vue.component 注册组件时,组件的注册是全局的,也就是这个组件可以在任意的Vue 实例下使用。
局部组件
当在某一具体的 vue 实例中设置 注册组件 这一步骤时,此时,该组件为局部组件
const vm = new Vue({
el:"#app",
data:{
},
components:{
'cpn':myComponent
}
注册组件的简写省去了调用 Vue.extend 的步骤,而是直接可以使用一个对象来代替。
注册全局组件的简写:
<script>
Vue.component('cpn',{
template:`
组件标题
我是子组件
`
})
script>
注册局部组件的简写:
const vm = new Vue({
el:"#app",
data:{
},
components:{
'cpn':{
template:`
组件标题
我是子组件
`
}
抽离模板:
<template id="cpn">
<div>
<h2>组件标题h2>
<p>我是子组件p>
div>
template>
<script>
const vm = new Vue({
el:"#app",
data:{
},
components:{
'cpn':{
template:'#cpn'
}
组件是不可以访问vue 实例中的数据的,Vue 组件应该有保存自己数据的地方。
组件对象也有一个 data 属性,也可以有 methods 属性。这个data必须是一个函数,而且返回一个对象,对象内部保存着数据。
components:{
'cpn':{
template:'#cpn',
data(){
return {
message: '我是子组件中的message'
}
为什么 data 是一个函数
如果不是一个函数,那么 Vue 会直接报错。
其次,Vue 可以让每个组件对象都返回一个新的对象,因为如果是同一个对象的话,多个组件使用时会互相影响。比如该组件为加减操作,使用多个组件时,假如 data 不是函数,那么所有组件共用同一个data,其中一个加减后,其余的组件都会被影响。
父组件和子组件的注册方式如下:
<script>
const vm = new Vue({
el:"#app",
components:{
'parent-cpn':{
template: '#parent-cpn',
components:{
'child-cpn':{
template:'#child-cpn'
}
script>
子组件是不能引用父组件或者 Vue 实例中的数据的。在一个页面中,通常由大组件获取数据,直接让大组件将数据传递给小组件,并不会让小组件去发送网络请求。
Vue 实例和子组件的通通信 和 父组件和子组件的通信 过程是一样的。
在组件中,使用 props 来声明需要从父组件接收到的数据。
props 的值有两种方式:
方式一:字符串数组,数组中的字符串就是传递时的名称。
方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。
数组方式
1.在子组件的标签中,添加父组件需要传递的数据。
<cpn :childmessage="message" :childmsg="msg">cpn>
冒号后面是在子组件中定义的数据名称,等于的内容是父组件中要传递的变量名称。
注意:父组件向子祖传传递数据时,冒号后面定义的名称不能采用驼峰写法。
2.当在标签中添加后,需要去子组件中添加 props。props 的内容即为标签中定义的名称。
props:['childmessage','childmsg']
3.在子组件的模板中使用props中的数据。
<template id="cpn">
<div>
<h2>{{childmessage}}h2>
<h3>{{ childmsg }}h3>
div>
template>
注意:当模板中有多个标签时,需要用div将多个标签包起来。
对象方式
当需要对props进行类型等验证时,需要采用对象的写法。
类型验证支持 String、Number、Boolean、Array、Object、Date、Function、Symbol,当有自定义构造函数时,也支持自定义类型。
简单的类型检测:
props:{
childmessage:String
}
检测多种数据类型:
props:{
childmessage:[String,Number]
}
提供默认值,此时,就算父子间没有向子组件传递childmessage,此childmessage也会生效,则显示设置的默认值:
props:{
childmessage:{
type: String,
default:'aaaaa'
}
}
必填的字符串,当设置了required,那么这个数据是必传的,否则会报错
props:{
childmessage:{
type: String,
required:true
}
}
当子组件需要向父组件传递数据时,要要用到自定义事件。
v-on不仅仅可以用于监听DOM事件,也可以用于组件间的自定义事件。
自定义事件流程:在子组件中,通过$emit()来触发事件。在父组件中,通过v-on来监听子组件事件。
下面以 子组件点击按钮传值给父组件为例说明。
1.在子组件中定义数据,该数据为数组类型。
data() {
return {
list:[{id:'18271',name:'热门推荐'},{id:'18272',name:'手机数码'},{id:'18273',name:'电脑办公'}]
}
2.在模板中遍历数据,并且为每一项注册点击事件,并将每一项的 item 传给该事件。
<template id="cpn">
<div>
<button v-for="item in list" @click="cpnclick(item)">{{item.name}}button>
div>
template>
3.在子组件中定义cpnclick 函数,通过$emit()来触发事件,并将 item 传给emitclick() 。
cpnclick(item){
this.$emit('emitclick',item)
}
4.在应用的子组件标签中添加如下内容,通过v-on来监听子组件事件。
<cpn @emitclick="btnclick">cpn>
5.在父组件中获取传递的内容
btnclick(item){
console.log(item);
}
有时候我们需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问跟组件。
父组件访问子组件:使用 c h i l d r e n 或 children或 children或refs。
子组件访问父组件:使用$parent
$children
this.$children 是一个数组类型,它包含所有子组件对象。
父组件中定义三个子组件
<template id="parent-cpn">
<div>
<h2>我是父组件h2>
<child-cpn>child-cpn>
<child-cpn>child-cpn>
<child-cpn>child-cpn>
<button @click="pbtnclick">访问子组件button>
div>
template>
pbtnclick(){
console.log(this.$children);// [VueComponent, VueComponent, VueComponent]
}
打印出来的是三个子组件的内容,可以使用 this.$children[0] 这种方式来访问各个组件。
$children的缺陷:
通过$children访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值。
但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化。
$refs
有时候,我们想明确获取其中一个特定的组件,这个时候就可以使用$refs。
$refs的使用:
首先,通过ref给某一个子组件绑定一个特定的ID。
<template id="parent-cpn">
<div>
<h2>我是父组件h2>
<child-cpn ref="child1">child-cpn>
<child-cpn ref="child2">child-cpn>
<child-cpn ref="child3">child-cpn>
<button @click="pbtnclick">访问子组件button>
div>
template>
然后,通过this.$refs.ID就可以访问到该组件了。
pbtnclick(){
console.log(this.$refs.child2.message);
}
$parent
在子组件中直接访问父组件,可以通过 this.$parent 后面可以追加父组件中的信息。
注意事项:
尽管在Vue开发中,我们允许通过$parent来访问父组件,但是在真实开发中尽量不要这样做。
子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。
如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题。
另外,更不好做的是通过$parent直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于我的调试和维护。
父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译。
在使用
的时候,整个组件的使用过程是相当于在父组件中出现的。
那么他的作用域就是父组件,使用的属性也是属于父组件的属性。
因此,isShow使用的是Vue实例中的属性,而不是子组件的属性。
组件的插槽也是为了让我们封装的组件更加具有扩展性。让使用者可以决定组件内部的一些内容到底展示什么。也就是,组件有些地方不能被写死,而是可以让使用者根据自己的需求,决定插槽中插入什么内容
在子组件中,使用特殊的元素就可以为子组件开启一个插槽。
<div id="app">
<cpn><button>哈哈哈button>cpn>
div>
<template id="cpn">
<div>
<slot><span>我是插槽默认内容span>slot>
div>
template>
使用组件时,即可为插槽填写内容,button 将会覆盖默认的 span。
当子组件的功能复杂时,子组件的插槽可能并非是一个。需要分别为插槽设置内容。此时,就需要给插槽起一个名字。
<div id="app">
<cpn><button slot="center">中间button>cpn>
div>
<template id="cpn">
<div>
<slot name="left">左边slot>
<slot name="center">中间slot>
<slot name="right">右边slot>
div>
template>
为每一个插槽起名之后,就可以针对性的插入内容,并且不会影响其他插槽。
子组件中有一些数据,希望在父组件中分别以不同形式展示,比如列表,横向,或者直接展示一个数组。
在父组件使用子组件时,可以拿到这些数据,然后就可以对这些数据进行操作了。
例如子组件中的数据
data() {
return {
list:['java','c++','python','go']
}
}
在子组件的插槽中将数据传给data,data名称可以任意取
<template id="cpn">
<div>
<slot :data="list">slot>
div>
template>
在使用子组件时,从子组件拿到数据,通过
<div id="app">
<cpn>
<template slot-scope="slotProps">
<ul>
<li v-for="item in slotProps.data">
{{ item }}
li>
ul>
template>
cpn>
<cpn>
<template slot-scope="slotProps">
<button v-for="item in slotProps.data">{{item}} button>
template>
cpn>
div>
更多Vue组件内容见官网 Vue组件