[vue2]插槽

本节目标

  • 默认插槽
  • 后备内容
  • 具名插槽
  • 作用域插槽
  • 案例-商品列表

默认插槽

让组件内部的一些结构 支持自定义

步骤

  1. 组件内需要定制的结构, 使用<slot></slot>占位

  1. 使用组件时, 传入结构替换slot的位置

后备内容

封装组件时, 可以为预留的<slot>插槽提供默认内容

步骤

  1. 在<slot>标签内, 放置内容, 作为默认显示内容

  1. 使用组件时, 如果不传东西, 显示slot后备内容

  1. 使用组件时, 传递内容, 则slot整体会被替换掉

具名插槽

一个组件内有多处结构, 需要外部传入标签, 进行定制, 需要使用具名插槽

步骤

  1. 组件内的slot标签, 使用 name属性 给插槽命名

  1. 使用组件时, 使用 template 配合 v-slot:插槽名 来分发标签

  1. v-slot:插槽名 可以简写成 #插槽名

作用域插槽

定义slot插槽的同时, 是可以传值的, 给插槽上绑定数据, 使用组件时可以接收数据

组件分类

  1. 默认插槽(组件内定义一处结构)
  2. 具名插槽(组件内定义多处结构)
  3. 作用域插槽不是一类组件, 是插槽的传参语法

封装表格组件

  1. 父传子, 动态渲染表格内容
  2. 利用默认插槽, 定制操作列
  3. 删除或查看操作, 都需要用到当前项的id, 属于组件内部的数据, 可以通过作用域插槽绑定数据, 供外部使用

步骤

  1. 给slot标签, 以添加属性的方式传值

  1. 所有添加的属性, 都会被收集到一个对象中

  1. 在template中, 通过 #插槽名="obj" 接收数据, 默认插槽, 通过 #default="obj" 接收数据

案例-商品列表

1>分析需求

my-ta组件

  1. 双击显示输入框, 输入框自动获取焦点
  2. 失去焦点, 隐藏输入框
  3. 回显标签信息
  4. 内容修改, 回车, 修改标签信息

my-table组件

  1. 动态传递表格数据渲染
  2. 表头支持自定义
  3. 主体支持自定义

2>创建tag组件

<template>
  <div class="my-tag">
  <!-- <input 
        class="input"
        type="text"
        placeholder="输入标签"
   /> -->
    <div class="text">茶具</div>
   </div>
</template>

<script>
export default {
};
</script>

<style lang="less" scoped>
.my-tag {
  cursor: pointer;
  .input {
    appearance: none;
    outline: none;
    border: 1px solid #ccc;
    width: 100px;
    height: 40px;
    box-sizing: border-box;
    padding: 10px;
    color: #666;
    &::placeholder {
      color: #666;
    }
  }
}
</style>

3>控制显示隐藏

<template>
  <div class="my-tag">
    <input
      v-if="isEdit"
      v-focus
      class="input"
      type="text"
      placeholder="输入标签"
      @blur="isEdit = false"
    />
    <div v-else class="text" @dblclick="handerEdit">茶具</div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      isEdit: false,
    };
  },
  methods: {
    handerEdit() {
      this.isEdit = true;
    },
  },
};
</script>
... ...

// 全局注册自动获取焦点指令
Vue.directive('focus', {
  inserted: function (el) {
    el.focus()
  }
})
... ...

4>回显/修改数据

<template>
  <div class="my-tag">
    <input
      ... ...
      :value="value"
      @keyup.enter="inputEnter"
    />
    ... ...
  </div>
</template>

<script>
export default {
  // 通过v-model完成父子组件通信
  props: {
    value: String,
  },
  methods: {
    ... ...
    // 监听键盘回车事件
    inputEnter(e) {
      // 非空处理
      if (e.target.value.trim() === "") return alert("输入不能为空");
      // 触发自定义事件, 更新父组件数据
      // 通过事件对象拿到输入的新值
      this.$emit("input", e.target.value);
      // 编辑完成后, 隐藏输入框
      this.isEdit = false;
    },
  },
};
</script>
<template>
  <div class="table-case">
    <table class="my-table">
       ... ...
      <tbody>
        <tr>
          <td>1</td>
          <td>梨皮朱泥三绝清代小品壶经典款紫砂壶</td>
          <td>
            <img src="https://yanxuan-item.nosdn.127.net/f8c37ffa41ab1eb84bff499e1f6acfc7.jpg" />
          </td>
          <td>
            <my-tag v-model="text"></my-tag>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
export default {
  name: 'TableCase',
  data() {
    return {
     // 临时绑定数据,检测v-model
     text: '默认数据';
    }
  },
}
</script>

5>创建my-table组件

<template>
  <table class="my-table">
    <thead>
      <tr>
        <th>编号</th>
        <th>名称</th>
        <th>图片</th>
        <th width="100px">标签</th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="item in data" :key="item.id">
         <td>{{ item.id }}</td>
        <td>{{ item.name }}</td>
        <td>
          <img :src="item.picture" />
        </td>
        <td>
          MyTage组件
          <!-- <MyTage v-model="item.tag"></MyTage> -->
        </td>
      </tr>
    </tbody>
  </table>
</template>

<script>
export default {
  props: {
    data: {
      type: Array,
      require: true,
    },
  },
};
</script>

<style lang="less" scoped>
.my-table {
  width: 100%;
  border-spacing: 0;
  img {
    width: 100px;
    height: 100px;
    object-fit: contain;
    vertical-align: middle;
  }
  th {
    background: #f5f5f5;
    border-bottom: 2px solid #069;
  }
  td {
    border-bottom: 1px dashed #ccc;
  }
  td,
  th {
    text-align: center;
    padding: 10px;
    transition: all 0.5s;
    &.red {
      color: red;
    }
  }
  .none {
    height: 100px;
    line-height: 100px;
    color: #999;
  }
}
</style>
<template>
  <div class="table-case">
    <MyTable :data="goods"></MyTable>
  </div>
</template>

<script>
export default {
  name: "TableCase",
  data() {
    return {
      goods: [
        {
          id: 101,
          picture:
            "https://yanxuan-item.nosdn.127.net/f8c37ffa41ab1eb84bff499e1f6acfc7.jpg",
          name: "梨皮朱泥三绝清代小品壶经典款紫砂壶",
          tag: "茶具",
        },
        {
          id: 102,
          picture:
            "https://yanxuan-item.nosdn.127.net/221317c85274a188174352474b859d7b.jpg",
          name: "全防水HABU旋钮牛皮户外徒步鞋山宁泰抗菌",
          tag: "男鞋",
        },
        {
          id: 103,
          picture:
            "https://yanxuan-item.nosdn.127.net/cd4b840751ef4f7505c85004f0bebcb5.png",
          name: "毛茸茸小熊出没,儿童羊羔绒背心73-90cm",
          tag: "儿童服饰",
        },
        {
          id: 104,
          picture:
            "https://yanxuan-item.nosdn.127.net/56eb25a38d7a630e76a608a9360eec6b.jpg",
          name: "基础百搭,儿童套头针织毛衣1-9岁",
          tag: "儿童服饰",
        },
      ],
    };
  },
};
</script>

<style lang="less" scoped>
.table-case {
  width: 1000px;
  margin: 50px auto;
  img {
    width: 100px;
    height: 100px;
    object-fit: contain;
    vertical-align: middle;
  }
}
</style>

6>结构自定义

<template>
  <table class="my-table">
    <thead>
      <tr>
        <slot name="header"></slot>
      </tr>
    </thead>
    <tbody>
      <tr v-for="item in data" :key="item.id">
        <slot name="body" :item="item"></slot>
      </tr>
    </tbody>
  </table>
</template>

<script>
export default {
  props: {
    data: {
      type: Array,
      require: true,
    },
  },
};
</script>
<template>
  <div class="table-case">
    <MyTable :data="goods">
      <template #header>
        <th>编号</th>
        <th>名称</th>
        <th>图片</th>
        <th width="100px">标签</th>
      </template>
      <template #body="{ item }">
        <td>{{ item.id }}</td>
        <td>{{ item.name }}</td>
        <td>
          <img :src="item.picture" />
        </td>
        <td>
          <!-- MyTage组件 -->
          <MyTage v-model="item.tag"></MyTage>
        </td>
      </template>
    </MyTable>
  </div>
</template>

<script>
export default {
  name: "TableCase",
  data() {
    return {
      goods: [
        {
          id: 101,
          picture:
            "https://yanxuan-item.nosdn.127.net/f8c37ffa41ab1eb84bff499e1f6acfc7.jpg",
          name: "梨皮朱泥三绝清代小品壶经典款紫砂壶",
          tag: "茶具",
        },
        {
          id: 102,
          picture:
            "https://yanxuan-item.nosdn.127.net/221317c85274a188174352474b859d7b.jpg",
          name: "全防水HABU旋钮牛皮户外徒步鞋山宁泰抗菌",
          tag: "男鞋",
        },
        {
          id: 103,
          picture:
            "https://yanxuan-item.nosdn.127.net/cd4b840751ef4f7505c85004f0bebcb5.png",
          name: "毛茸茸小熊出没,儿童羊羔绒背心73-90cm",
          tag: "儿童服饰",
        },
        {
          id: 104,
          picture:
            "https://yanxuan-item.nosdn.127.net/56eb25a38d7a630e76a608a9360eec6b.jpg",
          name: "基础百搭,儿童套头针织毛衣1-9岁",
          tag: "儿童服饰",
        },
      ],
    };
  },
};
</script>
  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值