Vue学习笔记——基础语法之组件(Component)
目标:
- 能够知道组件化开发思想
- 能够知道组件的注册方式
- 能够说出组件间的数据交互方式 ▲
- 能够说出组件插槽的用法
- 能够说出Vue调试工具的用法
- 能够基于组件的方式实现业务功能
1. 组件化开发思想——自定义标签
- Web Components 通过创建 封装好功能的定制元素(自定义标签)解决问题
- 组件可以扩展 HTML 元素,封装可重用的代码
2. 组件注册
2.1 全局注册
语法:
Vue.component( ‘组件名称’,{
data:组件数据,——是一个函数 function(){ return { } }
template:组件模板内容——是字符串,字符串当中可以写vue所支持的模板语法
})
例子:
<body>
<div id="app">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
//注册组件
Vue.component('button-counter',{
data: function(){
return {
count:0
}
},
//template:'<button @click="count++">点击了{{count}}次</button>', 这种也可以
template:'<button @click="handle">点击了{{count}}次</button>',
//这里也是可以有methods的
methods:{
handle:function(){
this.count+=2;
}
}
})
var vm = new Vue({
el:'#app',
data:{
},
})
</script>
2.2 组件注册注意事项
- data必须是一个函数, 同时这个函数要求返回一个对象
- 组件模板内容必须是单个根元素(很多内容的时候你一开始先用个div包一下就好了其实)
template:’<button @click=“handle”>点击了{{count}}次< /button>< button>测试< /button> ’
这样会报错,它们两个是兄弟关系,得在外层加个div才不会报错
- 组件模板内容可以是模板字符串的形式(可读性)
- 命名格式
如果使用驼峰式命名组件,那么使用组件的时候,只能在字符串模板中用驼峰的方式使用组件,在普通的标签模板中,必须使用短横线的方式使用组件(所有单词都小写,每个单词之间用短横线连接)
2.3 局部组件注册
- 只能在当前注册它的vue实例中使用
- 组件的内容可以抽取到一个对象当中
例子:
<body>
<div id="app">
<component-tom></component-tom>
<component-a></component-a>
<component-lili></component-lili>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
//组件的内容可以抽取到一个对象当中
var componentA = {
data:function(){
return {
msg:'HelloWorld'
}
},
template:'<div>{{msg}}</div>',
}
var componenttom = {
data:function(){
return {
msg:'i am tom'
}
},
template:'<div>{{msg}}</div>',
}
var vm = new Vue({
el:'#app',
data:{
},
components:{
'component-a':componentA, //组件的内容可以抽取到一个对象当中
'component-tom':componenttom,
}
})
</script>
组件名还是有加 ’ ’ 的
3. 组件间数据交互
3.1 父组件向子组件传值
在父组件中,直接通过属性的静态方式或动态绑定的属性值的方式传递给子组件,然后子组件通过props接收属性名来接收
- 父组件通过属性将值传递给子组件
< component-test title=‘来自父组件的值’>< /component-test>
< component-test :title=‘trans’ multi=‘nihao’>< /component-test>
- 子组件内部通过props接收传递过来的值
Vue.component(‘component-test’,{
props:[‘title’,‘multi’],
data:function(){
return { zmsg:‘子组件本身的数据’ }
},
template:’< div>{{zmsg + “----------”+ title + “--------”+multi}}< /div>’
})
props属性命名规则:
- 在props中使用驼峰形式,普通模板中需要使用短横线的形式,而在字符串形式的模板中没有这个限制
3.2 子组件向父组件传值 ▲只能多悟几遍
子组件触发一个自定义事件,父组件去监听这个自定义事件
- 子组件通过自定义事件向父组件传递信息
$emit(‘自定义事件的名称’,需要传递的数据)
触发事件 - 父组件用v-on 监听子组件的事件,接收子组件传递过来的内容($event)
<cart-list :list='list' @cart-del='delCart($event)'></cart-list>
看回放叭
3.3 兄弟之间的传递
- 需要借助事件中心管理组件间的通信
var hub = new Vue( );
- 监听事件与销毁事件
hub.$on(‘监听的事件名称’,()=>{});
hub.$off(‘监听的事件名称’);
- 触发事件
hub.$emit( '方法名‘,传递的数据)
- 组件内部什么时候去监听事件呢?
mounted钩子函数 - 什么时候触发?
点击按钮的时候触发对应的事件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="app">
<div>{{fmsg}}</div>
<test-tom></test-tom>
<test-jerry></test-jerry>
<button @click='handle'>销毁事件</button>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
//事件中心
var hub = new Vue();
Vue.component('test-tom',{
data:function(){
return {
num:0
}
},
template:`
<div>
<div>TOM:{{num}}</div>
<div>
<button @click='handle'>点击</button>
</div>
</div>
`,
methods:{
handle:function(){
// 触发兄弟组件的事件
hub.$emit('jerry-event',2);
}
},
mounted:function(){
hub.$on('tom-event',(val)=>{
this.num+=val;
})
}
});
Vue.component('test-jerry',{
data:function(){
return {
num:0
}
},
template:`
<div>
<div>JERRY:{{num}}</div>
<div>
<button @click='handle'>点击</button>
</div>
</div>
`,
methods:{
handle:function(){
// 触发兄弟组件的事件
hub.$emit('tom-event',1)
}
},
mounted:function(){
hub.$on('jerry-event',(val)=>{
this.num += val;
})
}
});
var vm = new Vue({
el:'#app',
data:{
fmsg:'父组件',
},
methods:{
handle:function(){
hub.$off('tom-event');
hub.$off('jerry-event');
}
}
})
</script>
</body>
</html>
4. 组件插槽
- 组件的最大特性就是复用性,而用好插槽能大大提高组件的可复用能力
- 插槽内可以包含任何模板代码,包括HTML
- 组件标签中的内容会传递给slot
(如果没有slot,在组件标签中写了内容也不会显示)
4.1 匿名插槽
<div id="app">
<alert-box>你好哇</alert-box>
<alert-box></alert-box>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
Vue.component('alert-box',{
// ① 当组件渲染的时候,这个slot元素将会被替换为组件标签中嵌套的内容
// ② 插槽内可以包含任何模板代码,包括 HTML
template:`
<div>
<strong>ERROR:</strong>
<slot>默认内容</slot>
</div>
`,
})
var vm = new Vue({
el:'#app',
data:{
}
})
</script>
4.2 具名插槽
- 具名插槽的渲染顺序,完全取决于模板,而不是取决于父组件中元素的顺序。
(下面代码就不是按顺序写的,但是渲染的时候是按模板顺序渲染)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="app">
<alert-box>
<div>主要内容1</div>
<div>主要内容2</div>
<h1 slot="header">标题内容</h1>
<p slot="footer">底部内容</p>
</alert-box>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
Vue.component('alert-box',{
template:`
<div>
<header>
<slot name='header'></slot>
</header>
<main>
<slot>
</main>
<footer>
<slot name='footer'></slot>
</footer>
</div>
`,
})
var vm = new Vue({
el:'#app',
data:{
}
})
</script>
</body>
</html>
4.3 作用域插槽
- 父组件对子组件加工处理
(从父组件中获取到一些子组件的数据,获取数据的方式就是通过slot-scope=‘slotProps’
来得到,它当中得到的值就是在子组件slot属性中所绑定的,它对应的值会被父组件获取到)
例子:
父组件动态控制哪一种水果高亮
(因为子组件一旦封装好后,后期就很少在动了,所以最好有一种办法,从父组件可以直接去决定,这就是作用域插槽所解决的问题)
这个列表数据是通过动态遍历的方式生成的
所以我们需要提供对应数据。
对于数据来说,我们一般是通过父组件传递过来的,所以 ①在父组件中要提供一份水果列表的数据。②父组件通过属性绑定的方式传递给子组件 ③子组件通过props接收,然后遍历
<div id="app">
<fruit-list :list='list'>
<!-- 通过插槽传递过来的属性 子组件通过属性名传递过来数据-->
<template slot-scope='slotProps'>
<strong v-if='slotProps.info.id==3' class="current">{{slotProps.info.name}}</strong>
<span v-else>{{slotProps.info.name}}</span>
</template>
</fruit-list>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
Vue.component('fruit-list',{
props:['list'],
template:`
<div>
<li :key='item.id' v-for='item in list'>
<slot :info='item'>
{{item.name}}
</slot>
</li>
</div>
`,
})
var vm = new Vue({
el:'#app',
data:{
list:[{
id:1,
name:'pear',
},{
id:2,
name:'orange',
},{
id:3,
name:'strawberry'
}]
}
})
</script>
首先,在子组件的模板当中要提供一个插槽,slot中间填充我们要显示的数据,并且slot标签当中要绑定一个属性,(属性名字自定义,用来提供给父组件。属性值是你要操作的数据)
然后,从父组件的角度:要填充slot。
填充规则:在组件标签的中间提供内容,这个内容用< template>标签进行包裹,并且通过template的一个属性:slot-scope来得到子组件中传递过来的数据,数据得到后就可以进行一些控制了