JianShaw / note

记录个人学习
2 stars 0 forks source link

理解vue中的插槽 #9

Open JianShaw opened 5 years ago

JianShaw commented 5 years ago

什么是插槽

通常我们声明使用一个vue按钮组件.

const TestButton = {

     template: `<button>组件A</button>`
}

使用这个button组件

<test-button>提交</test-button>

渲染出的button上的text依然是 组件A

vue中实现了slot(插槽)来做内容分发。有了slot,我们能写出更灵活强大的插件。

const TestButton = {

     template: `<button><slot>组件A</slot></button>`
}
<test-button>提交</test-button>

现在,渲染出的就是文字内容为提交的按钮了。

具名插槽

在我使用elementel-dialog,有如下代码

<el-dialog
  title="提示"
  :visible.sync="dialogVisible"
  width="30%"
  :before-close="handleClose">
  <span>这是一段信息</span>
  <span slot="footer" class="dialog-footer">
    <el-button @click="dialogVisible = false">取 消</el-button>
    <el-button type="primary" @click="dialogVisible = false">确 定</el-button>
  </span>
</el-dialog>

slot='footer'便是名为footer的插槽。查看源码,dialog实际设置了3个插槽,分别为title,default,footer。使用组件时,通过显示的指定对应的name便可以设置相关内容。

设置具名插槽的语法.

<div class="define-slot-name">
    <header>
          <slot name="header"> </slot>
    </header>
</div>

注意,slot没有设置name的时候,默认为default

插槽的作用域

dialog的示例代码为例

<el-button type="text" @click="dialogVisible = true">点击打开 Dialog</el-button>

<el-dialog
  title="提示"
  :visible.sync="dialogVisible"
  width="30%"
  :before-close="handleClose">
  <span>这是一段信息</span>
  <span slot="footer" class="dialog-footer">
    <el-button @click="dialogVisible = false">取 消</el-button>
    <el-button type="primary" @click="dialogVisible = false">确 定</el-button>
  </span>
</el-dialog>

<script>
  export default {
    data() {
      return {
        dialogVisible: false
      };
    },
    methods: {
      handleClose(done) {
        this.$confirm('确认关闭?')
          .then(_ => {
            done();
          })
          .catch(_ => {});
      }
    }
  };
</script>
// 对于slot内的`this`指向的显然是父组件的。
  <span slot="footer" class="dialog-footer">
    <el-button @click="dialogVisible = false">取 消</el-button>
    <el-button type="primary" @click="dialogVisible = false">确 定</el-button>
  </span>

业务是变化的,有时候我们是需要插槽访问到子组件才有的数据的。如下的这个todolist组件

<parent>
<todo-list v-bind:data="todos">
  <template v-slot:todo="{ todo }">
    <span v-if="todo.isComplete">✓</span>
    {{ todo.text }}
  </template>
</todo-list>
</parent>
<script>
export default {
   data(){
      return {
         todos: []
     }
   }
}
</script>

todolist组件允许传入一个list列表,在slot里能访问到每个单独的list,根据具体的业务判断显示不同的内容。

vue允许我们在定义slot时,绑定一个属性上去,称之为 插槽prop 。在父级作用域中,我们可以给v-slot 带一个值来定义我们提供的插槽 prop 的名字。

我们来看一下todolist是如何实现的

<ul>
   <li v-for="todo in todos" v-bind:key="todo.id">
       <!--
           slot定义在 遍历内,我们为每个todo都准备一个插槽。
           通过`v-bind:todo=todo`,prop就是 插槽prop。
           在使用todolist的时候,就可以访问到子组件的todo了。使用方法如上段代码。

    -->
       <slot name="todo" v-bind:todo ="todo"'>
         {{todo.text}}
        </slot>
   </li>
</ul>

文档中将这种插槽称之为作用域插槽

插槽中一些简写的方法

独占默认插槽的简写

前提是只有默认插槽,在强调一遍,是只有默认插槽,组件的标签代替<template>

<component-name v-slot="scope">
{{ scope.user.name }}
</component-name>

结构插槽prop

可以使用es6的结构来处理scope

<component-name v-slot="{user}">  // {user: person} => 重命名, {user= {name: "haha"}} 设置默认值,如果user为undefined
{{ user.name }}
</component-name>

同上边例子作对比,用结构直接取出user。

具名插槽的缩写

v-slot:name也可以被缩写为#name

<base-layout>
  <template #header>
    <h1>Here might be a page title</h1>
  </template>

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

  <template #footer>
    <p>Here's some contact info</p>
  </template>
</base-layout>

<!-- 代码有错。这样会触发一个警告 -->
<current-user #="{ user }">
  {{ user.firstName }}
</current-user>
<!-- 如果你希望使用缩写的话,你必须始终以明确插槽名取而代之:
也就是default的默认slot,要明确写出default的插槽名才可以。
-->
<current-user #default="{ user }">
  {{ user.firstName }}
</current-user>