Vue基础知识点 — 组件
2、Vue 中的组件
1)组件化
组件化可以这么理解:面对一个复杂的应用时,我们可以将其分解为一个个独立可复用的组件来实现。
任何的应用都会被抽象成一颗组件树
组件使用步骤
- 创建组件构造器 Vue.extend()
- 注册组件 Vue.component(‘注册组件的标签名’, ‘组件构造器’)
- 使用组件 在Vue实例范围内使用组件
代码展示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>组件的基本使用</title>
</head>
<body>
<div id="app">
<my-cpn></my-cpn>
<my-cpn></my-cpn>
</div>
<script src="../js/vue.js"></script>
<script>
const Cpn = Vue.extend({
template: `
<div>
<h2>标题</h2>
<p>祝你今天也开心!!!</p>
</div>`
})
Vue.component('my-cpn', Cpn)
const app = new Vue({
el: '#app',
data: {
}
})
</script>
</body>
</html>
显示效果:
2)全局组件和局部组件
全局组件:是注册组件之后,在所有的vue实例中都可以使用。 (在全局作用域中注册)
局部组件:在vue实例中注册组件,只在对应的vue实例中可以使用
全局组件代码展示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>全局组件</title>
</head>
<body>
<div id="app">
<my-cpn></my-cpn>
<my-cpn></my-cpn>
</div>
<div id="app1">
<my-cpn></my-cpn>
</div>
<script src="../js/vue.js"></script>
<script>
const cpnC = Vue.extend({
template: `
<div>
hello,我是全局组件!!!
</div>
`
})
Vue.component('my-cpn', cpnC)
const app = new Vue({
el: '#app',
data: {
}
})
const app1 = new Vue({
el: '#app1',
})
</script>
</body>
</html>
效果显示:
局部组件代码展示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>局部组件</title>
</head>
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
</div>
<div id="app1">
<cpn></cpn>
</div>
<script src="../js/vue.js"></script>
<script>
const cpnC = Vue.extend({
template: `
<div>
hello, 这里是局部组件!!!
</div>
`
})
const app = new Vue({
el: '#app',
components: {
cpn: cpnC
}
})
const app1 = new Vue({
el: '#app1'
})
</script>
</body>
</html>
显示效果:
3)父子组件
父子组件:简单来说,就是在组件中注册组件,其中在组件内部注册的组件为子组件,外部的组件为父组件,在父组件内部可以使用子组件。
其实,Vue实例也可以看做是一个组件,可以看作是一个根组件。
代码展示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>父组件 子组件</title>
</head>
<body>
<div id="app">
<cpn2></cpn2>
</div>
<script src="../js/vue.js"></script>
<script>
// cpnC1是子组件
const cpnC1 = Vue.extend({
template: `
<div>
<h2>我是标题1</h2>
<p>祝前途似锦</p>
</div>
`
})
// 在cpnC2组件中, 注册cpn1组件 其中cpnC2是父组件
const cpnC2 = Vue.extend({
template: `
<div>
<h2>我是标题2</h2>
<p>祝学有所成</p>
<cpn1></cpn1>
</div>
`,
components: {
cpn1: cpnC1
}
})
const app = new Vue({
el: '#app',
components: {
cpn2: cpnC2
}
})
</script>
</body>
</html>
显示效果:
4)注册组件语法糖 + 模板抽离
在注册组件的过程中,还有语法糖的写法。
就是省略了Vue.extend()
此外,在Vue.extend()中写template真的太不方便,可以进行模板的抽离
注意:如果在template中有多行标签(内容)时,需要保证其有一个根元素,建议最好将内容包在一个div中,否则会报错!!!
代码展示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册组件语法糖 模板分离</title>
</head>
<body>
<div id="app">
<cpn></cpn>
<cpn1></cpn1>
</div>
<template id="cpn">
<div>
<h2>我是模板</h2>
<p>请问,你今天学习了吗???</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
Vue.component('cpn', {
template: `#cpn`
})
const app = new Vue({
el: '#app',
components: {
cpn1: {
template: `#cpn`
}
}
})
</script>
</body>
</html>
显示效果:
5)组件中的data() - 函数
组件中也有自己的data属性,不过是以函数的形式存在的。这样保证每一份数据都保存在一个新的对象中。
eg:如果复用了多个组件,每个组件调用的数据都是同一个,那么改变一个组件的对应数据,其他组件的对应数据也会改变,这不是我们想要的效果。
这里以封装一个简单的加减器来说明:
代码展示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>组件中的data</title>
</head>
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>当前的数字为:{{count}}</h2>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
Vue.component('cpn', {
template: `#cpn`,
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++;
},
decrement() {
if(this.count > 0) {
this.count--;
}
}
}
})
const app = new Vue({
el: '#app',
})
</script>
</body>
</html>
显示效果:
6)组件之间的通信
组件之间需要通信出现的情况: 当我们在搭建界面时,需要从服务器请求数据,但是一般情况下,服务器是将我们请求的整个数据都发送给了界面中的类似根组件的对象(可能不太准确,个人理解),界面中内部的组件要想获得服务器传来的数据,需要与父组件进行通信。
父子组件的通信:
- 通过props 向子组件传递数据 (props)
- 通过事件向父组件传递消息 ($emit)
父组件向子组件传递数据 props
代码展示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>父组件向子组件传递数据</title>
</head>
<body>
<div id="app">
<cpn :cmessage="message" :cmovies="movies"></cpn>
</div>
<template id="cpn">
<div>
<ul>
<li v-for="item in cmovies">{{item}}</li>
</ul>
<h2>{{cmessage}}</h2>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: `#cpn`,
props: {
cmovies: {
type: Array,
required: true, //用了required,这个属性必须得传
default: []
},
cmessage: {
type: String,
default: '愿自由自在'
}
},
data() {
return {}
}
}
//这里将vue看作是父组件
const app = new Vue({
el: '#app',
data: {
movies: ['哈尔的移动城堡', '天空之城', '千与千寻', '悬崖上的金鱼姬'],
message: '今天也要好好加油呀'
},
components: {
cpn
}
})
</script>
</body>
</html>
显示效果:
props 驼峰标识
代码展示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>父组件 props中的驼峰标识</title>
</head>
<body>
<div id="app">
<cpn :c-info="info" :c-message="message"></cpn>
</div>
<template id="cpn">
<div>
<h2>{{cInfo}}</h2>
<p>{{cMessage}}</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: `#cpn`,
props: {
cMessage: {
type: String,
default: '没有关系的,宝贝!!!'
},
cInfo: {
type: Object,
default: {}
}
}
}
const app = new Vue({
el: '#app',
data: {
info: {
name: 'YiBo',
age: 18,
height: 1.82
},
message: '不要emo了!!!'
},
components: {
cpn
}
})
</script>
</body>
</html>
显示效果:
子组件向父组件传递数据 $emit
子组件需要向父组件传递数据的情况:在搭建界面的时候,有时候希望点击某个按钮,触发相应的事件,此时,界面发生相应的改变;这时候,需要通知父组件向服务器请求对应的数据,需要进行子组件与父组件之间的通信。
自定义事件:
- 在子组件中,通过 $emit() 来触发事件
- 在父组件中,通过v-on来监听子组件的事件
代码展示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>子组件向父组件传递数据</title>
</head>
<body>
<div id="app">
<cpn @itemclick="cpnClick"></cpn>
<!--这里方法不写(),默认会将第一个参数传过来-->
</div>
<template id="cpn">
<div>
<button v-for="item in categories" @click="btnClick(item)">{{item.name}}</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: `#cpn`,
data() {
return {
categories: [
{id: 'one', name: '新鲜水果'},
{id: 'two', name: '海鲜水产'},
{id: 'three', name: '蔬菜蛋品'},
{id: 'four', name: '冷饮冻食'}
]
}
},
methods: {
btnClick(item) {
//发射事件,是自定义的
this.$emit('itemclick', item);
}
}
}
const app = new Vue({
el: '#app',
data: {
},
components: {
cpn
},
methods: {
cpnClick(item) {
console.log('cpnclick', item); //获取子组件传过来的数据
}
}
})
</script>
</body>
</html>
显示效果:
7)父子组件间的访问方式
出现情景:有些情况下,我们需要父组件直接访问子组件,子组件直接访问父组件,或者子组件访问根组件
- 父组件访问子组件:$children(用的相对较少)、 $refs(用的超级多)
- 子组件访问父组件:$parent
$children:会一次获取所有的子组件
$refs:获取组件中具有ref属性的组件
父访问子:$children、 $refs
父访问子:$children
由于每次通过 $children 获取的都是父组件所有的子组件,主要是通过数组下标获取单个子组件,这样操作很不方便,有时也会出现问题(eg: 在原有的子组件中插入了新的组件,则数组下标会发生改变)。所以大多数情况下,我们不使用这种方式。该方式适合获取父组件的所有子组件
代码展示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>父传子 $children $refs</title>
</head>
<body>
<div id="app">
<cpn></cpn>
<button @click="btnClick">按钮</button>
</div>
<template id="cpn">
<div>
<h2>我是子组件!!!</h2>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
},
methods: {
btnClick() {
//$children获取的是所有的子组件
console.log(this.$children);
//获取第一个cpn组件中的name属性
console.log(this.$children[0].name);
}
},
components: {
cpn: {
template: `#cpn`,
data() {
return {
name: 'YiBo'
}
},
methods: {
showMessage() {
console.log('这里是子组件!!!');
}
}
}
}
})
</script>
</body>
</html>
显示效果:
父访问子:$refs
通过 ref 来标记对应的组件,使用 $refs 可获取到对应 ref 标记的组件信息。
代码展示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>父访问子-$refs</title>
</head>
<body>
<div id="app">
<cpn ref="btn"></cpn>
<button @click="btnClick">按钮</button>
</div>
<template id="cpn">
<div>
<h2>hello, 我是子组件呀!!!</h2>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {},
methods: {
btnClick() {
//获取Vue组件
console.log(this.$refs);
console.log(this.$refs.btn.name);
}
},
components: {
cpn: {
template: `#cpn`,
data() {
return {
name: 'YiBo'
}
},
methods: {
showMessage() {
console.log('good luck for you!')
}
}
}
}
})
</script>
</body>
</html>
显示效果:
子访问父:$parent、 $root
子访问父:$parent、 $root
一般情况下也并不常用,因为用多了子组件和父组件之间的耦合度太高,不利于组件的复用
代码展示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>子访问父 $parent</title>
</head>
<body>
<div id="app">
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>这里是子组件呀!!!</h2>
<button @click="cbtnClick">按钮</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
name: 'YiBo',
message: '今天也有在好好加油呀!!!'
},
components: {
cpn: {
template: `#cpn`,
methods: {
cbtnClick() {
//获取父组件
console.log(this.$parent);
console.log(this.$parent.name);
//获取根组件的message属性
console.log(this.$root.message);
}
}
}
}
})
</script>
</body>
</html>
显示效果: