Vue 插槽 & 高复用组件

2019-3-4更新】Vue 2.6+修改了部分语法,对插槽的使用有了较多的更新。在本文中笔者在相应位置给出了更新提示和新的推荐用法


# 前言

  组件是Vue插槽中最为关键的一个特性之一,而插槽是组件的一大亮点。本文主要描述

  • 对插槽的理解
  • 匿名、具名、作用域插槽
  • 编写高复用组件的几点思路

# 为什么用插槽

  组件的最大特性就是复用性,而用好插槽能大大提高组件的可复用能力。

  组件的复用性常见情形如在有相似功能的模块中,他们具有类似的UI界面,通过使用组件间的通信机制传递数据,从而达到一套代码渲染不同数据的效果

  然而这种利用组件间通信的机制只能满足在结构上相同,渲染数据不同的情形;假设两个相似的页面,他们只在某一模块有不同的UI效果,以上办法就做不到了。可能你会想,使用 v-ifv-else来特殊处理这两个功能模块,不就解决了?很优秀,解决了,但不完美。极端一点,假设我们有一百个这种页面,就需要写一百个v-ifv-else-ifv-else来处理?那组件看起来将不再简小精致,维护起来也不容易。

  而 插槽 “SLOT”就可以完美解决这个问题

# 什么情况下使用插槽

  顾名思义,插槽即往卡槽中插入一段功能块。还是举刚才的例子。如果有一百个基本相似,只有一个模块功能不同的页面,而我们只想写一个组件。可以将不同的那个模块单独处理成一个卡片,在需要使用的时候将对应的卡片插入到组件中即可实现对应的完整的功能页。而不是在组件中把所有的情形用if-else罗列出来

  可能你会想,那我把一个组件分割成一片片的插槽,需要什么拼接什么,岂不是只要一个组件就能完成所有的功能?思路上没错,但是需要明白的是,卡片是在父组件上代替子组件实现的功能,使用插槽无疑是在给父组件页面增加规模,如果全都使用拼装的方式,和不用组件又有什么区别。因此,插槽并不是用的越多越好

插槽是组件最大化利用的一种手段,而不是替代组件的策略,当然也不能替代组件。如果能在组件中实现的模块,或者只需要使用一次v-else, 或一次v-else-ifv-else就能解决的问题,都建议直接在组件中实现。

# 准备工作

  使用插槽前,需要先了解什么是编译作用域, 即

父组件模板的内容在父组件的作用域内编译,子组件模板的内容在子组件的作用域内编译

  什么意思?假设有如下案例

// 父组件

// 组件 child-component

  在父组件作用域中参与编译的内容有:(1) 父组件P标签的greet。(2)【变量 message; (3) 变量myData
  在子组件中参与编译的内容有:(1)子组件 p 标签中的myName。(2) 【子组件中的data特性

  需要强调的是,【上】中的存在于父组件编译作用于上的message部分也就是插槽的后备内容,是存在于父组件作用域内,该部分是不能访问存在于子组件作用域【下】中的data特性的,如果需要访问这部分内容,需要使用到作用域插槽功能

  上面提到过一个观点:卡片是在父组件上代替子组件实现的功能,使用插槽无疑是在给父组件页面增加规模。从上面案例中也可以看出,子组件只提供了插槽,而具体什么内容它并不管,都交给了父组件作用于中存在于包含的那部分内容去分发。这部分内容,就是我们所说的卡片

# 单个插槽 (匿名插槽)

  在没有使用插槽前,组件内部写入的后备内容都会被抛弃,原因很简单,在父组件渲染的时候,会使用子组件里的内容来替换它在父组件的占位。如果不想被丢弃,就需要在子组件中使用单个插槽来接收内容

  单个插槽一般都是匿名的,当然也可以给他命名,默认未命名情况下,Vue2.6+版本默认为v-slot:default或简写#default

// 父组件中定义卡片

父组件

卡片内容1

卡片内容2

// child-component组件中使用slot接收

子组件

插槽默认内容

在案例中除了有卡片内容与插槽内容,我们还看到了在中定义的一段话,它是插槽标签的默认内容,会在子组件编译作用域内编译,只有当宿主元素为空,且没有相应的插入内容时才显示。上面的案例我们可以得到如下结果:

// 渲染结果:

父组件

子组件

卡片内容1

卡片内容2

# 具名插槽 (Vue2.6+有更新)

  我们可以给插槽定义名字,使其成为具名插槽。在单个插槽中,会将父组件中所有的卡片(假设都没有命名)按其在父组件中定义的顺序都接收过来;

  而具名插槽则是接收指定的卡片。这样,我们就可以在不同位置定义多个插槽,分别用来接收不同的卡片内容。也可以增加一个匿名插槽,用来接收父组件编译作用域中未被指定名称的卡片内容(剩余内容)。

Vue2.6+版本中,要求对所有的具名插槽的v-slot都添加在一个