插槽,作用域插槽筆記

78 阅读11分钟
  • 组件创建, 注册和使用 - 伸手就来特别熟练
  • 指令的作用=》v-bind:属性名="变量" | v-model="变量"等操作DOM
  1. 组件进阶知识
  2. 自定义指令创建和使用
  3. tabbar案例

组件进阶-props校验

props校验

普通格式: props: ["propA", "propB"]。没有类型检查

高阶格式:

props: {
    // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
    propA: Number,
    // 多个可能的类型
    propB: [String, Number],
    // 必填的字符串
    propC: {
      type: String,
      required: true
    },
    // 带有默认值的数字
    propD: {
      type: Number,
      default: 100
    },
    // 带有默认值的对象
    propE: {
      type: Object,
      // 对象或数组默认值必须从一个工厂函数获取
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator: function (value) {
        // 这个值必须匹配下列字符串中的一个
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
  }

组件进阶 - 动态组件

目标效果

需求: 完成一个注册功能页面, 2个按钮切换, 额外封装两个组件:一个填写注册信息, 一个填写用户简介信息

目标: 多个组件使用同一个挂载点,并可以动态切换,这就是动态组件

目录结构

根组件
├── App.vue
└── components
    ├── UserName.vue # 用户名和密码输入框
    └── UserInfo.vue # 人生格言和自我介绍框

格式

<component :is="comName"></component>

操作

UserName.vue

<template>
  <div>
    <h2>UserName</h2>
    <p>用户名:<input /> </p>
    <p>密码:<textarea /> </p>
  </div>
</template>

<script>
export default {

}
</script>

在父组件App.vue中使用

<template>
  <div>
    <button @click="comName = 'UserName'">账号密码填写</button>
    <button @click="comName = 'UserInfo'">个人信息填写</button>

    <p>下面显示注册组件:</p>
    <div style="border: 1px solid red">
      <!-- vue内置的组件component, 可以动态显示组件 -->
      <component :is="comName"></component>
    </div>
  </div>
</template>

<script>
import UserName from "./UserName";
import UserInfo from "./UserInfo";
export default {
  data() {
    return {
      comName: "UserName",
    };
  },
  components: {
    UserName,
    UserInfo,
  },
};
</script>

注意

  • is只能是动态属性=》:is="组件注册后的标签名字符串或data变量"
  • 不能直接拿注册标签名赋值使用

小结

vue内置component组件, 配合is属性, 设置要显示的组件标签名字

组件进阶-keep-alive组件

目标

掌握组件缓存的使用

背景

组件切换会导致组件被频繁销毁和重新创建, 大多数情况下是有自己的意义的,但也可能会导致不必要的性能损耗

演示1: 可以先给UserName.vue和UserInfo.vue 注册created和destroyed生命周期事件, 观察创建和销毁过程

keep-alive

使用Vue内置的keep-alive组件, 可以让包裹的组件保存在内存中不被销毁

格式

演示2: 使用keep-alive内置的vue组件, 让动态组件缓存而不是销毁

补充生命周期:

  • activated - 激活
  • deactivated - 失去激活状态
<keep-alive>
    <!-- vue内置的组件component, 可以动态显示组件 -->
    <component :is="comName"></component>
</keep-alive>

小结

keep-alive可以提高组件的性能, 内部包裹的标签不会被销毁和重新创建, 触发激活和非激活的生命周期方法

组件进阶-keep-alive组件-指定缓存

目标

掌握keep-alive的include属性的用法

语法

  1. include="组件名1,组件名2..."
  2. :include="['组件名1', '组件名2']"
<keep-alive include="name1,name2">
    <!-- vue内置的组件component, 可以动态显示组件 -->
    <component :is="comName"></component>
</keep-alive>

注意:

  1. 匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称 (父组件 components 选项的键值)

组件进阶 - 默认插槽

目标

掌握组件插槽的使用

理解

生活中的插槽

vue中的插槽

  1. 组件通过插槽传入自定义结构
  2. 用于实现组件的内容分发, 通过 slot 标签, 可以接收到写在组件标签内的内容
  3. vue提供组件插槽能力, 允许开发者在封装组件时,把不确定的部分定义为插槽

格式

在定义组件时,在template中用slot来占个坑;

使用时,将组件之间的内容来填坑;

示例

组件进阶-具名插槽

目标

掌握具名插槽的使用

背景

当一个组件内有2处以上需要外部传入标签的地方

格式

定义:

使用:

  • <template #xxx>;

图示:

  1. 传入的标签可以分别派发给不同的slot位置
  2. v-slot一般跟template标签使用 (template是html5新出标签内容模板元素, 不会渲染到页面上, 一般被vue解析为内部标签)

示例

  1. 子组件-Pannel2.vue
<div class="container" v-show="isShow">
    <slot name="one"></slot>
    <slot name="two"></slot>
</div>

2 .父组件-UseSlot2.vue

v-slot可以简化成#使用

v-bind可以省略成:    

v-on: 可以省略成@  

v-slot: 可以简化成#

写法1:

<Pannel2>
    <template v-slot:one>
		<img src="../assets/mm.gif" alt="" />
    </template>
    <template v-slot:two>
		<span>我是文字哦</span>
    </template>
</Pannel2>

写法2:

<Pannel2>
    <!-- 简化写法 -->
    <template #one>
		<div>
            <p>寒雨连江夜入吴,</p>
            <p>平明送客楚山孤。</p>
            <p>洛阳亲友如相问,</p>
            <p>一片冰心在玉壶。</p>
        </div>
    </template>
    <template #two>
		<img src="../assets/mm.gif" alt="" />
    </template>
</Pannel2>

小结

  1. slot有可以设置多个
  2. 定义组件时:slot的name属性起插槽名
  3. 使用组件时, template配合#插槽名传入具体html标签或组件

插槽的案例-升级折叠面板

需求

允许面板中传入不同的内容

示例

改造之前的折叠面板案例, 想要实现不同内容显示, 我们把折叠面板里的Pannel组件, 添加组件插槽方式

参考代码

定义组件

<template>
  <div>
    <div class="title">
      <slot></slot>
      <span class="btn" @click="fn">
        {{isShow ? "收起" :"展开"}}
      </span>
    </div>
    <div class="container" v-show="isShow">
      <slot name="content"></slot>
    </div>
    <div>
      <slot name="footer"></slot>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      // 控制显示隐藏
      isShow: true
    }
  },
  methods: {
    fn(){
      console.log('fn')
      // 在之前的基础上,取反
      this.isShow = !this.isShow
    }
  }
}
</script>

<style scoped>
.title {
    display: flex;
    justify-content: space-between;
    align-items: center;
    border: 1px solid #ccc;
    padding: 0 1em;
  }
  .title h4 {
    line-height: 2;
    margin: 0;
  }
  .container {
    border: 1px solid #ccc;
    padding: 0 1em;
  }
  .btn {
    /* 鼠标改成手的形状 */
    cursor: pointer;
  }
</style>

app.vue - 使用组件

<template>
  <div id="app">
    <h3>44-插槽-升级版折叠面板</h3>
    <!-- <p v-for="item in 10" :key="item">p</p> -->
    <!-- <MyPlane v-for="item in 10" :key="item"/> -->
    <MyPlane>
      <h3>标题1-图片-文字</h3>
      <template #content>
        <p>dog!</p>
        <img style="width:100%" src="https://scpic.chinaz.net/files/pic/pic9/202003/zzpic23514.jpg" alt="">
      </template>
      <template #footer>
       <button @click="fn">点赞</button>
      </template>
    </MyPlane>
  </div>
</template>

<script>
import MyPlane from './MyPlane.vue'
export default {
  components: {
    MyPlane
  },
  methods: {
    fn(){
      alert('fn')
    }
  }
}
</script>

<style>
 body {
  background-color: #ccc;
 }
  #app {
    width: 400px;
    margin: 20px auto;
    background-color: #fff;
    border: 4px solid blueviolet;
    border-radius: 1em;
    box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.5);
    padding: 1em 2em 2em;
  }
  h3 {
    text-align: center;
  }
  
</style>

小结

组件内容分发技术, slot占位, 使用组件时传入替换slot位置的标签

组件进阶-作用域插槽(难点,不是重点)

目标

掌握作用域插槽的用法

目标: 子组件中的数据, 在给插槽赋值时在父组件环境下使用=> 子传父=》传数据

步骤

  • 创建子组件, 准备slot, 在slot上绑定属性和子组件值
  • 使用子组件, 传入自定义标签, 用template和v-slot="自定义变量名"
  • 自定义变量名会自动绑定slot上所有属性, 就可以使用子组件内值, 并替换slot位置

示例

具名插槽, 给slot绑定属性和值

<template>
  <div style="border:1px solid #ccc; margin:5px;padding:5px">
    <h2>子组件</h2>
    <!-- 给slot上补充自定义的属性 -->
    <slot name="content" :a="1" :b="2">
      默认内容
    </slot>
  </div>
</template>

<script>
export default {
}
</script>
  1. 父组件
<template>
  <div style="border:1px solid #ccc; margin:5px;padding:5px">
    <h1>45-插槽-作用域插槽</h1>


    <MyCom>
      <!-- 
        v-slot:插槽名="对象" 
      对象会自动接收这个插槽传递回来自定义属性
      -->
      <template v-slot:content="scope">
        <!-- <h1>自定义的内容,填坑, {{scope}}</h1> -->
        <h3>{{scope.a}}</h3>
        <p>{{scope.b}}</p>
      </template>
    </MyCom>
  </div>
</template>

<script>

// 父传子
//  1.传数据。 自定义属性    (父)  props(子)
//  2.传结构。 在组件中写内容(父)  slot (子)

import MyCom from './MyCom.vue'
export default {
  components: { MyCom }
}
</script>

<style scoped>
.content{
background-color: #ccc;
}
</style>

小结

组件内变量绑定在slot上, 然后使用组件v-slot:插槽名字="变量" ,变量上就会绑定slot传递的属性和值

组件进阶 - 作用域插槽使用场景

目标

了解作用域插槽使用场景, 自定义组件内标签+内容

案例

案例: 封装一个表格组件, 在表格组件内循环产生单元格

准备示例数据:

{
            name: "小传同学",
            age: 18,
            headImgUrl:
            "https://www.escook.cn/vuebase/pics/1.png",
          },
          {
            name: "小黑同学",
            age: 25,
            headImgUrl:
            "https://www.escook.cn/vuebase/pics/2.png",
          },
          {
            name: "智慧同学",
            age: 21,
            headImgUrl:
            "https://www.escook.cn/vuebase/pics/3.png",
          }
]
  1. 封装MyTable.vue
<template>
  <table>
    <tr>
      <th>序号</th>
      <th>名字</th>
      <th>年龄</th>
      <th>头像</th>
    </tr>
    <tr v-for="(item,idx) in list" :key="idx">
      <td>{{idx+1}}</td>
      <td>{{item.name}}</td>
      <td>{{item.age}}</td>
      <td>
        <!-- slot name具名插槽 
        slot有自定义的属性,向slot传递数据,这个数据在父组件使用插槽时,能自动接收
        -->
        <slot name="img" :girl="item" :loveName="item.name" :img="item.headImgUrl">
          <!-- 默认内容 -->
          <img :src="item.headImgUrl"/>
        </slot>
      </td>
    </tr>
  </table>
</template>

<script>
export default {
  props: {
    list: {
      type: Array,
      required: true
    }
  }
}
</script>
  1. UseTable.vue - 准备数据, 传入给MyTable.vue组件里循环使用
<template>
  <div style="border:1px solid #ccc; margin:5px;padding:5px">
    <h1>46-插槽-作用域插槽-场景</h1>
    <MyTable :list="list">
      <!-- scope是一个变量名,自动接收slot上的自定义属性 -->
      <template v-slot:img="scope">
        <img style="display:block;border-radius:50%" :src="scope.img" />
        <button @click="like(scope)">点赞</button>
        <button>加好友</button>
      </template>
    </MyTable>
  </div>
</template>

<script>

import MyTable from './MyTable.vue'
export default {
  data(){
    return {
      list: [
          {
            name: "小传同学",
            age: 18,
            headImgUrl:
            "https://www.escook.cn/vuebase/pics/1.png",
          },
          {
            name: "小黑同学",
            age: 25,
            headImgUrl:
            "https://www.escook.cn/vuebase/pics/2.png",
          },
          {
            name: "智慧同学",
            age: 21,
            headImgUrl:
            "https://www.escook.cn/vuebase/pics/3.png",
          }
        ]
    }
  },
  methods: {
    like(obj){
      console.log(obj)
    }
  },
  components: { MyTable }
}
</script>

总结: 插槽可以自定义显示内容; 作用域插槽可以把组件内的值取出来自定义显示内容

小结

  • props允许父向子传入数据
  • 插槽允许父向子传入结构
  • 作用域插槽允许父向子传入自定义的数据+结构

自定义指令-基本使用

自定义指令文档(了解)

除了核心功能默认内置的指令 (v-modelv-show)等,Vue 也允许注册自定义指令=》 v-xxx

  1. html+css+js的复用的主要形式是组件
  2. 你需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令

作用

扩展标签额外的功能

自定义指令-定义方式

{
  data(){},
  methods: {},
  directives: {
    focus: { // 自定义指令名
        inserted(el){ // 固定配置项 - 当指令插入到标签自动触发此函数
            el.focus()
        }
    },
  },
}

示例 自动获取焦点

<template>
  <div>
    <input type="text" v-focus />
  </div>
</template>

<script>
export default {
  // 注册
  directives: {
    focus: { // 自定义指令名
        inserted(el){ // 固定配置项 - 当指令插入到标签自动触发此函数
            el.focus()
        }
    }
  }
}
</script>

自定义指令-传值和更新

目标: 使用自定义指令, 传入一个值

需求: 定义color指令-传入一个颜色, 给标签设置文字颜色

main.js定义处修改一下

directives: {
  "color":{
    inserted(el, binding){ // 插入时触发此函数
      el.style.color = binding.value;
    },
    update(el, binding){ // 更新绑定的变量时触发此函数=》手动更新
      el.style.color = binding.value;
    }
  }
}

Direct.vue处更改一下

<p v-color="theColor" @click="changeColor">使用v-color指令控制颜色, 点击变蓝</p>

<script>
  data() {
    return {
      theColor: "red",
    };
  },
  methods: {
    changeColor() {
      this.theColor = 'blue';
    },
  },
</script>

总结: v-xxx, 自定义指令, 获取原生DOM, 自定义操作

案例-tabbar

需求分析和效果演示

知识点:

  • 组件封装
  • 动态组件
  • keep-alive
  • 作用域插槽

案例-tabbar-创建项目

目标: 创建和初始化项目, 下载bootstrap, axios

vue create tabbar-demo
npm i bootstrap axios
npm i less less-loader -D
  1. 删除不相关文件
  2. 在main.js中引入bootStrap.css和案例字体图标样式

说明:字体图标文件在04-资料/tabBar案例图标/fonts,拷贝到项目assets目录下

import 'bootstrap/dist/css/bootstrap.min.css'
import './assets/fonts/iconfont.css'

案例-tabbar-组件拆分和创建

目标:根据需求进行组件拆分和创建

注意:商品列表页面,列表抽离为公共的table组件开发

根目录
├── components
│   ├── MyTable.vue  # 公共的表格组件
│   ├── MyTabBar.vue # 页面的底部
│   └── MyHeader.vue # 页面的头部
├── pages               
│   ├── MyGoodsList.vue    # 三个页面1
│   ├── MyGoodsSearch.vue  # 三个页面2
│   └── MyUserInfo.vue     # 三个页面3
└── App.vue # 根组件

创建如下文件:

  1. MyHeader.vue
<template>
  <div class="my-header">
    拼刀刀
  </div>
</template>

<script>
export default {
}
</script>

<style lang="less" scoped>
.my-header {
  height: 50px;
  line-height: 50px;
  text-align: center;
  font-size: 16px;
  background-color: red;
  color: #fff;
  position: fixed;
  left: 0;
  top: 0;
  width: 100%;
}
</style>
  1. MyTabBar.vue
<template>
  <div class="my-tab-bar">
  	<div class="tab-item" v-for="item in 3" :key="item">
      <!-- 图标 -->
      <span class="iconfont icon-shangpinliebiao"></span>
      <!-- 文字 -->
      <span>商品{{item}}</span>
    </div>
  </div>
</template>

<script>
export default {
}
</script>

<style lang="less" scoped>
.my-tab-bar {
  position: fixed;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 50px;
  border-top: 1px solid #ccc;
  display: flex;
  justify-content: space-around;
  align-items: center;
  background-color: white;
  .tab-item {
    display: flex;
    flex-direction: column;
    align-items: center;
  }
}
</style>
  1. components/MyTable.vue(可复用=》能自定义表格显示的数据)
<template>
  <table class="table table-bordered table-stripped">
    <!-- 表格标题区域 -->
    <thead>
      <tr>
        <th>#</th>
        <th>商品名称</th>
        <th>价格</th>
        <th>标签</th>
        <th>操作</th>
      </tr>
    </thead>
    <!-- 表格主体区域 -->
    <tbody>
      <tr >
        <td>1</td>
        <td>商品</td>
        <td>998</td>
        <td>xxx</td>
        <td><button class="btn btn-danger btn-sm">删除</button></td>
      </tr>
    </tbody>
  </table>
</template>

<script>
export default {
}
</script>
  1. MyGoodsList.vue导入复用的MyTable组件,注册使用
  1. MyGoodsSearch.vue和MyUserInfo.vue做切换练习使用,无功能
  2. 导入创建的组件到App.vue中注册使用

总结:根据需求准备页面组件

案例-tabbar-头部封装

目标: 完成头部导航自定义标题和背景色功能

使用技术:父传子=》props自定义组件显示

  1. 允许用户自定义  title 标题
  2. 允许用户自定义  bgcolor 背景色

MyHeader.vue

<template>
  <div class="my-header" :style="{ backgroundColor: bgcolor }">
    {{ title }}
  </div>
</template>

<script>
export default {
  props: {
    title: {
      type: String,
      default: 'tabbar 案例'
    },
    bgcolor: {
      type: String,
      default: '#1d7bff'
    }
  }
}
</script>

<style lang="less" scoped>
.my-header {
  height: 50px;
  line-height: 50px;
  text-align: center;
  font-size: 16px;
  background-color: red;
  color: #fff;
  position: fixed;
  left: 0;
  top: 0;
  width: 100%;
}
</style>

案例-tabbar-底部封装

目标: 实现MyTabBar.vue组件功能

  1. 通过 tabs 的 props 属性,为 tabbar 组件指定数据源
  2. 每个 tabbar 的 item 配置项,至少要包含如下两个属性:
    • icon -> 要展示的图标
    • text -> 显示的文本
  1. 从App.vue给MyTabBar.vue传入底部导航的数据

MyTabBar.vue

<template>
  <div class="my-tab-bar">
  	<div class="tab-item" v-for="item in tabs" :key="item.text">
      <!-- 图标 -->
      <span class="iconfont" :class="item.icon"></span>
      <!-- 文字 -->
      <span>{{ item.text }}</span>
    </div>
  </div>
</template>

<script>
export default {
  name: "MyTabBar",
  props: {
    tabs: {
      type: Array,
      default:()=>[],
      required: true,
    },
  },
};
</script>

<style lang="less" scoped>
.my-tab-bar {
  position: fixed;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 50px;
  border-top: 1px solid #ccc;
  display: flex;
  justify-content: space-around;
  align-items: center;
  background-color: white;
  .tab-item {
    display: flex;
    flex-direction: column;
    align-items: center;
  }
}
</style>

App.vue

<MyTabBar :tabs="tabList"></MyTabBar>

<script>
import MyTabBar from "@/components/MyTabBar"
export default {
	components: {
		MyHeader,
		MyTabBar
    },
    data() {
        return {
          tabList: [
            {
              icon: "icon-shangpinliebiao",
              text: "商品列表",
            },
            {
              icon: "icon-sousuo",
              text: "商品搜索",
            },
            {
              icon: "icon-user",
              text: "我的信息",
            },
          ],
        };
     },
}
</script>

案例-tabbar-底部高亮

目标

点击底部导航实现高亮效果

思路

步骤

  1. 绑定点击事件, 获取点击的索引
  2. 给循环的标签设置动态class, 当遍历的索引, 和点击保存的索引比较, 相同则高亮

代码

MyTabBar.vue里定义

<template>
  <div class="my-tab-bar">
    <div
      class="tab-item"
      v-for="(item, index) in tabs"
      :key="item.text"
+      :class="{ current: activeIndex === index }"
+      @click="tabClickFn(index)"
    >
      <!-- 图标 -->
      <span class="iconfont" :class="item.icon"></span>
      <!-- 文字 -->
      <span>{{ item.text }}</span>
    </div>
  </div>
</template>

<script>
import "../assets/fonts/iconfont.css";
export default {
  name: "MyTabBar",
  data() {
    return {
+      activeIndex: 0,
    };
  },
  methods: {
    tabClickFn(ind) {
+      this.activeIndex = ind;
    },
  },
  // 省略props
};
</script>

<style lang="less" scoped>
/* 省略了代码... */

+ .current {
  color: #1d7bff;
}
</style>

案例-tabbar-组件切换

目的: 点击底部导航, 切换页面组件显示

思路: 现在还没学路由, 所以, 使用动态组件切换页面

步骤:

  1. 在App.vue - 引入所有需要切换的组件-3个页面
  2. 给底部导航数据每个对象添加属性name保存组件标签名字
  3. tabbar点击后, 把应该切换的组件名再传递出来, 赋予给is属性, 实现组件切换

父组件-App.vue - 引入并注册

<template>
  <div>
    <MyHeader />
    <div class="main">
      <keep-alive>
+        <components :is="isCom"></components>
      </keep-alive>
    </div>
+    <MyTabBar :tabs="tabList" @changeCom="changeFn"></MyTabBar>
  </div>
</template>

<script>
import MyHeader from "@/components/MyHeader";
import MyTabBar from "@/components/MyTabBar";
import MyGoodsList from "@/components/MyGoodsList";
import MyGoodsSearch from "@/components/MyGoodsSearch";
import MyUserInfo from "@/components/MyUserInfo";
export default {
  components: {
    MyHeader,
    MyTabBar,
    MyGoodsList,
    MyGoodsSearch,
    MyUserInfo,
  },
  methods: {
    changeFn(ind) {
+      this.isCom = this.tabList[ind].name;
    },
  },
  data() {
    return {
+      isCom: "MyGoodsList", // 页面默认显示的
      tabList: [
        {
          icon: "icon-shangpinliebiao",
          text: "商品列表",
+          name: "MyGoodsList",
        },
        {
          icon: "icon-sousuo",
          text: "商品搜索",
+          name: "MyGoodsSearch",
        },
        {
          icon: "icon-user",
          text: "我的信息",
+          name: "MyUserInfo",
        },
      ],
    };
  },
};
</script>

<style scoped>
.main {
  padding: 50px 0;
}
</style>

子组件-MyTabBar.vue - 点击传递过来索引, 把组件名字替换到动态组件位置显示

methods: {
    tabClickFn(ind) {
      this.activeIndex = ind;
+      this.$emit("changeCom", ind);
    },
  },

案例-tabbar-商品列表-请求数据

目标: 使用axios请求数据, 传入表格组件渲染

main.js - 注册axios配置默认地址

import axios from 'axios'

axios.defaults.baseURL = "https://www.escook.cn"
Vue.prototype.$axios = axios

MyGoodsList.vue - 使用axios请求数据, 把数据传入给MyTable.vue里循环铺设

<template>
  <div>
    <MyTable :data="goodsList">
    </MyTable>
  </div>
</template>

<script>
import MyTable from "./MyTable";
export default {
  data() {
    return {
      goodsList: [],
    };
  },
  components: {
    MyTable,
  },
  methods: {
    // 初始化商品的数据
    async getGoodsList() {
      // 发送ajax请求
      const res = await this.$axios.get("/api/goods");
      const { status, data } = res.data;
      if (status !== 0) return console.log("获取商品列表失败");
      // 请求成功
      this.goodsList = data;
    }
  },
  created() {
    this.getGoodsList();
  },
};
</script>

案例-tabbar-商品列表-自定义表格-插槽技术

目标: 使用插槽技术, 给MyTable.vue组件, 自定义列标题, 自定义表格内容

步骤:

  1. 提高组件复用性, 把表格列标题thead部分预留标签, 设置name属性
  2. 使用MyTable.vue时, 传入自定义的列标题标签
  3. 表格内容td部分也可以让组件使用者自定义, 也给tbody下tr内留好标签和name属性名

子组件-MyTable.vue - 留好具名插槽

<template>
  <table class="table table-bordered table-stripped">
    <!-- 表格标题区域 -->
    <thead>
      <tr>
+        <slot name="header"></slot>
      </tr>
    </thead>
    <!-- 表格主体区域 -->
    <tbody>
      <tr v-for="(item, i) in data" :key="item.id">
+        <slot name="body"></slot>
      </tr>
    </tbody>
  </table>
</template>

<script>
export default {
  name: 'MyTable',
  props: {
    data: {
      type: Array,
      default: () => []
    }
  }
}
</script>

父组件-MyGoodsList.vue 使用

<template>
  <div>
    <MyTable :data="goodsList">
      <template #header>
        <th>#</th>
        <th>商品名称</th>
        <th>价格</th>
        <th>标签</th>
        <th>操作</th>
      </template>

      <template #body>
        <td>1</td>
        <td>xxx</td>
        <td>¥ 99.00</td>
        <td>xxx</td>
        <td>
          <button class="btn btn-danger btn-sm">删除</button>
        </td>
      </template>
    </MyTable>
  </div>
</template>

案例-tabbar-商品列表-作用域插槽

目标: 在传入标签时, 要使用对应组件内的数据

思路:

  1. 子MyTable.vue里在slot上绑定动态属性和值
  2. 父-MyGoodsList.vue在template上声明变量接收
  3. 插槽td标签上使用MyTable.vue内的数据

MyTable.vue

<tbody>
    <tr v-for="(item, index) in data" :key="item.id">
+        <slot name="body" :row="item" :index="index"></slot>
    </tr>
</tbody>

MyGoodsList.vue

<template #body="scope">
    <td>{{ scope.index + 1 }}</td>
    <td>{{ scope.row.goods_name }}</td>
    <td>¥ {{ scope.row.goods_price }}</td>
    <td>{{ scope.row.tags }}</td>
    <td>
    	<button class="btn btn-danger btn-sm">删除</button>
    </td>
</template>

案例-tabbar-商品列表-tags渲染

目标: 把单元格里的标签, tags徽章铺设下

bootstrap徽章: v4.bootcss.com/docs/compon…

MyGoodsList.vue - 插槽

<span v-for="tag in row.tags" :key="tag" class="badge btn-danger">{{ tag }}</span>

下面额外添加样式

<style lang="less" scoped>
.my-goods-list {
  .badge {
    margin-right: 5px;
  }
}
</style>

案例-tabbar-商品列表-删除功能

目标: 点击删除对应这条数据

  1. 父-MyGoodsList.vue - 注册点击事件
<button class="btn btn-danger btn-sm" @click="delFn(scope.index)">删除</button>
  1. 父-MyGoodsList.vue - 根据 id 删除
delFn(index){
    this.goodsList.splice(index, 1)
}
  1. 动态组件的使用步骤
  2. 组件缓存使用步骤和作用
  3. 组件插槽用法
    传递html结构内容
    • 默认插槽 =》1. 子组件标签下不使用template写html结构 2. 子组件通过
    • 具名插槽 => 1. 子组件标签下使用template =》包裹传递html结构 =》template上提供名字#插槽名 2. 子组件通过

传递数据=》子传父

    • 作用域插槽=> 1. 子组件<slot name="插槽名" :传递数据的名字="变量"> 2. 父组件=》<template #插槽名="接收数据对象">

插槽使用的目的:
使复用组件的内容结构和数据动态化=》增强组件的可复用性

  1. 自定义指令如何使用

指令作用:操作DOM元素

几种方式?

  1. 全局 =》Vue.directive('指令名', { inserted (el, bind)  { 操作DOM } })
  2. 局部=》在组件对象 { directives: { 指令名: {指令逻辑}  }  }
<p v-指令名="data"></p>