目录
一、vue组件概念
- 组件是可复用的Vue实例,封装标签,样式和JS代码
- 组件化:封装的思想,把页面上“可重用的部分”封装为“组件”,从而方便项目的开发和维护
- 一个页面,可以拆分成一个个组件,一个组件就是一个整体,每个组件可以有自己独立的结构样式和行为(html,css和js)
- 每个组件都是一个独立的个体,代码里体现为一个独立的.vue文件
二、组件的基础使用
1.创建组件:封装要复用的标签、样式、JS代码(创建.vue文件)
2.引入组件:(语法:import 组件名 from .vue文件路径)
3.注册组件
全局注册 - main.js中
- 语法:Vue.component("组件名","组件对象")
局部注册 - 某.vue文件内
4.使用组件
语法:
<组件名></组件名>
代码示例:
(1)全局注册
main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
// 全局注册
// 1. 创建组件 - Demo.vue
// 2. 引入组件
import Demo from './components/Demo'
// 3. 注册组件 - 全局
Vue.component("Demo", Demo)
new Vue({
render: h => h(App),
}).$mount('#app')
App.vue
<template>
<div>
<!-- 4. 使用组件 -->
<Demo></Demo>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
(2)局部注册
<template>
<div>
<!-- 4. 使用组件 -->
<Demo></Demo>
</div>
</template>
<script>
// 局部注册
// 1. 创建组件 - Demo.vue
// 2. 引入组件
import Demo from './components/Demo.vue'
export default {
// 3. 注册组件
/* 语法:
components: {
"组件名": 组件对象
}
*/
components: {
"Demo": Demo
}
}
</script>
<style>
</style>
三、组件name属性使用
1.组件定义name属性和值
2.注册组件可用上面name的值
四、组件通信-父传子
- 父(App.vue)
- 子(MyProduct.vue)
代码示例:
MyProduct.vue
<template>
<div class="my-product">
<!-- (1)定义变量 -->
<h3>标题:{{title}}</h3>
<p>价格:{{price}}</p>
<p>{{info}}</p>
</div>
</template>
<script>
export default {
// (2)props准备接收
props:['titile','price','info']
}
</script>
<style>
.my-product {
width: 400px;
padding: 20px;
border: 2px solid #000;
border-radius: 5px;
margin: 10px;
}
</style>
App.vue
<template>
<div>
<!-- (3)使用变量 -->
<Product title="好吃的汉堡包" price="20" info="新店开张,全场买一送一!"></Product>
<Product title="好吃的肯德基" price="50" info="疯狂星期四!"></Product>
<Product title="好吃的热狗" price="6" info="第二根半价!"></Product>
</div>
</template>
<script>
// 1. 创建组件(.vue文件)
// 2. 引入组件
import Product from './components/MyProduct.vue'
export default {
// 3. 注册组件
components:{
// Product: Product key和value同名可以简写 Product
Product
}
}
</script>
<style>
</style>
五、修改父传入的数据-自定义事件
从父到子的数据流向,叫做单向数据流。而子组件修改父级传过来的数据,不通知父级,就会造成数据不一致性。且Vue规定props里的变量,本身是只读的,不能修改。那么子组件如何才能修改父组件里的数据呢?
子传父如何实现?
- 父组件内,给组件@自定义事件="父methods函数"
- 组件内,恰当时机this.$emit('自定义事件名', 值)
App.vue
<template>
<div>
<!--
3.父组件内,绑定自定义事件和事件处理函数
语法:@自定义事件="父methods函数"
-->
<Product v-for="(item,index) in list" :key="index"
:title="item.title"
:price="item.price"
:info="item.info"
:index="index"
@subprice="fn"></Product>
</div>
</template>
<script>
import Product from './components/MyProduct.vue'
export default {
data(){
return{
list:[{title:"好吃的汉堡包",price:20,info:"新店开张,全场买一送一!"},
{title:"好吃的肯德基",price:50,info:"新店开张,全场买一送一!"},
{title:"好吃的热狗",price:6,info:"新店开张,全场买一送一!"}
]
}
},
components: {
Product
},
methods:{
// 4.事件处理函数
fn(index, price) {
this.list[index].price > 1 && (this.list[index].price = (this.list[index].price - price).toFixed(2))
}
}
}
</script>
<style>
</style>
MyProduct.vue
<template>
<div class="my-product">
<h3>标题:{{title}}</h3>
<p>价格:{{price}}</p>
<p>{{info}}</p>
<p>
<!-- 1.给砍价按钮绑定点击事件 -->
<button @click="kanjia">砍价</button>
</p>
</div>
</template>
<script>
export default {
props:['index','title','price','info'],
methods:{
kanjia(){
// 2.this.$emit()规定方法,主动触发父给我绑定的自定义事件,导致父methods里的事件处理函数执行
this.$emit('subprice',this.index,1)
}
}
}
</script>
<style>
.my-product {
width: 400px;
padding: 20px;
border: 2px solid #000;
border-radius: 5px;
margin: 10px;
}
</style>
六、组件通信-EventBus
场景:当2个没有引用关系的组件之间需要通信
技术本质:空白Vue对象,只负责$on和$emit
语法:
- src/EventBus/index.js 创建空白Vue对象并导出
- 在要接收值的组件:eventBus.$on('事件名', 函数体)
- 在要传递值的组件:eventBus.$emit('事件名', 值)
传递方:
<script>
// 跨组件传值-传递方
// 1.引入空白vue对象(EventBus)
import eventBus from '../EventBus'
export default {
methods:{
// 2.传递方-$emit触发事件,语法:eventBus.$emit('事件名', 值)
fn(){
eventBus.$emit('send',this.index, this.price)
}
}
}
</script>
接收方:
<script>
// 跨组件传值
// 1.引入空白vue对象(EventBus)
import eventBus from '../EventBus'
export default {
props:['arr'],
// 2.接收方-$on监听事件,语法:eventBus.$on('事件名', 函数体)
created(){
eventBus.$on('send',(index, price)=> {
// 函数体
})
}
}
</script>
七、动态dynamic组件使用
作用:在同一个挂载点,可以切换展示不同组件
语法:vue的内置componment组件,配合is属性
<component :is="变量"> </component> // 改变is属性的值,为要显示的组件名即可
场景:切换2个组件,互斥的显示或隐藏
代码示例:
App.vue
<template>
<div>
<button @click="userNameFn">账号密码填写</button>
<button @click="userInfoFn">个人信息填写</button>
<p>下面显示注册组件-动态切换:</p>
<div style="border: 1px solid red;">
<!-- vue内置的组件component,可以动态显示组件 -->
<component :is="ComName"></component>
</div>
</div>
</template>
<script>
// 动态组件 - 切换组件显示
// 场景:同一个挂载点要切换 不同组件 显示
// 1.创建要被切换的组件
// 2.引入到要展示的vue文件内,注册
// 3.变量-承载要显示的组件名
// 4.设置挂载点<component :is="变量"> </component>
// 5.点击按钮-切换comName的值要显示的组件名
import UserName from './demo/UserName.vue'
import UserInfo from './demo/UserInfo.vue'
export default {
data(){
return{
ComName:"UserName"
}
},
methods:{
userNameFn(){
this.ComName = "UserName"
},
userInfoFn(){
this.ComName = "UserInfo"
}
},
components:{
UserName,
UserInfo
},
}
</script>
<style>
</style>
UserName.vue
<template>
<div>
<div>
<span>用户名:</span>
<input type="text">
</div>
<div>
<span>密码:</span>
<input type="password">
</div>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
UserInfo.vue
<template>
<div>
<div>
<span>人生格言:</span>
<input type="text">
</div>
<div>
<span>自我介绍</span>
<input type="text">
</div>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
注意点:
- 频繁的切换回导致组件频繁创建和销毁,性能会降低。解决方案为:组件缓存
八、组件缓存
作用:不会频繁的创建和销毁组件,页面呈现更快
语法:vue内置的keep-alive组件,包起来要频繁切换的组件
<keep-alive>
<component :is="ComName"></component>
</keep-alive>
九、组件激活和非激活
问题:如何知道组件被切走了,还是切换回来了
方法名:
- activated:激活时触发
- deactivated:失去激活状态触发
十、组件插槽
作用:通过slot标签,让组件内可以接收不同的标签结构显示
语法口诀:
- 组件内使用<slot></solt>占位
- 使用组件时<Pannel></Pannel>夹着的地方,传入标签替换slot
代码示例:
App.vue
<template>
<div>
<!-- 2.使用组件,传入具体的标签进行替换到slot位置上 -->
<SlotDemo>
<span>组件插槽</span>
</SlotDemo>
<UserInfo>
<input type="text">
</SlotDemo>
<SlotDemo>
<img src="./assets/images.jpg" alt="">
</SlotDemo>
</div>
</template>
<script>
import SlotDemo from './demo/SlotDemo.vue'
export default {
components:{
SlotDemo
}
}
</script>
<style>
</style>
Slot.vue
<template>
<div>
<!-- 1.组件内使用<slot></slot> 占位 -->
<slot></slot>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
十一、具名插槽
作用:slot占位,给name属性起名字来区分
场景:一个组件内有2处以上需要外部传入标签的地方
语法:
- template配合v-slot:名字来分发对应标签
代码示例:
App.vue
<template>
<div>
<SlotDemo>
<!-- 2.使用组件,template配合v-slot:插槽名,夹着传入具体标签-->
<template v-slot:title>
<p>组件插槽-具名插槽</p>
</template>
<template v-slot:image>
<img src="./assets/images.jpg" alt="">
</template>
</SlotDemo>
</div>
</template>
<script>
import SlotDemo from './demo/SlotDemo.vue'
export default {
components:{
SlotDemo
}
}
</script>
<style>
</style>
SlotDemo.vue
<template>
<div>
<!-- 1.slot占位 - name属性起名字 -->
<slot name="title"></slot>
<slot name="image"></slot>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
十二、作用域插槽
作用:使用插槽时,可以使用组件内的变量
语法和口诀:
- 子组件,在slot上绑定属性和子组件的值
- 使用组件,传入自定义标签,用template和v-slot='自定义变量名'
- scope变量名自动绑定slot上所有属性和值
代码示例:
App.vue
<template>
<div>
<SlotDemo>
<!-- 2.使用组件,template配合v-slot="变量名",变量名会收集slot身上属性和值形成对象 -->
<template v-slot="scope">
<p>年龄:{{ scope.studentInfo.age }}</p>
</template>
</SlotDemo>
</div>
</template>
<script>
import SlotDemo from './demo/SlotDemo.vue'
export default {
components:{
SlotDemo
}
}
</script>
<style>
</style>
SlotDemo.vue
<template>
<div>
姓名:{{student.name}}<br>
<!-- 1.slot标签,自定义属性和内变量关联 -->
<!-- 冒号:后面的变量名可以随便起 -->
<slot :studentInfo="student">
</slot>
</div>
</template>
<script>
export default {
data(){
return{
student:{
name:"张三",
age:21
}
}
}
}
</script>
<style>
</style>