组件化开发思想
组件化规范:Web Components
问题:
- 尽可能多的重用代码
- 自定义组件的方式不太容易(html、css和js)
- 多次使用组件可能导致冲突
Web Components通过创建封装好功能的定制元素(自定义标签)解决上述问题
Vue 实现了部分上述规范
组件注册
Vue.component(组件名称,{
data:组件数据,
template:组件模板内容
})
例子:计算按钮点击次数
Vue.component('button-counter',{
data:function(){
return{
count:0
}
},
template:'<button @click="count++">点击了{{count}}次</button>'
});
//另一种写法:定义方法使用
Vue.component('button-counter',{
data:function(){
return{
count:0
}
},
template:'<button @click="handle">点击了{{count}}次</button>',
methods:{
handle:function(){
this.count+=1;
}
}
});
使用:
<div id="app">
<!-- 可多次使用,组件数据相互独立 -->
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
组件注册注意事项
- data必须是一个函数
分析函数与普通对象的对比 - 组件模板内容必须是单个根元素
// 报错
template:'<button @click="handle">点击了{{count}}次</button><button>测试</button>'
//这样可以
template:'<div><button @click="handle">点击了{{count}}次</button><button>测试</button></div>'
- 组件模板内容可以是模板字符串
模板字符串需要浏览器提供支持(ES6语法)
Vue.component('hello-world',{
template:`
<div class="hello-world-box">
<span>hello</span>
</div>
`
});
- 组件命名方式
短横线方法:
Vue.component(‘my-component’,…)
驼峰方式:
Vue.component(‘MyComponent’,…)
驼峰命名不能直接在html中使用,只能加入到组件命名的template中使用
局部组件注册
不能在全局组件中使用,只能在注册它的父组件中使用
var ComponentA={
data:function(){
return(
msg:'helloworld'
)
},
template:'<div>{{msg}}</div>'
};
var ComponentB={/*...*/};
var ComponentC={/*...*/};
var vm = new Vue({
el:'#app',
data:{
},
components:{
'component-a':ComponentA,
'component-b':ComponentB,
'component-c':ComponentC
}
});
组件间数据交换
父组件向子组件传值
- 组件内部通过props接收传递过来的参数
//定义子组件
Vue.component('menu-item',{
props:['title'],
template:'<div>{{title}}</div>'
});
//vm本身是一个组件,是根组件
var vm = new Vue({
el:'#app',
data:{
title:'动态绑定属性'
}
});
- 父组件通过属性将值传递给子组件
<menu-item title="来自父组件的数据"></menu-item>
<menu-item :title="title"></menu-item>
- props属性名规则
- 在props中使用驼峰形式,dom模板中需要使用短横线的形式(原因:dom属性不存在大小写)
- 字符串形式的模板中没有限制
//定义子组件
Vue.component('menu-item',{
//驼峰形式
props:['menuTile'],
//字符串形式模板
template:'<div>{{menuTile}}</div>'
});
<!-- dom中使用时需要短横线形式 -->
<menu-item menu-title="来自父组件的数据"></menu-item>
- props属性值类型
- 字符串String
- 数值 Number
<!-- typeof num:string -->
<menu-item num='12'></menu-item>
<!-- typeof num:number -->
<menu-item :num='12'></menu-item>
- 布尔值 Boolean(同上,不加冒号为string类型,加冒号为boolean类型)
- 数组 Array
- 对象 Object
- props传递数据原则:单向数据流(只允许父组件传递数据给子组件)
子组件向父组件传值
- 子组件通过自定义事件向父组件传递信息
<!-- $emit是固定的 -->
<button v-on:click='$emit("自定义事件名")'>测试</button>
- 父组件监听子组件的事件
<menu-item v-on:自定义事件名='触发事件'>测试</menu-item>
- 子组件通过自定义事件向父组件传递值
<button v-on:click='$emit("enlarge",0.1)'>测试</button>
- 父组件监听子组件的事件
<!-- 固定$event接收0.1 -->
<menu-item v-on:enlarge='fontSize+= $event'>测试</menu-item>
非父子(兄弟)组件间传值
- 单独的事件中心管理组件间的通信
组件A<–监听/触发–>事件中心<–触发/监听–>组件B
var eventHub = new Vue();
- 监听事件与销毁事件
eventHub.$on('自定义函数名称',自定义函数);
eventHub.$off('自定义函数名称');
- 触发事件
eventHub.$emit('自定义函数名称',参数);
组件插槽
作用:
- 父组件向子组件传递内容(模板内容)
组件插槽基本用法
- 插槽位置
Vue.component('alert-box',{
template:`
<div class="demo-alert-box">
<strong>Error!</strong>
// 插槽
<slot></slot>
</div>
`
});
- 插槽内容
<alert-box>Something bad happend.</alert-box>
运行结果(页面显示为):Error! Something bad happend.
注意:插槽内容为空,则默认显示slot的内容,否则slot内容不显示
具名插槽用法(有名字的插槽)
- 插槽定义
Vue.component('alert-box',{
template:`
<div class="container">
<header>
<slot name='header'></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<solt name='footer'></solt>
</footer>
</div>
`
});
- 插槽内容
<!-- 填充到名字匹配的插槽中,没有名字则匹配到默认的无名插槽中 -->
<base-layout>
<h1 slot="header">标题内容</h1>
<p>主要内容1</p>
<p>主要内容2</p>
<p slot="footer">底部内容</p>
</base-layout>
可以使用标签 填充多条内容
标签:临时性包裹内容,不会渲染到页面
<base-layout>
<template slot="header">
<p>标题信息1</p>
<p>标题信息2</p>
</template>
<p>主要内容1</p>
<p>主要内容2</p>
<p slot="footer">底部内容</p>
</base-layout>
作用域插槽
- 应用场景:父组件对子组件内容进行加工处理
- 插槽定义
Vue.component('alert-box',{
props:['list'],
template:`
<ul>
<li v-for="item in list" v-bind:key=item.id>
<slot v-bind:绑定数据自定义名="item">
{{itm.name}}
</slot>
</li>
</ul>
`
});
- 插槽内容
<fruit-list v-bind:list="list">
<!-- slot-scope获取数据 值是在子属性slot中绑定的-->
<template slot-scope="自定义名">
<!-- 条件判断进行加工 -->
<strong v-if='自定义名.绑定数据自定义名.id==2' class='current'>
{{slotProps.info.name}}
</strong>
</template>
</fruit-list>