组件化开发
标准、分治、重用、组合
1.组件注册
命名:驼峰/短横线,驼峰命名的只能在字符串模板中使用驼峰式的组件,在普通的标签模板中需要转化为短横线使用。
①全局组件注册
Vue.component('组件名称',{
data:对象,组件数据[函数,闭包环境],
template:组件模板内容[只包含一个根元素;可以是`模板字符串`]
})
Vue.component('button-counter',{
data:function(){
return{
count:0
}
},
template:'<button @click="handle">点击{{count}}次</button>',
methods:{
handle:function(){
this.count++;
}
}
})
<button-counter></button-counter>
②局部组件注册
局部组件只能在注册他的父组件中使用
var ButtonCounter = {
data:function(){
return{
count:0
}
},
template:'<button @click="handle">点击{{count}}次</button>',
methods:{
handle:function(){
this.count++;
}
}
})
components:{
'button-counter':ButtonCounter
}
2.Vue调试工具vue-devtools
3.组件间数据交互
父--->子 方式 父组件 子组件 其他 ① 属性props,组件内部通过props接收传递过来的值 <menu-item :title='ptitle' content='hello'></menu-item> props:['title','content'] ② 引用refs(dom操作) <hello-word ref="hw"></hello-word>
this.$refs.hw.foo ='bar'// data
foo:'foo'③ 子元素children this.$children[0].foo = 'dong' 子元素不保证顺序,也不是响应式。数组只读。
子--->父 方式 父组件 子组件 其他 $emit(自定义事件,携带参数) <menu-item title='来自父组件的值' @enlarge-text='handle($event)'></menu-item> <div @click='$emit("enlarge-text",5)'>扩大字体大小</div> 观察者模式
非父子组件 1 任意两个组件 事件中心(事件总线)或vuex 2 兄弟组件 通过 $parent
共同的父组件进行传递
祖先--->后代 祖先 后代 provide/inject
// 和data同级
provide:{
foo:'foo'
}// 和data同级
inject:['foo']用于高阶插件/组件库开发,不推荐直接用于应用程序代码。
created->mounted:父组件先于子组件加载。
①父组件向子组件传值
1️⃣属性props
- 组件内部通过props接收传递过来的值
props:使用驼峰形式,模板中使用短横线形式;字符串形式的模板中没有限制。
props:['menuTitle']-------------menu-title
- props属性
- 字符串string
:pstr
- 数值number:
:pnum
数值,pnum
字符串 - 布尔boolean:
:pboo
布尔,pboo
字符串 - 数组array
:parr
- 对象object
:pobject
- 字符串string
// 子组件
Vue.component('menu-item',{
props:['title','content'],
data:function(){
return{
msg:'子组件数据'
}
},
template:'<div>{{msg + "---" + title + content}}</div>',
})
- 父组件通过属性将值传递给子组件
<!-- 父组件 -->
<menu-item title='来自父组件的值'></menu-item>
<menu-item :title='ptitle' content='hello'></menu-item>
data:{
ptitle:'动态绑定属性'
}
2️⃣引用refs
应用场景:dom操作
- 父组件
<hello-word ref="hw"></hello-word>
this.$refs.hw.foo ='bar'
- 子组件
// data
foo:'foo'
3️⃣子元素children
- 父组件
this.$children[0].foo = 'dong'
注:子元素不保证顺序,也不是响应式。数组只读。
②子组件向父组件传值
props传递数据原则:单向数据流,只允许父组件向子组件传递数据,不允许子组件直接操作数据
观察者模式:子组件派发,父组件监听。事件真正的监听者是子组件(谁派发谁监听)。
- 子组件通过自定义事件
$emit(自定义事件,携带参数)
向父组件传递信息
// 子组件
Vue.component('menu-item',{
props:['title'],
template:`
<div>
<div @click='$emit("enlarge-text",5)'>扩大字体大小</div>
</div> `
})
- 父组件模板中监听子组件的事件,事件名称需要一致
enlarge-text
,获取参数$event
<!-- 父组件 -->
<div :style='{fontSize:fontSize+"px"}'>{{pmsg}}</div>
<menu-item title='来自父组件的值' @enlarge-text='handle($event)'></menu-item>
// 父组件中
data:{
pmsg:'hello',
fontSize:10
}
// 子组件传递参数val=5
methods:{
handle:function(val){
this.fontSize += val;
}
}
③非父子组件间传值(任意两个组件之间)
1️⃣任意组件
事件中心(事件总线)或vuex
- 单独的事件中心(事件总线)管理组件间的通信
var eventHub = new Vue();
- 监听事件与销毁事件
// 'add-todo'事件名称,addTodo事件函数
mounted:function(){
eventHub.$on('add-todo',addTodo)
}
eventHub.$off('add-todo')
eventHub.$on('add-todo',(val)=>{
this.num += val
})
- 触发事件
handle:function(){
eventHub.$emit('add-todo',id)
}
- 事件中心(事件总线),全局
// main.js
Vue.prototype.$bus = new Vue()
// child1
this.$bus.$on('foo',handle)
// child2
this.$bus.$emit('foo')
2️⃣兄弟组件
通过$parent
共同的父组件进行传递
④祖先和后代
由于嵌套层数过多,传递props不切实际,可以使用provide/inject
单向传值:祖先->后代
应用:用于高阶插件/组件库开发,不推荐直接用于应用程序代码。
// 祖先
provide(){
return {foo:'foo'}
}
//后代
inject:['foo']
- 父组件
// template
<hello-word></hello-word>
// 和data同级
provide:{
foo:'foo'
}
// 传动态的值
provide(){
return {foo:this.val}
}
data(){
return {
val:123
}
}
- 子组件
// 和data同级
inject:['foo']
4.组件插槽 模板内容
分类 子组件 父组件(插槽内容) 匿名插槽 <slot>默认内容</slot> <error-box>有bug发生</error-box> 具名插槽 <slot name="header"></slot> <h1 v-slot:header>标题内容</h1> 作用域插槽 <slot :item="item">{{item.name}}</slot> <template v-slot:default="scope">
{{scope.item.name}}
</template>
父组件向子组件传递内容
①匿名插槽
- 插槽位置,子组件模板做
// 子组件预留一个插槽位置slot
Vue.component('error-box',{
template:`
<div>
<strong>ERROR:</strong>
<slot>默认内容</slot>
</div> `
})
- 插槽内容
<!-- 父组件传递插槽内容 -->
<error-box>有bug发生</error-box>
②具名插槽
根据名称匹配,没有匹配到的放到默认<slot>
中。
v-slot
2.6.0引入slot
被官方废弃。
<!-- 子组件插槽定义 -->
<div>
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
<!-- 父组件插槽内容 -->
<!-- 一个标签 -->
<base-layout>
<h1 v-slot:header>标题内容</h1>
<p>主要内容1</p>
<p>主要内容2</p>
<p v-slot:footer>底部内容</p>
</base-layout>
<!-- 父组件插槽内容 -->
<!-- template多个标签 -->
<base-layout>
<template v-slot:header>
<h1>标题内容1</h1>
<h1>标题内容2</h1>
</template>
<p>主要内容1</p>
<p>主要内容2</p>
<template v-slot:footer>
<p>底部内容1</p>
<p>底部内容2</p>
</template>
</base-layout>
③作用域插槽
应用场景:父组件获取子组件的内容并进行加工处理
2.6.0以后 slot-scope
废弃,使用v-slot
。
// 子组件插槽定义 提供slot位置,绑定属性item
Vue.component('fruit-list',{
props:['list'],
template:`
<div>
<li v-for="item in list" :key="item.id">
<slot :item="item">{{item.name}}</slot>
</li>
</div>
`
})
<!-- 父组件 -->
<!-- slot-scope(v-slot)可以得到子组件绑定的属性item -->
<!-- 也可以解构直接写item,v-slot:default="item" -->
<fruit-list :list="list">
<template v-slot:default="scope">
<strong v-if="scope.item.id==2" class="current">{{scope.item.name}}</strong>
<span v-else>{{scope.item.name}}</span>
</template>
</fruit-list>
date:{
list:[{
id:1,
name:'apple'
},{
id:2,
name='orange'
}]
}