1.什么是动态组件?动态组件就是指的是动态切换组件的显示和隐藏
可以把component理解为一个占位符,专门为组件占位,需要展示哪个组件,就给component指定组件的名称即可
如下:
但此时会发现上面的值是写死的,下面代码案例就可以实现动态绑定了
<template>
<div class="app-container">
<h1>App 根组件</h1>
<hr />
<button @click="comName='left'">展示Left</button>
<button @click="comName='right'">展示Right</button>
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
<!-- <left></left>
<right></right> -->
<keep-alive>
<component :is='comName'></component>
</keep-alive>
</div>
</div>
</template>
<script>
import left from '@/components/Left.vue'
import right from '@/components/Right.vue'
export default {
data(){
return{
comName:'right'
}
},
components:{
left,
right
}
}
</script>
<style lang="less">
.app-container {
padding: 1px 20px 20px;
background-color: #efefef;
}
.box {
display: flex;
}
</style>
当在left组件定义个按钮,让其值自增1,初始值为0,当加到某一值时,切换right组件,再切换回来,就会发现left值又回到了初始值,如下:
初始值为0,加到了18
点击切换到right组件
再点回来left组件
可以发现left组件切换后回到了初始值,保存不了数据(即每次切换组件都是组件重新渲染了,就是组件创建–销毁—创建–销毁的过程)
可以通过-keep-alive解决这个问题(就是保持组件的状态)
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
<!-- <left></left>
<right></right> -->
<keep-alive>
<component :is='comName'></component>
</keep-alive>
</div>
keep-alive可以把内部的组件进行缓存,而不是销毁组件
使用-keep-alive包裹要保持状态的组件即可(用vue调试工具可以查看left组件每次切换没有被销毁重新创建,而是变灰了,等重新切换回来就正常了(组件被缓存到内存了),同理right组件也是灰了,然后二者就不会重复销毁—创建—销毁—创建了)
然后刷新网页,问题成功解决
当组件被缓存时,会自动触发组件的deactivated生命周期函数。
当组件被激活时,会自动触发组件的activated生命周期函数。
<script>
export default {
activated(){
console.log("组件被激活了,activated");
},
destroyed(){
console.log("组件被销毁了");
},
deactivated(){
console.log("组件被缓存了");
},
data(){
return {
count:0
}
}
}
</script>
下面演示left组件会被缓存,right组件不用被缓存的示例
<keep-alive include="left">//指定left组件被缓存
<component :is='comName'></component>
</keep-alive>
left组件被缓存了,right组件创建—销毁—创建—销毁状态
下面演示多个组件需要被缓存,因为我这就left和right,没有更多的,因此就指定这两个
<keep-alive include="left,right">//指定组件名称,多个组件用逗号分割
<component :is='comName'></component>
</keep-alive>
下面演示left组件会被缓存,right组件不用被缓存的示例
<keep-alive exclude="right">//指定right组件不被缓存
<component :is='comName'></component>
</keep-alive>
当组件向外导出提供了name值如下
那上面的排除和包含项的具体名字不再使用注册提供的名称了,要以组件自己提供的name值为准,注册名还是以标签形式展现到页面使用
插槽(Slot)是vue为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望由用户指定的部分定义为插槽。可以把插槽认为是组件封装期间,为用户预留的内容的占位符。
在left组件定义个插槽
在App父组件使用插槽显示内容
显示效果
简单来说,插槽就是相当于子组件给父组件定义的注册名标签内的内容占位置
vue官方规定:每个插槽都有一个name属性,来指定当前插槽的名称,默认不写的话相当于name=“default”
默认情况下,使用组件的时候,提供的内容都会放到名字为default的插槽之中,例如上面的p标签默认放到default插槽内
<slot name="default"></slot>
v-slot只能加给组件或者template标签,其他不可用(不能直接用在元素上),因此使用方式如下
<div class="box">
<!-- 渲染 Left 组件和 Right 组件 -->
<left>
//使用template标签接收v-slot指令
//如果要把内容填充到指定名称的插槽中,需要使用v-slot:这个指令
//':'后面的为引用的组件的slot标签的name属性值,默认为default
//这里的template标签只起到包裹的作用,不会被渲染成真实的html元素,可以浏览器控制台查看标签源代码,可以发现并没有template
<template v-slot:default>
<p>left组件的内容区域</p>
</template>
</left>
</div>
简写: 例如插槽name属性为default,那么简写为 #default
如果在封装组件时需要预留多个插槽节点,则需要为每个插槽指定具体的name 名称。这种带有具体名称的插槽叫做“具名插槽”。示例代码如下:
注意:没有指定name名称的插槽,会有隐含的名称叫做’'default"。
新建个active.vue文件,内容如下:(在这段代码定义了三个插槽,分别为标题,内容,作者)
<template>
<div class="active-container">
<!-- 文章标题 -->
<div class="header-box">
<slot name="title"></slot>
</div>
<!-- 文章内容 -->
<div class="content-box">
<slot name="body"></slot>
</div>
<!-- 文章作者 -->
<div class="footer-box">
<slot name="zuozhe"></slot>
</div>
</div>
</template>
<script>
export default {
name: 'Active'
}
</script>
<style lang="less" scoped>
.active-container{
>div{
min-height: 150px;
}
.header-box{
background-color: pink;
}
.content-box{
background-color: lightblue;
}
.footer-box{
background-color: lightsalmon;
}
}
</style>
下面代码为App.vue使用部分
<active>
<template #title>
<h1>我是标题部分</h1>
</template>
<template #body>
<h1>我是内容部分</h1>
</template>
<template #zuozhe>
<h1>我是作者部分</h1>
</template>
</active>
在插槽定义时同时可以传递一些数据
在子组件中,定义插槽,同时携带数据,如下:(在上个代码作者里面改动的)
<!-- 文章作者 -->
<div class="footer-box">
<slot name="zuozhe" msg="xiaoji 牛逼"></slot>//msg为携带的数据(这个属性是随便定义的,也可以定义别的或多个)
</div>
在父组件中接收展示
<template #zuozhe="obj">//obj为形参,随便填写,为了接收子组件插槽的msg的数据
<h1>我是作者部分</h1>
{{ obj }}//可以通过打点访问里面的属性
</template>
效果图:
在封装组件时,为预留的提供属性对应的值,简单来说就是带数据的插槽,这种用法就叫做’作用域插槽’
还是参照上面的代码将obj解构赋值
在父组件中接收展示
<template #zuozhe="{msg}">//obj为形参,随便填写,为了接收子组件插槽的msg的数据,接收多个数据的结构赋值用逗号分割
<h1>我是作者部分</h1>
{{ msg}}//可以通过打点访问里面的属性
</template>
vue中的自定义指令分为两类,分别是:
私有自定义指令
全局自定义指令
在每个vue 组件中,可以在directives节点下声明私有自定义指令。
需求:定义v-color指令,当元素绑定到此指令时,元素内的颜色会变成红色
代码如下:
export default {
components: {
left,
active
},
// 私有自定义指令的节点
directives:{
//定义v-color指令,为绑定到的HTML元素设置红色的文字
//定义名为color的指令,指向一个配置对象(空对象时,到这里v-color就可以用了(不会报错了),只是没做操作)
color:{
//业务逻辑
//当这个v-color指令第一次被绑定元素身上时就会立即触发bind函数
bind(el){
// 形参中的el 是绑定了此指令的、原生的 DOM对象
console.log(el,1);
el.style.color='red'
}
}
}
}
//定义数据节点和自定义指令节点
data(){
return {
color:'blue'
}
},
// 私有自定义指令的节点
directives:{
//定义v-color指令,为绑定到的HTML元素设置红色的文字
//定义名为color的指令,指向一个配置对象(空对象时,到这里v-color就可以用了(不会报错了),只是没做操作)
color:{
//业务逻辑
//当这个v-color指令第一次被绑定元素身上时就会立即触发bind函数
bind(el,binding){
// 形参中的el 是绑定了此指令的、原生的 DOM对象
console.log(el,1);
console.log(binding)
el.style.color=binding.value
}
}
},
给v-color绑定数据节点的颜色数据
<h1 v-color="color">App 根组件h1>
先定义一个数据节点存放颜色,然后为元素绑定数据触发bind方法,打印bind方法的第二个形参,结果如下,可以发现数据的键为value,通过此参数动态改变颜色数据
效果图:
也可以不声明data数据节点,可以直接声明数据,通过以下代码可以动态去设置自己想要的值,切记一定要用单引号,不然会当成data数据去data去找了,代码如下:
这种方式是自己赋值,不是通过data数据获取的
<h1 v-color="'yellow'">App 根组件</h1>
<h1 v-color="'orange'">App 根组件</h1>
<h1 v-color="'pink'">App 根组件</h1>
这是再打印bind的第二个参数,会发现里面有个数据expression 之前的值为color,通过我们自己传值变成了我们自己传的值,而不是color了
expression 这个值为我们v-color=" ",为这个双引号的里面的值,我们真正的值在value里面存放,expression 代表我们自己传的值
bind指令只在第一次绑定时才出发,例如定一个按钮,点击按钮改变颜色数据,此时不会触发bind函数,这时候就需要update函数了
如果bind和update函数中的逻辑完全相同,则对象格式的自定义指令可以简写成函数格式:
directives:{
color(el,binding){
// 在bind和update时,会触发相同的业务逻辑
console.log(el,1);
console.log(binding)
el.style.color=binding.value
}
},
全局共享的自定义指令需要在main.js通过“Vue.directive()”进行声明,示例代码如下:
//全写
Vue.directive('color',{
bind(el,binding){
el.style.color=binding.value
},
update(el,binding){
el.style.color=binding.value
}
})
//简写
//参数一:字符串,表示全局自定义指令的名字
//参数二:对象,用来接收指令的参数值
Vue.directive('color',function(el,binding){
el.style.color=binding.value
})