vue的插槽

vue的插槽

插槽这个概念相对就比较抽象,但抽象的概念用生活中常见的事物去做类比,也就变得没那么抽象了!

举一个生活中的例子:比如装修房子的时候我们会在很多地方预留出一些插孔,可能要插电冰箱,插电式,插充电器等,反正就是你觉得预留在这个位置的插座一定有用,这个预留的插座就类似我们今天要说的插槽,插槽就是你在模板中提前通过一个占位符(slot)来预言一块固定的区域将来会被某些元素替换掉,但是这个位置我得先预留出来。

在veu 2.6.0 中,具名插槽作用域插槽引入了一个新的统一的语法 (即 v-slot 指令)。它取代了 slotslot-scope 这两个目前已被废弃但未被移除且仍在文档中的 attribute。

插槽的基本使用

  1. 在组件中定义一个<slot></slot>标签即可
  2. 如果插槽内具有共性的东西较多,可在插槽内设置默认值,当没有在插槽传入值时显示默认值
  3. 如果有多个值,同时放入到组件中进行替换时,一起作为替换元素

定义一个默认插槽

插槽的定义位置在组件中,所以首先我们得创建一个组件,再在这个组件中定义插槽,再使用!

老规矩,翠花上代码:

<div id="app">
	<mainview></mainview>
</div>
    
<!-- 组件模板 -->
<template id="view">
  <div>
    <slot>
      <button type="button">插槽默认值</button>
    </slot>
  </div>
</template>
    
    <script type="text/javascript">
      var app = new Vue({
        el: "#app",
        components:{
          // view注册为一个局部组件
          mainview: {
            template: "#view"
          }
        }
      })
    </script>

 

运行这段代码我们会看到组件为我们渲染除了slot插槽里的默认内容,当然我们也可以不用设置默认内容,那么它将不会显示任何东西,那如何覆盖默认内容呢?只需要在组件中插入你需要显示的内容即可,如下:

<mainview>
	<button type="button" style="color: #42B983;">
		修改插槽
	</button>
</mainview>

修改之后,我们可以看到渲染的内容已经变成了我们在 mainview 中定义的内容!

这就是插槽 <slot> 一个基本使用,还是比较好理解的。但一般在实际项目中不会像 demo 一样简单直接。

通常情况下,一个页面设计非常复杂,需要在一个页面的不同位置放入不同的插槽内容。那么在这种情况下,我们就需要给插槽 <slot> 指定一个具体的名称 name,使其具有具体匹配的特性,在 Vue 中把这种具有具体名称 name 的插槽叫做具名插槽

具名插槽

现在,假设我们要做一个博客页面,这个页面分为三个部分头部header, 内容main,底部footer,这三个模块我们都通过插槽来实现!

老规矩,翠花上代码:

<div id="app">
	<!-- 引用插槽 -->
	<base-layout>
		<template v-slot:header>
          <h1>这是头部header插槽内容</h1>
        </template>
        
        <template v-slot:default>
          <p>这是默认插槽的内容</p>
        </template>
        
        <template v-slot:footer>
          <p style="color: red;">这是footer插槽内容</p>
        </template>
        
      </base-layout>
</div>
    
    <!-- 定义组件间模板 -->
    <template id="layout">
      <div>
        <header>
          <!-- 定义具名插槽 -->
          <slot name="header"></slot>
        </header>
        <main>
          <slot></slot>
        </main>
        <footer>
          <slot name="footer"></slot>
        </footer>
      </div>
    </template>
    
    <script type="text/javascript">
      var app = new Vue({
        el: "#app",
        components:{
          // 局部注册组件
          BaseLayout: {
            template: "#layout"
          }
        }
      })
    </script>

以上案例中layout组建中定义了三个插槽,其中header和footer通过slot的name属性制定了插槽的名称,main用一个默认插槽填充,其中header和footer两个具名插槽会精确地匹配 name传入相应的插槽,任何没有被包裹在带有 v-slot<template> 中的内容都会被视为默认插槽的内容。

注意: v-slot 只能添加在 <template> ,并且可以缩写为一个#,即把参数之前的所有内容 (v-slot:) 替换为字符 #。例如 v-slot:header 可以被重写为 #header

<template #header>
	<h1>这是头部header插槽内容</h1>
</template>

作用域插槽

什么是作用域插槽?要弄清楚这个问题就得先知道作用域的概念,我们一起先来看看百度百科给出的解释!

百度百科:通常来说,一段程序代码中所用到的名字并不总是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。

作用域的使用提高了程序逻辑的局部性,增强程序的可靠性,减少名字冲突。

vue的作用域插槽在2.6版本中有所改变,之前版本中使用的slot-scope命令已经废弃,统一成了v-slot方法。

老规矩,翠花上代码:

<div id="app">
    <!-- 引入组件 -->
    <current-user></current-user>
</div>

<!-- 定义CurrentUser组件模板 -->
<template id="account">
    <div>
        <slot>
        	{{ user.LastName }}
        </slot>
    </div>
</template>

   <script type="text/javascript">
      var app = new Vue({
        el: "#app",
        components:{
          CurrentUser: {
            template:"#account",
            data:function(){
              return {
                user: {
                  fristName: "刘",
                  lastName: "德华"
                }
              }
            },
          }
        }
      })
    </script>

我们来看这个例子,首先我们定义了一个<current-user>的组件,这个组件的模板中定义了一个默认插槽并设置了一个初始值{{ user.lastName }},运行这段代码会看到如期渲染除了组建中传入的lastName的值,但是有一天这个名字我不让显示了,只让显示一个姓就行了,我们知道插槽就是为了预定义一个区块,之后你想插入什么随你,但是要在父组件中调用子组件的frstName覆盖掉插槽默认传入的lastName要怎么做呢?这时候就是作用域插槽出场的时候了!

我们看作用域的概念发现里边总是提到作用域是一个有名字的代码范围,其实这在Vue的插槽表现形式中应该就是具名插槽,具名插槽要用到v-slot指令,v-slot又只能在template中使用。

首先,我们必须把子组件的user作为slot的元素属性通过v-bind绑定上去才可以,这个绑定在 <slot> 元素上的 属性 (user)被称为插槽 prop

<slot v-bind:user="user">
	{{ user.lastName }}
</slot>

现在在父级作用域中,我们可以使用带值的 v-slot 来定义我们提供的插槽 prop 的名字:

<div id="app">
	<current-user>
		<template v-slot:default="slotProps">
          {{ slotProps.user.fristName }}
        </template>    
	</current-user>
</div>

在这个例子中,我们选择将包含所有插槽 prop 的对象命名为 slotProps,但你也可以使用任意你喜欢的名字。

注意,如果当我们提供的组件模板中有且仅有一个默认插槽时,组件的标签可以被当做插槽的模板使用,简写为:

<current-user>
    <!-- 简写 -->
	<template v-slot="slotProps">
		{{ slotProps.user.fristName }}
	</template>    
</current-user>

但是,注意默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确!

动态插槽

动态插槽依赖的是Vue2.6新增的动态参数概念,这里我大致说一下什么是动态参数:

<a v-bind:[attributeName]="url"> ... </a>

这个示例中用方括号括起来的 JavaScript 表达式作为一个指令的参数进行动态求值,求得的值将会作为最终的参数来使用!

这个动态的值可以通过方法,计算属性或者data里的数据作为内容,如下所示:

<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>
</base-layout>