插槽:父组件和子组件内容的一个通信
子组件使用<slot>接收父组件传入的内容
如果内容有多个标签时,使用<template>包裹
默认插槽:
<template v-slot:default>
<h2>标题</h2>
<p>插槽内容</p>
</template>
<slot></slot>
具名插槽:v-slot替代字符:#
<template v-slot:tree>
<p>树</p>
</template>
<slot name="tree"></slot>
作用域插槽:(可以传参)
<template v-slot:listtemp="slotList">
<ul>
<li v-for="book in slotList.books">
{{book}}
</li>
</ul>
</template>
data() {
return {
title: "子组件",
list:['javascript高级编程','vue高级','css高级']
};
},
<slot name="listtemp" :books="list"></slot>
依赖注入:利用props的话,只可以进行父子通信,如果要进行爷孙或者关系更深层的通信,要使用依赖注入。
跨组件通讯
依赖注入:provide-inject
在根元素写入要传的值:
data() {
return {
title: "父组件",
msg:'这是根组件数据'
}
},
provide(){
return {
message:this.msg
}
},
在子/孙组件写inject来接收
inject:['message']
注入别名:(根节点传入的时候名字与子孙组件的名字相重的时候,可以在用到的子组件的地方改个名字。)
下面的代码的意思是将message的名字改为msg,defalut的意思是如果没有传值下来,页面会显示的值为默认值。
inject:{
msg:{
from:'message',
default:'默认值'
}
},
和响应式数据配合使用:message: computed(()=>this.msg)
如果父组件的值改变了,但是子组件不会发生变化,要利用计算属性来改变值:因为计算属性依赖的值变化他会跟着变化!
data() {
return {
title: "父组件",
msg:'这是根组件数据'
}
},
// 提供数据
provide(){
return {
// message:this.msg
message: computed(()=>this.msg)
}
},
methods: {
bindUpdateMessage(){
this.msg = '新内容'
}
},
动态组件: 组件是动态变化的
- 让多个组件使用同一个挂载点,并动态切换,这就是动态组件
<component>元素包裹起来
<component :is="currentTab"></component>
可以实现多个组件来回切换
import Home from "./Home.js";
import Category from "./Category.js";
import Cart from "./Cart.js";
import My from "./My.js";
/**
* 点击tab选项 内容区域切换为对应组件
* 1. tab选项绑定点击事件
* 2. 切换组件
*/
export default {
components: {
Home,
Category,
Cart,
My,
},
data() {
return {
title: "动态组件",
currentTab:'home',
list:[
{name:'home',title:'首页'},
{name:'category',title:'分类'},
{name:'cart',title:'购物车'},
{name:'my',title:'我的'},
],
isActive:false
};
},
methods: {
onTabChange(tabName){
this.currentTab = tabName
}
},
/*html*/
template: `<div class="g-container">
<div class="g-content">
<component :is="currentTab"></component>
</div>
<!--<ul class="g-footer">
<li @click="onTabChange('home')" :class="{active:currentTab=='home'}">首页</li>
<li @click="onTabChange('category')" :class="{active:currentTab=='category'}">分类</li>
<li @click="onTabChange('cart')" :class="{active:currentTab=='cart'}">购物车</li>
<li @click="onTabChange('my')" :class="{active:currentTab=='my'}">我的</li>
</ul>-->
<ul class="g-footer">
<li v-for="item in list" @click="onTabChange(item.name)" :class="{active:currentTab==item.name}">{{item.title}}</li>
</ul>
</div>`,
};
购物车:(其它页面与此页面一样只改了Title的值)
/**
*/
export default {
data() {
return {
title: "购物车",
};
},
/*html*/
template: `<div>
<h2>{{title}}</h2>
</div>`,
};
组件来回切换,在首页的时候,生命周期会经历创建和挂载,点击到其它页面时,首页的生命周期会被销毁,此时,所在的页面的生命周期就创建挂载起来。以此类推,如果我想实现一个组件缓存起来,不进行销毁。使用:<keep-alive>将不销毁的组件放这儿</keep-alive>
当一个组件在<keep-alive>中被切换时,他的activated和deactivated生命周期钩子被调用,用来代替mounted和unmounted。这适用于<keepAlive>的直接子节点及其所有子孙节点。
如何实现我想缓存哪些?哪些不缓存呢?
可以使用include / exclude来完成
<keep-alive include="Home,Category,Cart">
<component :is="currentTab"></component>
</keep-alive>
在表达方式上,还可以用多样化:
组件如果想要条件性地被keepAlive缓存,就必须显示声明一个name选项。 name里面存放的是所在文件的文件名。
异步组件:(性能优化)从后端(服务端)拿到数据。
- Vue 提供了defineAsyncComponent方法来实现此功能
首先要引入:
import { defineAsyncComponent } from "https://unpkg.com/vue@3/dist/vue.esm-browser.js";
获取异步主件,使用promise。这里只是模拟~
const AsyncChild = defineAsyncComponent(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
//异步获取后端定义的异步组件
const asyncComponent = {
template: `<p>我是异步组件</p>`,
};
resolve(asyncComponent);
}, 2000);
});
});
获取后,进行注册
components: {
Child,
AsyncChild,
},
在templeate中使用:
<async-child></async-child>
在显示我们异步主件前,先显示加载中....... 使用 Suspense即可
<!-- Suspense 作用: 首先显示 名为fallback的插槽内容,当异步组件加载完成后,显示异步组件 -->
<Suspense>
<async-child></async-child>
<template #fallback>
<p>加载中...</p>
</template>
</Suspense>
Teleport传送门:
Dialog是子组件,传送的是子组件
<Teleport to="body">
<Dialog v-if="show" @closeDialog="show=false"></Dialog>
</Teleport>