Vue学习笔记(八)--- 动态组件和组件插槽

一、动态组件

1、概念

​ 动态组件是指在页面的某个位置,动态的切换不同的组件,显示不同的组件内容。Vue 为开发者提供了一个内置的 组件,专门用来实现动态组件的渲染。

2、基本使用

<template>
  <div class="app-container">
    <button @click="comName = 'Left'">展示 Left</button>
    <button @click="comName = 'Right'">展示 Right</button>

    <div class="box">
      <!-- 渲染 Left 组件和 Right 组件 -->
      <!-- 1. component 标签是 vue 内置的,作用:组件的占位符 -->
      <!-- 2. is 属性的值,表示要渲染的组件的名字 -->
      <!-- 3. is 属性的值,应该是组件在 components 节点下的注册名称 -->
        <component :is="comName"></component>
    </div>
  </div>
</template>

<script>
import Left from '@/components/Left.vue'
import Right from '@/components/Right.vue'

export default {
  data() {
    return {
      // comName 表示要展示的组件的名字
      comName: 'Left'
    }
  },
  components: {
    Left,
    Right
  }
}
</script>
页面效果:

在这里插入图片描述

点击展示 Right 按钮,动态切换到Right组件:

在这里插入图片描述

3、keep-alive

​ 在基本使用中,我们虽然使用了 组件实现了组件的动态切换,但是每次组件的切换都是将旧组件销毁,并创建新组件并渲染。这样就会就会造成一个问题,那就是旧组件中的一些数据的改变不会被保存,当动态切换组件之后,再切回来,组件中的数据就会被初始化。而且这样还会消耗很多不必要的内存。

​ 为了解决这个问题,vue 给我们准备一个内置组件 ,它将 组件包含在内,并将所有出现在 组件 中的组件进行缓存,当切换回来时,再进行激活,组件内的数据也会原封不动的进行显示。

​ 组件被创建时会触发 created 生命周期函数,在被销毁时,会触发destroyed 生命周期函数。当组件被激活时,会触发 activated 生命周期,当组件被缓存时,会触发 deactivated 生命周期函数。为了验证 我们对 Left 组件进行补充修改:

Left 组件:
<template>
  <div class="left-container">
    <h3>Left 组件 --- {{ count }}</h3>
    <button @click="count += 1">+1</button>
  </div>
</template>

<script>
export default {
  name: 'MyLeft',
  data() {
    return {
      count: 0
    }
  },
  created() {
    console.log('Left 组件被创建了!')
  },
  destroyed() {
    console.log('Left 组件被销毁了~~~')
  },

  // 当组件第一次被创建的时候,既会执行 created 生命周期,也会执行 activated 生命周期
  // 当组件被激活的时候,只会触发 activated 生命周期,不再触发 created。因为组件没有被重新创建
  activated() {
    console.log('Left 组件被激活了,activated')
  },
  deactivated() {
    console.log('Left 组件被缓存了,deactivated')
  }
}
</script>
根组件:
<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 组件 -->
      <!-- keep-alive 会把内部的组件进行缓存,而不是销毁组件 -->
      <keep-alive exclude="MyRight">
        <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 表示要展示的组件的名字
      comName: 'Left'
    }
  },
  components: {
    Left,
    Right
  }
}
</script>
打开页面:

在这里插入图片描述

修改left组件中变量值:

在这里插入图片描述

点击展示 Right 组件:

在这里插入图片描述

切换回 Left 组件,被修改变量的值未被初始化:

在这里插入图片描述

4、include 和 exclude

​ 默认是将包含在内的所有组件进行缓存,如果我们只想缓存部分组件,那我们可以通过 组件的 include 和 exclude 属性来实现。 include 属性用来指定哪些组件会被缓存,exclude 属性用来指定哪些组件不被缓存,两者都是通过组件名来进行匹配,多个组件名之间用逗号进行分隔。两者不可同时使用,只能选择其一使用。

<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 组件 -->
      <!-- keep-alive 会把内部的组件进行缓存,而不是销毁组件 -->
      <!-- 在使用 keep-alive 的时候,可以通过 include 指定哪些组件需要被缓存; -->
      <!-- 或者,通过 exclude 属性指定哪些组件不需要被缓存;但是:不要同时使用 include 和 exclude 这两个属性 -->
      <keep-alive exclude="MyRight">
        <component :is="comName"></component>
      </keep-alive>
    </div>
  </div>
</template>

<script>
import Left from '@/components/Left.vue'
import Right from '@/components/Right.vue'

export default {
  // 这是声明组件时,为组件指定的name,那么此时调试工具中 组件的名称就是 name 名称,
  // 而且 include 和exclude 属性匹配的组件名 也应该是这个 name属性的值
  name: 'APP',
  data() {
    return {
      // comName 表示要展示的组件的名字
      comName: 'Left'
    }
  },
  components: {
    // 如果在“声明组件”的时候,没有为组件指定 name 名称,则组件的名称默认就是在此处“注册时候的名称”
    // 此时在调试工具中 组件的名称就是 此处注册的名称, include 和 exclude 匹配的名称也应该是这个名称
    Left,
    Right
  }
}
</script>

二、组件插槽

1、插槽概念

​ 插槽(slot)是 Vue 为组件的封装者提供的能力,允许开发者在封装组件时,把不确定的、希望由用户传递数据进来的部分定义为插槽。简单来说,插槽就是组件在封装期间,为使用者预留的内容占位符。插槽的内容由使用者去指定。

2、基本插槽

Left 组件:
<template>
	<p>
        下面是用户通过插槽传递的数据信息
    </p>
	<!-- 通过slot标签,为用户预留插槽,用户通过组件包含的内容将会在这里显示 -->
	<!-- solt 标签内的内容,相当于默认内容 如果使用者没有为插槽提供内容 则会显示这些默认内容 -->
	<slot>这是设置的默认内容</slot>
</template>
APP根组件:
<template>
  <div class="app-container">
    <h1>App 根组件</h1>
    <Left>
        <!-- 在组件标签内包含的内容 将会默认显示到组件内的 solt 标签的位置 -->
        <!-- 如果组件内没有定义 solt 标签  那这些自定义内容将会被丢弃 -->
        <p>这是用户自定义要显示在 Left 组件中的内容</p>
    </Left>
  </div>
</template>

<script>
import Left from '@/components/Left.vue'
    
export default {
  components: {
    Left
  }
}

3、具名插槽

​ 如果在封装组件时需要预留多个插槽,那么想要将内容显示在特定插槽中,就需要为每个 插槽指定具体的 name 名称,这种带有 name 属性的插槽就叫做具名插槽。如果一个插槽没有指定 name 属性,那么这个插槽就会有一个默认名称 default 。

​ 想要向具名插槽提供内容,我们可以用组件标签或者在组件标签内使用 包裹要传递的内容,并在组件标签 或 上使用 v-slot 指令,指定传递到哪个具名插槽。该指令也可以缩写,将 v-slot:插槽名称 -> #插槽名称。

article 组件:
<template>
  <div class="article-container">
    <h3>Article 组件</h3>
    <!-- 文章的标题 -->
    <div class="header-box">
      <slot name="title"></slot>
    </div>

    <!-- 文章的内容 -->
    <div class="content-box">
      <slot name="content"></slot>
    </div>

    <!-- 文章的作者 -->
    <div class="footer-box">
      <slot name="author"></slot>
    </div>
  </div>
</template>
App根组件:
<template>
  <div class="app-container">
    <h1>App 根组件</h1>

    <Article>
      <!-- v-slot: 后面要跟上插槽的名字 但该指令不能直接用在元素身上,必须用在 template 标签或者组件标签上 -->
      <!-- template 这个标签,它是一个虚拟的标签,只起到包裹性质的作用,但是,不会被渲染为任何实质性的 html 元素 -->
      <template v-slot:title>
        <h3>一首诗</h3>
      </template>
	  <!-- v-slot: 指令的简写形式是 # -->
      <template #content>
        <div>
          <p>啊,大海,全是水。</p>
          <p>啊,蜈蚣,全是腿。</p>
          <p>啊,辣椒,净辣嘴。</p>
        </div>
      </template>
	  <!-- v-slot: 指令的简写形式是 # -->
      <template #author>
        <div>作者:彬果锅</div>
      </template>
    </Article>
  </div>
</template>

<script>
import Article from '@/components/Article.vue'
    
export default {
  components: {
    Article
  }
}

4、作用域插槽

​ 在封装组件的过程中,可以为预留的 插槽绑定 props 数据,这种绑定了数据的插槽叫做 作用域插槽。简单来说就是让父组件中的插槽内容能够访问子组件插槽中才有的数据。在子组件的插槽中,以属性值的形式将数据向外传递,父组件中可以通过 v-slot:插槽名=“props” 来获取包含子组件所有插槽 向外传递数据的对象。然后就可以使用相关数据了。也可以通过解构赋值的方式简化数据的接收。

article 组件:
<template>
  <div class="article-container">
    <h3 v-color="'red'">Article 组件</h3>
    <!-- 文章的标题 -->
    <div class="header-box">
      <slot name="title"></slot>
    </div>

    <!-- 文章的内容 -->
    <div class="content-box">
      <!-- 在封装组件时,为预留的 <slot> 提供属性对应的值,这种用法,叫做 “作用域插槽” -->
      <slot name="content" msg="hello vue.js" :user="userinfo"></slot>
    </div>

    <!-- 文章的作者 -->
    <div class="footer-box">
      <slot name="author"></slot>
    </div>
  </div>
</template>

<script>
export default {
  // 首字母要大写
  name: 'Article',
  data() {
    return {
      // 用户的信息对象
      userinfo: {
        name: 'zs',
        age: 20
      }
    }
  }
}
</script>
App根组件:
<template>
  <div class="app-container">
    <h1>App 根组件</h1>

    <Article>
      <template v-slot:title>
        <h3>一首诗</h3>
      </template>
	  <!-- 正常方式接收数据,接收的是一个对象,包含子组件所有插槽向外传递的数据 -->
     <!-- <template #content="scope">
        <div>
          <p>{{ scope.msg }}</p>
          <p>{{ scope.user.name }}</p>
        </div>
      </template> -->
	  <!-- 解构赋值的方式简化数据接收 -->
      <template #content="{ msg, user }">
        <div>
          <p>啊,大海,全是水。</p>
          <p>啊,蜈蚣,全是腿。</p>
          <p>啊,辣椒,净辣嘴。</p>
          <p>{{ msg }}</p>
          <p>{{ user.name }}</p>
        </div>
      </template>
	  <!-- v-slot: 指令的简写形式是 # -->
      <template #author>
        <div>作者:彬果锅</div>
      </template>
    </Article>
  </div>
</template>

<script>
import Article from '@/components/Article.vue'
    
export default {
  components: {
    Article
  }
}



版权声明:本文为weixin_45092437原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。