目录
简介
组件(component)是vue.js最强大的功能之一。组件的作用就是封装可重用的代码,通常一个组件就是一个功能体,便于在多个地方都能够调用这个功能体。 每个组件都是Vue的实例对象。 我们实例化的Vue对象就是一个组件,而且是所有组件的根组件
在工程化工发后,一个组件就是一个 xxx.vue 文件
- 父子组件定义:一个组件中定义了一个局部组件,那么这时这个局部组件就称为这个组件的“子组件”,这个组件对于局部组件来说就称为“父组件”
- 组件分为全局组件和局部组件
- 父子页面之间的data是不共享的
- 在组件中,this代指当前的组件
- 在组件中,data是一个函数,需要有返回值(return)
全局组件
全局组件就是定义了以后,全局都可以使用的组件,就是全局组件
语法
Vue.component( # 定义一个全局组件,但是需要注意,这个组件需要写到根组件上方,写到根组件下方会不生效
'test1', # 定义一个组件名称,调用的时候,只要写到body中的根组件中即可
{ # 除了组件名称以外,其它的内容都写到大括号中
template:`html内容`, # 定义template(固定写法),然后在里面写html内容
data:(){return {xxx:xxx}} # 定义组件中的变量,组件中的变量需要使用return返回
methods:{} # 除了上述部分的以外,其它的定义与根组件是相同的,比如这个methods
}
)
# 注意:多个组件使用多个Vue.component()进行定义,组件之间也可以引用,只要将定义好的组件名称以标签的形式写到template中引用即可,但是不能相互引用,否则会报错。详细可以见下面的示例。
相互引用报错图例:
示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
<test1></test1> <!--只要使用组件定义的标签就可以了-->
<test2></test2> <!--多个组件只要使用标签定义即可,当然也可以调用多次-->
</div>
<script>
// 组件必须写到new Vue({})也就是根组件的上面,写到下面会不生效
Vue.component( // 全局组件
'test1', #, // 定义组件名称,之后在根组件调用的时候直接使用此名称的标签即可
{
// 定义组件的内容,这里面就是html页面内容,使用反引号的话可以换行
template: `
<div>
<span style="font-size: 40px">{{test}}</span>
<!--组件内也可以添加其它组件-->
<test2></test2>
</div>
`,
// 定义组件的变量,组件的变量必须是return出来
data() {
return {
test: '全局组件1测试',
}
},
// 其它的就与根组件定义相同了,比如methods定义方法
methods: {},
})
// 如果要定义多个组件,就写多个Vue.component即可
Vue.component( // 全局组件
'test2',
{
template: `
<div>
<span style="font-size: 40px">{{test}}</span>
</div>
`,
data() {
return {
test: '全局组件2测试',
}
},
methods: {},
})
var vm = new Vue({
el: '#app',
data: {
},
methods: {
btnFunc() {
this.name='Hello World'
}
},
})
</script>
</body>
</html>
局部组件
局部组件就是定义在一个组件内部,只供这个组件使用,其他的组件都不能使用。
语法
# 在一个全局组件或者根组件中使用components进行定义
components:{
'组件名称':{ # 注意这里与全局变量不同,使用的是冒号
template:`html内容`,
data(){
return {变量: '变量内容'}
}
}
}
# 之后,在组件中就可以调用这个局部组件了。
# 如果将局部组件定义到根组件中,那么也可以直接在body下的根标签中调用
# 也可以将局部组件的内容赋值给一个变量,然后在components中使用变量即可,如下:
var xxx = {
template: `
<h1>{{ name }}</h1>
`,
data() {
return {name: '我是app的局部组件,测试foo赋值'}
}
}
components: {xxx} # 这样定义了以后,调用的时候使用 <xxx></xxx> 调用即可
# 写法二
components: {'组件名称': xxx} # 这样定义了以后,调用的时候使用 <组件名称></组件名称> 即可
# 组件名称可以简写:
<组件名称/>
# 如果要定义多个局部组件,只需要在components中增加新的定义即可(不是增加新的components),使用逗号隔开,如下:
components:{
'组件名称1':{ # 注意这里与全局变量不同,使用的是冒号
template:`html内容`,
data(){
return {变量: '变量内容'}
}
}, # 使用逗号隔开
'组件名称2':{ # 注意这里与全局变量不同,使用的是冒号
template:`html内容`,
data(){
return {变量: '变量内容'}
}
}
}
示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
<jsd></jsd>
<foo></foo>
</div>
<script>
// 如果要定义多个组件,就写多个Vue.component即可
Vue.component( // 全局组件
'test2',
{
template: `
<div>
<span style="font-size: 40px">{{ test }}</span>
</div>
`,
data() {
return {
test: '全局组件2测试',
}
},
methods: {},
components: { // 在全局组件中定义局部组件
'jsd': {
template: `
<h1>{{ name }}</h1>
`,
data() {
return {name: '我是app的局部组件'}
}
},
foo // 如果要定义多个局部组件,只需要写在components中即可
},
})
// 也可以将组件的内容赋值给一个变量,之后再在组件中调用这个变量即可
var foo = {
template: `
<h1>{{ name }}</h1>
`,
data() {
return {name: '我是app的局部组件,测试foo赋值'}
}
}
var vm = new Vue({
el: '#app',
data: {},
methods: {},
components: { // 定义局部组件
'jsd': {
template: `
<h1>{{ name }}</h1>
`,
data() {
return {name: '我是app的局部组件'}
}
},
foo // 如果要定义多个局部组件,只需要写在components中即可
},
})
</script>
</body>
</html>
组件间通信
因为组件之间的data是不共享的,有时候一个组件需要把数据传递给另一个组件,有三种情况:
- 父组件传值给子组件:使用自定义属性的方式,或使用ref属性
- 子组件传值给父组件:使用自定义事件的方式,或使用ref属性
- 跨组件传值:使用vuex或cookies/localstorage等都可以实现
父子页面通信之父传子
父传子通信需要使用自定义属性的方式。
什么是自定义属性?
自定义属性就是标签上的 style、class、id 等都属于属性,我们知道,属性是可以自定义的。
语法
# 注意:自定义属性不可以使用驼峰式写法;如果与子组件中变量冲突,子组件中的变量会被替换
# 1. 父组件传子组件
<子标签 :自定义属性名称="父组件的变量名">
# 2. 子接书父组件的值,增加一个键值对
props: ['自定义属性名称']
# 2.1 也可以限制传入的值必须是某一个类型,如Number或String类型,这样当传入的值不是指定的类型后,不会影响页面的显示,但在console中会报错。
props{自定义属性名称:值类型定义}
# 3. 在子组件中的插值语法插入的值应该为自定义属性名称,而不是变量名了
{{自定义属性名称}}
示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
<!--在标签中定义组件,用来将父组件中的值传入-->
<myele :my_publish="name"></myele>
</div>
<script>
var myele = {
template: `
<div>
<p>父组件的变量name:{{my_publish}}</p>
<p>子组件的变量publish:{{publish}}</p>
</div>
`,
data() {
return {
// 子组件中的变量
publish: '任天堂',
}
},
// 子组件接收父组件传入的值
props: ['my_publish']
// 如果要限定传入的值必须为一个属性,可以使用下面的方法
props: {'my_publish': String}
// 如果传入的值不是限定的值,这样会显示到页面,但在console中会报错,可见下图
props: {'my_publish': Number}
}
new Vue({
el: '#app',
data: {
// 父组件中的变量
name: '赛尔达传说',
},
components: {
myele
}
})
</script>
</body>
</html>
- 限定值后的报错
组件通信之子传父
子传父需要使用自定义事件的方法,自定义事件就是定义一个函数,通过函数进行传递
需求:定义一个input输入框,之后在用户输入之后,将输入的值传递给父组件并在浏览器这显示,如下图
语法
# 1. 定义样式
# 2. 在点击按钮时,会触发一个函数
<input type="text" v-model="name"><button @click="clickFunc">点我传递数据</button>
# 3. 函数的内容是触发父组件的一个函数,也就是sendmsg函数
clickFunc(){
# this.$emit的是固定写法,意思是触发父组件自定义事件sendmsg的执行,并且传递一个this.name参数
this.$emit('sendmsg', this.name)
}
# 4. 父组件定义一个sendmsg事件,用于接收子组件传递过来的参数
myevent(item){
this.name=item
}
# 5. 父组件的name就与子组件的name的值一致了
示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
<p>子组件传递过来的数据: {{name}}</p>
<myele @sendmsg="myevent"></myele>
</div>
<script>
var myele={
// 1. 子组件中,使用v-model双向数据绑定,这样输入的值会传递给name参数,之后定义点击事件,触发clickFunc函数
template:`
<div>
<input type="text" v-model="name"><button @click="clickFunc">点我传递数据</button>
</div>
`,
data(){
return{
name:''
}
},
methods:{
// 2. 因为第一步中触发函数,所以要定义一个函数供botton按钮触发
clickFunc(){
// 3. 函数内容是触发发父组件的函数,并将子组件的变量当作值传递给父组件
this.$emit('sendmsg', this.name)
}
}
}
new Vue({
el:'#app',
data:{
name: '',
},
methods:{
// 4. 因为子组件触发了父组件的函数并传入了值,所以父组件需要定义一个函数供它触发,并且需要定义参数用于接收子组件传入进的值
myevent(item){
// 5. 让自己的变量等于传入的值,这样就完成了子传父了整个过程
this.name=item
}
},
components:{
myele
}
})
</script>
</body>
</html>
使用ref进行父子组件通信
vue提供了一个ref方法,可以更方便简单的进行父子组件通信
使用ref不需要关注是父传子还是子传父
方法
# 1. ref可以放到普通标签中
<div ref="mydiv">{{name}}</div>
# 2. ref也可以放到组件中
<myele ref="myele"></myele>
template: `<div><div ref="mydiv">我是局部组件的div--->{{name}}</div></div>`
# 3. 在父或子组件中进行赋值
this.name=this.$refs.myele.name
示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
<!--在标签中可以定义ref-->
<div ref="mydiv">{{name}}</div>
<button @click="btnFunc">点我</button>
<!--在组件中也可以定义ref-->
<myele ref="myele"></myele>
</div>
<script>
myele = {
template: `<div><div ref="mydiv">我是局部组件的div--->{{name}}</div></div>`,
data(){return{name: '局部组件的标签'}}},
new Vue({
el: '#app',
data: {name: '我是全局组件的标签'},
methods: {
btnFunc() {
console.log(this.$refs)
// 可以使用this.$refs找到在body体中定义的ref标签,根据标签就可以找到标签对应的值,之后就可以根据这个进行赋值,就完成了父子组件通信
this.name=this.$refs.myele.name
// 下面这个就是父传子,上面的就是子传父
this.$refs.myele.name=this.name
},
},
components: {
myele
}
})
</script>
</body>
</html>
动态组件(component)
实现点击一个标签,则显示对应的内容功能
- 如下图所示,点击sheet1则显示sheet1中的内容,点击sheet2则显示sheet2中的功能
普通方法实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
<!--增加点击事件,如果点击相应的页面,则将值传入函数,再由函数修改show的值,用于下面的判断-->
<span style="font-size: 40px" @click="btnFunc('sheet1')">sheet1 ||</span>
<span style="font-size: 40px" @click="btnFunc('sheet2')">sheet2 ||</span>
<span style="font-size: 40px" @click="btnFunc('sheet3')">sheet3</span>
<!--设定条件,如果条件成立,则显示,如果不成立则不显示-->
<sheet1 v-if="show=='sheet1'"></sheet1>
<sheet2 v-else-if="show=='sheet2'"></sheet2>
<sheet3 v-else-if="show=='sheet3'"></sheet3>
</div>
<script>
var sheet1 = {template: `<div>我是sheet1页面,第一个页面</div>`}
var sheet2 = {template: `<div>我是sheet2页面,第二个页面</div>`}
var sheet3 = {template: `<div>我是sheet3页面,第三个页面</div>`}
new Vue({
el: '#app',
data: {
show: 'sheet1'
},
// 设定好函数,用于接收页面返回的参数,根据参数修改show的值
methods: {
btnFunc(item) {
this.show = item
}
},
components: {
sheet1, sheet2, sheet3
}
})
</script>
</body>
</html>
使用组件component实现
使用component的话,就可以替换大量的if-else循环了,如果页面比较多的话,会节省大量的代码。
- 如下代码所示,只修改了if-else循环部分,其他部分不需要修改
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
<!--增加点击事件,如果点击相应的页面,则将值传入函数,再由函数修改show的值,用于下面的判断-->
<span style="font-size: 40px" @click="btnFunc('sheet1')">sheet1 ||</span>
<span style="font-size: 40px" @click="btnFunc('sheet2')">sheet2 ||</span>
<span style="font-size: 40px" @click="btnFunc('sheet3')">sheet3</span>
<!--使用component组件,使用is绑定一个变量,is后面的值是什么,就会显示哪个组件,与if-else相比,更加的简洁,也更好写一些-->
<component :is="show"></component>
</div>
<script>
var sheet1 = {template: `<div>我是sheet1页面,第一个页面</div>`}
var sheet2 = {template: `<div>我是sheet2页面,第二个页面</div>`}
var sheet3 = {template: `<div>我是sheet3页面,第三个页面</div>`}
new Vue({
el: '#app',
data: {
show: 'sheet1'
},
// 设定好函数,用于接收页面返回的参数,根据参数修改show的值
methods: {
btnFunc(item) {
this.show = item
}
},
components: {
sheet1, sheet2, sheet3
}
})
</script>
</body>
</html>
动态组件相关keep-alive
动态组件就是当你跳转到一个标签里面后,之前的标签就会被删除掉。有这么一个需求,就是如果我的页面中有一个input输入框,我输入了一些内容后,切换了标签,之后再切换回input输入框的标签后,需要输入的内容还在。这就需要keep alive了
- 其它代码与上一章节相同,只是增加了keep-alive标签
<keep-alive>
<component :is="show"></component>
</keep-alive>
组件之插槽(slot)
一般情况下,编写完一个组件之后,组件的内容都是写死的,如果需要修改页面的时候,就需要去组件中修改,这样扩展性比较差。为了解决这个问题,就出现了插槽这个概念。只需要在组件中添加
即可
匿名插槽
<!--先设定好组件,里面定义一个slot标签-->
var sheet1 = {template: `
<div>
<h1>我是标题</h1>
<slot></slot>
<h1>我是结尾</h1>
</div>
`}
<!--引用组件-->
<!--引用时只需要将内容写到引用的组件中即可-->
<sheet1>
我是插槽的内容!!!
</sheet1>
- 但是匿名插槽有一个问题,如果定义了多个slot标签,则会导致插入的内容会重复显示在页面中
var sheet1 = {template: `
<div>
<h1>我是标题</h1>
<slot></slot>
<h1>我是结尾</h1>
<slot></slot>
</div>
`}
具名插槽
为了解决上面的问题,就引出了具名插槽,指定插入到哪个slot中
<!--定义组件,并在组件中定义name属性,指定name属性的名字-->
var sheet1 = {template: `
<div>
<h1>我是标题</h1>
<slot name="s1"></slot>
<h1>我是结尾</h1>
<slot name="s2"></slot>
</div>
`}
<!--在引用的时候,定义一个slot属性,并在slot属性中指定要引用哪个slot标签-->
<sheet1>
<div slot="s2">我是2号插槽的内容</div>
<div slot="s1">我是1号插槽的内容</div>
</sheet1>
- 效果如下: