Vue组件基础内容:插槽、动态组件(二)

2021-06-07  本文已影响0人  爱妃给朕躺下

插槽

插槽内容

建立一个组件,组件的使用通常用其名字组成的标签,例如<myComponent>组件标签里的内容</myComponent>,在这两个标签的中间,我们可以插入一些内容,这些内容是根据需求可要可不要的,但谁知道到底什么时候会需要呢?并且,自定义组件标签便是组件模板本身,不同于原生标签那样简单。如果组件标签会被渲染成组件模板,那组件标签内的内容该渲染在哪里?于是,插槽的作用显现了。在模板内某个想要插入组件标签内容的地方,放置一对<slot></slot>,这个标签被称为插槽。插槽功能很强大,第一个功能便是上述场景,<slot></slot>可在模板某个位置事先安插,用来动态盛放并在渲染时显示组件标签里的内容。

// 组件及其标签内容
<myComponent>组件标签里的内容</myComponent>

// myComponent 组件本身
<template>
<div id="app">
<slot></slot>
</div>
</template>

// 渲染后
<div id="app">
组件标签里的内容
</div>

后备内容

与第一种作用相反,插槽作为内容分发工具,不仅仅可以作为预留空间承载组件标签里的内容,也可以提前放置内容作为组件标签内容的预备。

<button type="submit">
  <slot>Submit</slot>
</button>

// 组件标签有内容
<submit-button>
  Save
</submit-button>

// 组件标签无内容
<submit-button>
</submit-button>

// 渲染后的 element
<button type="submit">
  Submit
</button>

插槽编译作用域

上面提到,组件标签里可以插入内容,只是需要插槽来呈现这些内容,插槽也可以设置后备内容,当组件标签没有内容时作为备用选择。

当作为呈现子组件标签内容的功能时,子组件标签里如果使用了父组件作用域的数据,插槽作为承载工具可以接收到这些内容。但如果插槽作为后备内容的作用时,插槽使用了所在子组件作用域的数据,希望可以作为子组件标签的后备内容时,是没办法传递这些数据给子组件标签的,因为子组件标签已经处在了父级作用域:

// 子组件模板
<div class="hello">
    <slot></slot>
  </div>

// 子组件标签
<HelloWorld>
      {{message}}
</HelloWorld>

// 子组件标签所在父组件的 data
data () {
    return {
      message: '我是父组件内容'
    }
}

// 以上内容可以渲染
// 子组件模板
<div class="hello">
    <slot>{{ message }}</slot>
</div>

// 子组件标签
<HelloWorld>
</HelloWorld>

// 子组件的 data
data () {
    return {
      message: '我是子组件的内容'
    }
}

// 插槽里的 message 作为后备内容无法传递给子组件标签

具名插槽

在前面,我们提到插槽的两个作用,一个是为组件标签提供预设内容,一个是作为承载组件标签内容的预留空间,刚好是一个互补的作用。在这里,具名插槽的意思就是插槽有名字,它的渲染位置可以通过名字来确定。这个具名插槽的作用发挥的是第二种作用:作为承载组件标签内容的预留空间。比如,组件模板已经比较臃肿了,但我又想在模板里加上一些东西,这时我可以在组件标签里设置了一些内容,希望被渲染到组件模板的具体某个位置,便可以使用具名插槽。实现在组件标签里铺设内容;

<base-layout>
  <template v-slot:header>
    <h1>Here might be a page title</h1>
  </template>

  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <template v-slot:footer>
    <p>Here's some contact info</p>
  </template>
</base-layout>

可以看到这里的组件标签里的东西类似于一个迷你模板,在上面有一个指令:v-slot,指定了该“模板”的渲染在哪个插槽的位置:

<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

这是 base-layout 的模板,可以在这里实现安插插槽,并给插槽一个名字,组件标签里的迷你模板将会被渲染到指定的插槽所在的位置。没有给定插槽名字的插槽拥有一个默认名字:default。

作用域插槽

这个插槽释放了插槽的强大能力。在前面,我们提到,子组件标签在父组件里渲染,当希望 slot 可以使用子组件的数据设置后备内容时,子组件标签并不能接收到子组件里的 data 等数据。

但作用域插槽突破了这个限制,可在子组件插槽添加属性:v-bind:user="user"

<div class="helloworld">
    <slot v-bind:user="user"></slot>
  </div>

这样,子组件的数据被传递到一个 slotProps 的对象上,当然,这个名字可以自定义。可以在子组件标签里这么使用:

<HelloWorld>
    <template v-slot:default="slotProps">
        {{ slotProps.user.firstName }}
    </template>
</HelloWorld>

这样,子组件里的数据便可以子组件标签里使用。同样的,让我们串联起前面具名插槽的知识,这里的 default 指定了插槽的名字,可以更改插槽名字指定使用哪个插槽作为渲染的容器。

但不得不吐槽的是,在子组件里的数据,费尽九牛二虎之力传到子组件标签所在父组件的作用域里,但渲染后这些内容却又是被渲染回子组件里。这样的折腾实在不知有何意义。也不知有何使用场景,暂且可搁置在一旁。

插槽的作用其实十分强大,但目前来说暂时没有驾驭它的能力,对插槽的使用场景尚无经验,可对上述基本功能做下掌握。

动态组件

想象一下以下场景,点击一个 tab 切换不同的页面。这是一个十分常见的需求,可以使用不同的组件承载不同的内容,点击以下切换组件。除了 v-if 的实现方式,还可以使用 is attribute 来切换不同的组件官方案例
现在,已经可以通过 is attribute 实现组件的切换。但你会注意到,如果你选择了一篇文章,切换到 Archive 标签,然后再切换回 Posts,是不会继续展示你之前选择的文章的。这是因为你每次切换新标签的时候,Vue 都创建了一个新的 currentTabComponent 实例。

但如果我们希望切换时组件的状态会被保留,就需要用到 keep-alive 元素的强大能力了。

<!-- 失活的组件将会被缓存!-->
<keep-alive>
  <component v-bind:is="currentTabComponent"></component>
</keep-alive>

这样,组件状态就会被缓存。当你来回切换组件时,你在组件上的操作状态将会被保留,组件不会每次都被刷新,这对于一些无更新的组件来说实现了高效复用。

上一篇下一篇

猜你喜欢

热点阅读