一、组件注册
1、全局组件注册
语法:Vue.component('组件名字',{对象形式表示的组件内容})
实例:
<body>
<div id="app">
<son></son>
</div>
</body>
<script>
Vue.component('son', {
data: function () {
return {
msg: '我是son组件里的内容'
}
},
template: "<h1>{{msg}}<h1>"
});
var vm = new Vue({
el: "#app",
data: {}
})
</script>
2、组件怎么使用
直接用组件名字的对应的自定义标签写在vm实例管辖的范围里即可。
<div id="app">
<son></son>
</div>
3、组件注册的注意事项【重要】
00.vue中的vm实例也是一个组件;
定义的组件和vm实例的语法区别(01,02):
01.组件的语法中和data平级的没有el,而是template用来定义模板,其余的methods等等和vm一样
02.vm实例里的data是一个对象形式,组件的data是一个函数,通过在函数里return一个个对象(这也是组件具有复用性的原因)
03.组件的template必须只有一个根标签,如果有多个根元素会直接报红(原因:template相当于vm实例(根组件)里的el,el只能控制一个大标签,内容都放在这个大盒子内部,组件的template也是同理)。
04.template支持普通字符串拼接(注意单双引号嵌套,且不能换行),支持es6模板字符串,支持先在html里用<template></template>标签拼接好,起一个id名,再直接通过调用id名字拿过来,如下:
<!-- 在这里定义子组件模板 -->
<template id="tpl">
<button @click="count++">点击了{{count}}次</button>
</template>
</body>
<script>
Vue.component('button-counter', {
data: function () {
return {
count: 0,
}
},
//在这里直接拿到tpl模板
template: '#tpl',
});
建议用以上方法,在<template></template>标签拼接好在拿过来用,较为方便且不易出错。
05.组件注册也可以将组件对象单独拎出来用变量接收,再定义组件;如下:
var buttcount={
data: function () {
return {
count: 0,
}
},
template:'#tpl',
}
Vue.component('button-counter', buttcount);
06.关于组件里的methods属性:
组件里的methods属性,和vm实例里的一样,可以定义一系列方法,来操作组件的data里的数据。需要加this.才能访问到data上的数据。当然,简单的操作也可以直接把事件执行函数写在template的行内
07.组件名称涉及多单词,建议使用全小写加短横线连接的方式(因为html标签名不可以出现大写字母),但是在用变量接收定义的组件内容(组件对象)的时候,可以用驼峰法
4、局部组件
局部组件定义在vm实例里,和el,data,methods平级的位置:用components:来定义;
<body>
<div id="app">
<hellow-jquery></hellow-jquery>
<hellow-vue></hellow-vue>
</div>
</body>
<script>
var hellojquery = {
data: function () {
return {
msg: 'hello-jquery!'
}
},
template: '<h1>{{msg}}</h1>'
};
var hellovue = {
data: function () {
return {
msg: 'hello-vue!'
}
},
template: '<h1>{{msg}}</h1>'
};
var vm = new Vue({
el: '#app',
data: {},
methods: {},
components: {//这里要用component要加s
'hellow-jquery': hellojquery,
'hellow-vue': hellovue,
},
});
</script>
注意:局部组件只能在定义它的父组件vm里访问,在其他地方是使用不了的
二、组件传值
1.父组件向子组件传值
父组件向子组件传值的核心:在子组件中用【props属性】来接收
思路: 01.在父组件区域里的子组件标签上用自定义属性得到父组件里的数据
02.在子组件的组件对象中定义一个props属性,用数组的形式来接收父组件传过来的数据,数组里的元素,拼接到子组件的模板中即可在子组件中展示从父组件中传递过来的数据。
代码实现:
<body>
<!---->
<div id="app">
<div>{{fmsg}}</div>
<son :abc='fmsg'></son>
</div>
<!-- 子组件模板 -->
<template id="tpl">
<div>{{smsg+"------------"+abc}}</div>
</template>
</body>
<script>
//定义子组件对象
var son = {
props: ['abc'],
data: function () {
return {
smsg: '我是子组件上的内容'
}
},
template: '#tpl'
}
//注册子组件
Vue.component("son", son)
var vm = new Vue({
el: '#app',
data: {
fmsg: '我是父组件的内容'
},
methods: {
}
});
</script>
2.子组件向父组件传值
子组件向父组件传值的核心:要借助【自定义事件】的方式来触发:
01.在父组件中给子组件的自定义标签绑定自定义事件,事件名自定义
02.在子组件中想办法去触发绑定在自己身上的自定义事件 (this.$emit(自定义事件名字,子组件需要传递的参数))
03.0:但是子组件如何 或者在哪里写 this.$emit(自定义事件名字) 这个代码 ????
03.1:答:怎么让这个$emit触发自定义事件呢,一般可以在子组件中创建一个固有事件比如点击等等(当然也可以是其他办法),把$emit写在这个点击事件的事件执行函数中,就可以通过$emit执行这个自定义事件了。
04.父组件模板区域之内,子组件的自定义标签上绑定的自定义事件添加了一个事件执行函数
05.在父组件的 methods里执行这个事件执行函数,定义一个形参,接收子组件传递过来的实参。再this.fmsg = val,把子组件的值赋值给父组件的内容
代码如下:
<body>
<div id="app">
<h1>我是父组件----------{{fmsg}}</h1>
<!-- 01.2.在父组件标签区域中,对子组件的自定义标签,用v-on绑定自定义事件my-event,这个事件用来监听子组件的自定义事件 ,同时也接收到了子组件在事件中携带的子组件的参数-->
<son @my-event="handle"></son>
</div>
</body>
<script>
// 子组件对象
var son = {
data() {
return {
smsg: '子组件上的内容'
}
},
template: ` <div>
<h1>{{smsg}}</h1>
<!-- 01.1:因为在子组件中的方法clickhandle不会自己执行,所以我们需要借助固有事件(如点击等)来帮助触发自定义事件-->
<button @click="clickhandle">传递数据给父组件</button>
</div>`,
methods: {
clickhandle() {
//01.2.在子组件里用.$emit用来触发自定义事件,同时传递子组件的参数到父组件
this.$emit('my-event', this.smsg)
}
}
};
//注册子组件
Vue.component('son', son);
var vm = new Vue({
el: '#app',
data: {
fmsg: ''
},
//02.父组件的methods中,子组件的自定义事件触发完,把子组件的值通过赋值传递给父组件
methods: {
handle(val) {
console.log(val);
//val形参接收到子组件传递过来的值(实参smsg)
this.fmsg = val
}
},
});
</script>
3.兄弟组件的传值
兄弟传值的核心原理是通过new一个Vue对象,作为【事件中心】,来实现的:
思路:01.通过new一个Vue对象,作为【事件中心】。
02. 在A组件中,利用事件中心来调用$emit方法来触发B组件的自定义事件,同时携带自己的参数
03. 在B组件中,利用事件中心来调用$emit方法来触发A组件自定义事件,同时携带自己的参数
04. 在A组件中生命周期的钩子函数mounted中做事件监听,在里面用事件中心的$on来绑定自己的自定义事件,用形参接收B组件传过来的值
05.在B组件中也是如此,接收A组件传过来的值
<body>
<div id="app">
{{msg}}
<son1></son1>
<son2></son2>
</div>
</body>
<script>
//定义一个事件中心(new一个Vue实例), 用它来调用¥emit来触发自定义事件
var eventBus = new Vue()
//子组件son1
Vue.component('son1', {
data: function () {
return {
msg_son1: '兄组件的内容',
msg_son2: '',//用来存从兄弟组件里传过来的内容
}
},
template: `
<div>
<div>{{msg_son1+'-------'+msg_son2}}</div>
<div>
<button @click='clickHandle'>点击</button>
</div>
</div>
`,
methods: {
clickHandle: function () {
//通过事件中心调用$emit来触发兄弟组件son2的自定义事件,把自己的值msg_son1做参数携带
eventBus.$emit('son2-event', this.msg_son1)
}
},
//在生命周期里的mounted函数里做事件监听
mounted: function () {
//用事件中心来添加事件监听
eventBus.$on('son1-event', (val) => {
this.msg_son2 = val//val形参接收兄弟组件son2传递过来的值msg_son2,赋给自己的模板里写的空的msg_son2
})
}
});
//子组件son2
Vue.component('son2', {
data: function () {
return {
msg_son2: '弟组件的内容',
msg_son1: ''
}
},
template: `
<div>
<div>{{msg_son2+'-------'+msg_son1}}</div>
<button @click='clickHandle'>点击</button>
</div>
`,
methods: {
clickHandle: function () {
//通过事件中心调用$mit触发兄弟son1组件的自定义事件,把自己的值msg_son2做参数携带
eventBus.$emit('son1-event', this.msg_son2)
}
},
//在生命周期mounted函数里做事件监听
mounted: function () {
//用事件中心来添加事件监听
eventBus.$on('son2-event', (val) => {
this.msg_son1 = val//val形参接收兄弟组件son1传递过来的值msg_son1,赋给自己模板里的空的msg_son1
})
}
});
var vm = new Vue({
el: '#app',
data: {
msg: '父组件vm'
},
methods: {},
});
</script>
组件传值的总结
总结:
01:父组件向子组件传值,通过【props属性】完成
02:子组件向父组件传值,通过借助【自定义事件】按完成
03:兄弟组件传值,通过【事件中心】实现,在一个组件中,通过事件中心调用$emit方法来触发兄弟的自定义事件,传入自己的值;调用$on方法来绑定自己的自定义事件,用形参接收兄弟组件传过来的值
4.单向数据流
父组件传值给子组件,props属性的原则是单项数据流——只允许父组件向子组件传递数据,不允许子组件操作props里面的数据。
但是复杂数据类型,修改其中的某一个属性或者某一个元素,是没有修改这个数据的内存地址的,所以不会报错,如果直接赋值,则还是会报错——但是也不建议这么操作
三、组件插槽
1.普通插槽
插槽就是组件的template模板中预留一个slot标签,组件的调用者在组件标签中间填入内容,就会展示在页面上。如果组件标签中,没有写任何内容,但是模板中的slot中写了一些默认内容,这个默认内容也会展示。组件标签中间填写了内容,默认内容就不会展示。
<body>
<div id="app">
<alert-box>有bug发生</alert-box>
<alert-box>有一个警告</alert-box>
<alert-box></alert-box> //组件标签中间没有写内容, template里的slot标签里写的默认内容就展示
</div>
<script>
//组件插槽:父组件向子组件传递内容
Vue.component('alert-box', {
template: `
<div>
<strong>ERROR:</strong>
<slot>默认内容</slot>
</div>
`
});
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
插槽的用处:
插槽是在组建中使用的; 组件的最大特性就是复用性,而用好插槽能大大提高组件的可复用能力
2.具名插槽
具名插槽就是有名字的插槽,在组件的template中,给slot标签添加一个name属性绑定一个名字,
然后再组件的自定义标签中写上标签,添加slot属性,属性值为template中slot标签添加的属性值,
这样这个标签就会替换template中插槽定义的位置。
<body>
<div id="app">
<son>我写在了组件标签中间
<div slot="abc">我是插槽内容1</div>
<div slot="def">我是插槽内容2</div>
</son>
</div>
</template>
</body>
<script>
Vue.component('son', {
data: function () {
return {
smsg: '我是一个组件的内容'
}
},
template: `
<div>
<div>
<slot name="abc"></slot> // <div>我是插槽内容1</div>
</div>
<span>
<slot name="def"></slot> // <div>我是插槽内容2</div>
</span>
</div>
`
})
var vm = new Vue({
el: '#app',
data: {},
methods: {}
});
</script>
在组件的自定义标签里,借助template标签一下填充多个文本或多个标签到插槽中:
给template标签添加slot属性,定义一个属性值:
<template slot='header'>
<p>标题信息1</p>
<p>标题信息2</p>
</template>
这样一下就可以添加多个标签到插槽里了,而且template标签是不会在结构中展示的
<body>
<div id="app">
<base-layout>
<template slot='header'><!-- 一下包含两个或者多个标签 -->
<p>标题信息1</p>
<p>标题信息2</p>
</template>
<p>主要内容1</p>
<p>主要内容2</p>
<template slot='footer'>
<p>底部信息信息1</p>
<p>底部信息信息2</p>
</template>
</base-layout>
</div>
<script>
/*
具名插槽
*/
Vue.component('base-layout', {
template: `
<div>
<header>
<slot name='header'></slot> // 替换成template标签里面的那两个或者多个标签,且template标签不会显示
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name='footer'></slot>
</footer>
</div>
`
});
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
3.作用域插槽
作用域插槽 的核心就是可以把组件中的数据暴露给父组件进行加工使用
<body>
<div id="app">
<son>
<!-- 作用域插槽 可以把组件中的数据暴露给父组件进行加工使用 -->
<!-- 这里是填充插槽的内容 -->
<!-- 需求 我想在这里展示 son 组件中的title的值 -->
<template slot-scope="row">
<a href="#">{{ row.info }}</a>
</template>
</son>
</div>
<script src="./js/vue.js"></script>
<script>
var son = {
data() {
return {
son_title: '子组件的标题'
}
},
template: `
<div>
<h1>son组件</h1>
<slot :info="son_title"></slot>
</div>`
}
Vue.component('son',son)
var vm = new Vue({
el: "#app",
data: {
},
methods: {
},
})
</script>