-
什么是组件化
将一个完整的页面分成很多个组件,每个组件用于实现页面的一个功能块,每一个组件又可以细分。 -
组件化基本使用
-
组件化使用三个步骤:
1.创建组件构造器
2.注册组件
3.使用组件
组件化使用步骤
-
<div id="app">
<!-- 3.使用组件-->
<my-cpn></my-cpn>
</div>
<script>
// 用``定义字符串可以换行
// 1.创建组件构造器对象
const cpnC = Vue.extend({
template: `
<div>
<h2>我是标题1</h2>
<p>我是内容111</p>
<p>我是内容222</p>
</div>`
})
// 2.注册组件
Vue.component('my-cpn', cpnC);
const app = new Vue({
el: '#app',
data: {
}
})
</script>
-
全局组件
全局组件:
// 注册组件(全局组件),可以在多个vue实例下使用
Vue.component('my-cpn', cpnC);
局部组件:
const app = new Vue({
el: '#app',
data: {
},
// 注册局部组件,只能在该vue实例中使用
components: {
my_cpn: cpnC
}
})
- 父组件和子组件
<div id="app">
<my_cpn2></my_cpn2>
</div>
<script>
// 创建第一个组件构造器(子组件)
const cpn_c1 = Vue.extend({
template:`
<div>
<h2>标题1</h2>
<p>内容1</p>
</div>`
})
// 创建第二个组件构造器(父组件)
const cpn_c2 = Vue.extend({
template:`
<div>
<h2>标题2</h2>
<p>内容2</p>
<my_cpn1></my_cpn1>
</div>`,
// 在组件2中注册组件1,就可以在组件2中使用组件1
components: {
my_cpn1: cpn_c1
}
})
const app = new Vue({
el: '#app',
data: {
},
components: {
my_cpn2: cpn_c2
}
})
</script>
- 注册组件语法糖和组件模板抽离写法
<div id="app">
<my-cpn1></my-cpn1>
<my_cpn2></my_cpn2>
</div>
<template id="my-cpn">
<div>
<h2>我是标题1</h2>
<p>我是内容111</p>
</div>
</template>
<script>
// 注册全局组件
Vue.component('my-cpn1', {
template: '#my-cpn'
});
const app = new Vue({
el: '#app',
data: {
},
// 注册局部组件,只能在该vue中使用
components: {
'my_cpn2': {
template: '#my-cpn'
}
}
})
</script>
- 组件中的data和方法
<div id="app">
<my_cpn></my_cpn>
<my_cpn></my_cpn>
</div>
<template id="cpn">
<div>
<h2>当前计数:{{counter}}</h2>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
</template>
<script>
const app = new Vue({
el: '#app',
components: {
'my_cpn': {
template: '#cpn',
// 存放组件的数据
data() {
return {
counter: 0
}
},
methods: {
increment() {
this.counter ++;
},
decrement() {
this.counter --;
}
}
}
}
})
</script>
-
组件通信
1.父传子props
父子组件通信
在组件中使用props来声明需要从父级接收的数据
props的值有两种方式:
方式一:字符串数组,数组中的字符串就是传递时的名称
方式二:对象,对象可以设置传递时的类型,也可以设置默认值等
function Person(name){
this.name = name
}
Vue.component('my-conmponent', {
props: {
// 基础类型检查(null匹配任何类型)
itemA: Number,
// 多个可能的类型
itemB: [String, Number],
// 必须传的
itemC: {
type: String,
required: true
},
// 有默认值的
itemD: {
type: String,
default: 'ABC'
},
// 有默认值的数组
itemE: {
type: Array,
default() {
return [];
}
},
// 有默认值的对象
itemF: {
type: Object,
default() {
return {message: 'hello'}
}
},
// 自定义验证函数
itemG: {
validator(value) {
return ['success','warning','danger'].indexOf(value) !== -1
}
},
// 验证自定义类型
itemH: Preson
}
})
举例:
<div id="app">
<my_cpn v-bind:cmovies="movies" :msg="message"></my_cpn>
</div>
<template id="cpn">
<ul>
<li v-for="item in cmovies">{{item}}</li>
</ul>
<h3>{{msg}}</h3>
</template>
<script>
const my_cpn = {
template: '#cpn',
// props: ['cmovies', 'msg']
props: {
// 1.类型限制
// cmovies: Array,
// msg: String
// 2.提供默认值
msg: {
type:String,
default: 'hi',
// 必传值(要使用my_cpn组件,必须传msg)
required: true
},
cmovies: {
type: Array,
default() {
return []
}
}
}
}
const app = new Vue({
el: '#app',
data: {
movies: ['送你一朵小红花', '金刚川', '少年的你'],
message: 'hello'
},
components: {
my_cpn
}
})
</script>
2.子传父
<div id="app">
<my_cpn @item-click="in_cpn_click"></my_cpn>
</div>
<template id="cpn">
<div>
<button v-for="item in categories" @click="btnClick(item)">{{item.name}}</button>
</div>
</template>
<script>
// 子组件
const my_cpn = {
template: '#cpn',
data() {
return {
categories: [
{id: 1, name: '热门推荐'},
{id: 2, name: '手机数码'},
{id: 3, name: '美妆护肤'}
]
}
},
methods: {
btnClick(item) {
console.log(item.name + '被点击');
// 告诉父组件被点击的是哪一个item
// 发射事件
this.$emit('item-click', item)
}
}
}
// 父组件
const app = new Vue({
el: '#app',
components: {
my_cpn
},
methods: {
in_cpn_click(item) {
console.log('子组件中的' + item.name + '被点击');
}
}
})
</script>
-
组件访问
1.父访问子
<div id="app">
<cpn></cpn>
<!-- refs方式需要在使用组件时,添加ref属性 -->
<cpn ref="cpn2"></cpn>
<button @click="btnClick()">按钮</button>
</div>
<template id="cpn">
<div>我是子组件</div>
</template>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'hello world'
},
methods: {
btnClick() {
// $children较少用,一般用refs
// console.log(this.$children);
// this.$children[0].showMessage()
// console.log(this.$children[0].name)
console.log(this.$refs.cpn2.name);
this.$refs.cpn2.showMessage();
}
},
components: {
cpn: {
template: '#cpn',
data() {
return {
name: 'Naruto'
}
},
methods: {
showMessage() {
console.log('showMessage');
}
},
}
}
})
</script>
2.子访问父
不建议使用
-
组件的插槽
组件的插槽是为了让我们封装的组件更具扩展性,让使用者可以决定组件内部的一些内容到底展示什么。
将共性抽取到组件中,将不同暴露为插槽。可以让使用者根据自己的需求,决定插槽中插入的内容。
<div id="app">
<cpn><button>按钮</button></cpn>
<cpn><span>哈哈哈</span></cpn>
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>我是组件标题</h2>
<p>我是组件内容</p>
<!-- 插槽即预留一些空间,使用时可以在组件中插入其它标签 -->
<!-- <slot></slot> -->
<!-- 如果使用时没有插入标签,则默认显示插槽中的标签 -->
<slot><button>插槽默认按钮</button></slot>
</div>
</template>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'hello world'
},
components: {
cpn: {
template: '#cpn'
}
}
})
</script>
插槽
- 具名插槽
<div id="app">
<!-- 替换没有name的slot -->
<cpn><span>span</span></cpn>
<cpn>
<template v-slot:left>
<span>替换左边slot</span>
</template>
<template v-slot:right>
<span>替换右边内容</span>
</template>
</cpn>
</div>
<template id="cpn">
<div>
<h2>组件标题</h2>
<p>组件内容</p>
<slot name="left">左边</slot>
<slot name="center">中间</slot>
<slot name="right">右边</slot>
<slot></slot>
</div>
</template>
<script>
const app = new Vue({
el: '#app',
components: {
cpn: {
template: '#cpn'
}
}
})
</script>
具名插槽
-
作用域插槽
父组件替换插槽的标签,但是内容由子组件来提供。
<!-- 父组件替换插槽的标签,但是内容由子组件来提供 -->
<div id="app">
<cpn></cpn>
<cpn>
<!-- 获取子组件中的pLanguages -->
<template v-slot:default="slotProps">
<span v-for="item in slotProps.languages">{{item}} - </span>
</template>
</cpn>
</div>
<template id="cpn">
<div>
<!-- 将子组件的数据传给父组件 -->
<slot :languages="pLanguages">
<ul>
<li v-for="item in pLanguages">{{item}}</li>
</ul>
</slot>
</div>
</template>
<script>
const app = new Vue({
el: '#app',
data: {
},
components: {
cpn: {
template: '#cpn',
data() {
return {
pLanguages: ['JavaScript', 'Java', 'Python', 'c++', 'go', 'Swift']
}
}
}
}
})
</script>