一、组件的组成部分

三大组成部分

1
2
3
4
5
6
7
8
9
10
11
<template>

</template>

<script>
export default {

}
</script>
<style scoped>
</style>

二、scoped解决样式冲突

默认情况:

写在组件中的样式会 全局生效 → 因此很容易造成多个组件之间的样式冲突问题。

  • 全局样式: 默认组件中的样式会作用到全局,任何一个组件中都会受到此样式的影响
  • 局部样式: 可以给组件加上scoped 属性,可以让样式只作用于当前组件
1
2
3
4
5
6
7
8
9
10
11
<template>

</template>

<script>
export default {

}
</script>
<style scoped>
</style>

scoped原理

通过上述的<style scoped>就可以使

  1. 当前组件内标签都被添加data-v-hash值 的属性
  2. css选择器都被添加 [data-v-hash值] 的属性选择器

最终效果: 必须是当前组件的元素, 才会有这个自定义属性, 才会被这个样式作用到
image.png

data必须是一个函数

一个组件的 data 选项必须是一个函数。目的是为了:保证每个组件实例,维护独立的一份数据对象。
每次创建新的组件实例,都会新执行一次data 函数,得到一个新对象。

1
2
3
4
5
6
7
8
9
<script>
export default {
data: function () {
return {
count: 100,
}
},
}
</script>

组件通信

什么是组件通信 ?

组件通信,就是指组件与组件之间的数据传递

  • 组件的数据是独立的,无法直接访问其他组件的数据。
  • 想使用其他组件的数据,就需要组件通信

如何通信及其组件之间的关系分类

父子关系
非父子关系
image.png

父组件通过 props 将数据传递给子组件
子组件利用 $emit 通知父组件修改更新

父向子通信代码示例

父组件通过props将数据传递给子组件
父组件App.vue
image.png

1
//:title="msg"  表示动态赋予属性

父向子传值步骤

  1. 给子组件以添加属性的方式传值
  2. 子组件内部通过props接收
  3. 模板中直接使用 props接收的值

image.png

子传父通信代码示例

子向父传值步骤

  1. $emit触发事件,给父组件发送消息通知
  2. 父组件监听$emit触发的事件
  3. 提供处理函数,在函数的性参中获取传过来的参数
    image.png

props

定义

组件上 注册的一些 自定义属性, 我们可以使用props属性来向子组件传递数据
两个特点:

  1. 可以 传递 任意数量 的prop
  2. 可以 传递 任意类型 的prop

image.png
案例:
Main.vue为我们自己定义的父组件, UserInfo为自定义的子组件 , 通过props就可以实现组件之间的数据传递
image.png

props校验

我们使用组件的props属性, 但是数据不能乱传, 所以就需要使用props来校验数据
为组件的 prop 指定验证要求,不符合要求,控制台就会有错误提示 → 帮助开发者,快速发现错误

语法

类型: 类型校验、非空校验、默认值、自定义校验

1
2
3
props: {
校验属性名:类型
}

完整的校验写法

1
2
3
4
5
6
7
8
9
10
11
props: {
校验的属性名: {
type: 类型, // Number String Boolean ...
required: true, // 是否必填
default: 默认值, // 默认值
validator (value) {
// 自定义校验逻辑
return 是否通过校验
}
}
},

代码实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script>
export default {
// 完整写法(类型、默认值、非空、自定义校验)
props: {
w: {
type: Number,
//required: true,
default: 0,
// 校验逻辑
validator(val) {
// console.log(val)
if (val >= 100 || val <= 0) {
console.error('传入的范围必须是0-100之间')
return false
} else {
return true
}
},
},
},
}
</script>

注意事项:

  1. default和required一般不同时写(因为当时必填项时,肯定是有值的)
  2. default后面如果是简单类型的值,可以直接写默认。如果是复杂类型的值,则需要以函数的形式return一个默认值

props和data、 单向数据流

1.共同点

都可以给组件提供数据

2.区别

  • data 的数据是自己的 —> 随便改
  • prop 的数据是外部的 —> 不能直接改,要遵循 单向数据流

单向数据流

父级props 的数据更新,会向下流动,影响子组件。这个数据流动是单向的

实现案例:
image.png

综合案例

组件之间数据的传输

代码地址: https://github.com/Ray2310/vue-demo
实现功能:

  • 拆分基础组件
  • 渲染待办任务
  • 添加任务
  • 删除任务
  • 底部合计 和 清空功能
  • 持久化存储

以组件TodoMain.vue(子组件)和组件App.vue(父组件) 为例 讲解父子数据传输的问题。

父传子 的 数据传输实现

  1. 在父亲组件中提供数据data并返回
  2. 在使用组件的template区域, 通过使用:list="list"来实现可以在子组件中接受数据
  3. 在子组件中通过使用props实现父亲组件传递内容的接收。
  4. 最后就可以在子组件的template中使用list中的数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template>
<!-- 列表区域 -->
<section class="main">
<ul class="todo-list">
<li class="todo" v-for="(item, index) in list" :key="item.id" >
<div class="view" >
<span class="index">{{ index + 1 }}</span> <label>{{ item.name }}</label>
<button class="destroy"></button>
</div>
</li>
</ul>
</section>
</template>

<script>
export default {
// 使用对象的写法 ,接收父组件中传入的内容
props: {
list: Array
}
}

</script>

<style></style>

App.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<template>
<!-- 主体区域 -->
<section id="app">
<TodoHeader></TodoHeader>
<!-- 通过在这里写list, 就可以实现在子组件中作接收 -->
<TodoMain :list="list"></TodoMain>
<TodoFooter></TodoFooter>

</section>
</template>

<script>
// 导入组件
import TodoHeader from './components/TodoHeader.vue'
import TodoMain from './components/TodoMain.vue'
import TodoFooter from './components/TodoFooter.vue'

export default {
// 注册组件
components: {
TodoHeader,
TodoMain,
TodoFooter
},

//TODO: 数据提供需要提供在父组件中, 这样如果我们想要使用, 直接使用props传递即可。
data () {
return {
list: [
{id: 1, name: 'eat'},
{id: 2, name: 'play'},
{id: 3, name: 'write'}
]

}
}
}
</script>
<style>
</style>

子传父 的 数据传输实现

TodoHeader.vue 子组件向App.vue父子 传输添加的数据

  1. 在子组件中通过v-model实现数据收集并通过点击事件或回车 进行数据发送
  2. 然后通过this.$emit('addItem', this.name) 实现给父组件发送消息通知
  3. 父组件监听$emit触发的事件, 通过 @addItem="add", 并且通过add()函数接收数据
  4. 最后在函数中实现数据的update操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<template>  
<!-- 输入框 -->
<header class="header">
<h1>记事本</h1>
<input placeholder="请输入任务" @keyup.enter="addItem()" v-model="name" class="new-todo" />
<button class="add" @keyup.enter="addItem()" @click="addItem()">添加任务</button>
</header>

</template>
<!--
这里实现数据的 子传父
完成添加功能
1. 收集表单数据 v-model
2. 监听时间 (回车+点击 都要进行添加)
3. 子传父,将任务名称传递给父组件App.vue
4. 父组件接受到数据后 进行添加 unshift(自己的数据自己负责)
-->

<script>
export default {
data(){
return {
name:''
}
},
methods: {
addItem(){
// console.log(this.name);
//传递给父亲组件
if(this.name === "" ) {
return
}
this.$emit('addItem', this.name)
//清空表单
this.name = ''
}
}
}
</script>
<style>
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<template>
<!-- 主体区域 -->
<section id="app">
<TodoHeader @addItem="add"></TodoHeader>
<!-- 通过在这里写list, 就可以实现在子组件中作接收 -->
<TodoMain @deleteItem="deleteItem" :list="list"></TodoMain>
<TodoFooter @clear="clear" :list="list"></TodoFooter>

</section>
</template>

<script>
// 导入组件
import TodoHeader from './components/TodoHeader.vue'
import TodoMain from './components/TodoMain.vue'
import TodoFooter from './components/TodoFooter.vue'

export default {
// 注册组件
components: {
TodoHeader,
TodoMain,
TodoFooter
},

//TODO: 数据提供需要提供在父组件中, 这样如果我们想要使用, 直接使用props传递即可。
data () {
return {
list: [
{id: 1, name: 'eat'},
{id: 2, name: 'play'},
{id: 3, name: 'write'}
]
}
},
methods: {
add(newName){
console.log(newName)
//使用unshift添加
this.list.unshift({
id: +new Date(), //通过时间戳来实现id的唯一性
name: newName
})
//还需要作的是清空表单
},
deleteItem(id) {
console.log(id)
//通过filter过滤器实现删除操作
this.list = this.list.filter(item => item.id !== id)
},
//清空任务栏
clear(){
this.list = []
}
}
}
</script>
<!--
TODO:
拆分基础组件
渲染待办任务
添加任务
删除任务
底部合计 和 清空功能
持久化存储
-->
<style>
</style>

非父子之间的数据通信—event bus事件总线

非父子组件之间,进行简易消息传递。(复杂场景→ Vuex)
发送通知不是一个一对一的关系, 但凡有人接收, 那么就都可以接受发送的内容

步骤:

在工具包utils

  1. 创建一个都能访问的事件总线 (空Vue实例)
1
2
3
import Vue from 'vue'
const Bus = new Vue()
export default Bus
  1. A组件(接受方),监听Bus的 $on事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
import Bus form './utils/EventBus'
export default {
created () {
Bus.$on('sendMsg', (msg) => {
this.msg = msg
})
},
data: {
return {
msg: ''
}
}
}
</script>
  1. B组件(发送方),触发Bus的$emit事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<button #click="clickSend"></button>

</template>
<script>
import Bus form './utils/EventBus'
export default {
methods: {
clickSend(){
Bus.$emit('sendMsg', '这是一个消息')
}
}
}
</script>


image.png

非父子通信-provide&inject

跨层级共享数据
从祖辈层到后辈层(例如:爷爷到孙子)
image.png

语法 :

  1. 父组件 provide提供数据
1
2
3
4
5
6
7
8
9
10
export default {
provide () {
return {
// 普通类型【非响应式】
color: this.color,
// 复杂类型【响应式】
userInfo: this.userInfo,
}
}
}
  1. 子/孙组件 inject获取数据
1
2
3
4
5
6
export default {
inject: ['color','userInfo'],
created () {
console.log(this.color, this.userInfo)
}
}

4.注意

  • provide提供的简单类型的数据不是响应式的,复杂类型数据是响应式。(推荐提供复杂类型数据)
  • 子/孙组件通过inject获取的数据,不能在自身组件内修改

v-model实现表单类组件的封装

**实现子组件和父组件数据的双向绑定 (实现App.vue中的selectId和子组件选中的数据进行双向绑定) **

v-model本质上实现的是双向绑定,而:value这中的是单向绑定, 但是我们子组件是不允许修改父组件的内容的, 所以如果直接使用v-model就会报错, 需要修改。

v-model其实就是 :value和@input事件的简写

  • 子组件:props通过value接收数据,事件触发 input
  • 父组件:v-model直接绑定数据

image.png
子组件

1
2
3
4
5
6
7
8
9
<select :value="value" @change="handleChange">...</select>
props: {
value: String
},
methods: {
handleChange (e) {
this.$emit('input', e.target.value)
}
}

父组件

1
<BaseSelect v-model="selectId"></BaseSelect>

.sync修饰符(vue3.x已排除)

:visible就是控制显示隐藏的
update是固定的。
image.png

ref 和 $refs (常用** 获取 dom 元素 **)

作用: 利用ref 和 $refs 可以用于 获取 dom 元素 或 组件实例

image.png

语法

  1. 给要获取的盒子添加ref属性
1
<div ref="chartRef">我是渲染图表的容器</div>
  1. 获取时通过 $refs获取 this.$refs.chartRef 获取
1
2
3
mounted () {
console.log(this.$refs.chartRef)
}

image.png

vue异步更新、$nextTick

需求

编辑标题, 编辑框自动聚焦

  1. 点击编辑,显示编辑框
  2. 让编辑框,立刻获取焦点

image.png“显示之后”,立刻获取焦点是不能成功的!
原因:Vue 是异步更新DOM (提升性能)

解决方案

$nextTick:等 DOM更新后,才会触发执行此方法里的函数体
**语法: **this.$nextTick(函数体)

1
2
3
this.$nextTick(() => {
this.$refs.inp.focus()
})

注意:$nextTick 内的函数体 一定是箭头函数,这样才能让函数内部的this指向Vue实例

自定义指令

  • 内置指令:v-html、v-if、v-bind、v-on… 这都是Vue给咱们内置的一些指令,可以直接使用
  • 自定义指令:同时Vue也支持让开发者,自己注册一些指令。这些指令被称为自定义指令每个指令都有自己各自独立的功能

概念:自己定义的指令,可以封装一些DOM操作,扩展额外的功能

案例, 通过自定义指令, 可以封装一些dom操作, 扩展额外的功能, 实现项目中的所有获取dom的操作都可以使用我们的自定义指令来实现获取dom

基本语法

  • 全局注册
1
2
3
4
5
6
7
//在main.js中
Vue.directive('指令名', {
"inserted" (el) {
// 可以对 el 标签,扩展额外功能
el.focus()
}
})
  • 局部注册
1
2
3
4
5
6
7
8
9
//在Vue组件的配置项中
directives: {
"指令名": {
inserted () {
// 可以对 el 标签,扩展额外功能
el.focus()
}
}
}
  • 使用指令语法: v-指令名

注意事项

  • 注意:在使用指令的时候,一定要先注册再使用,否则会报错
    使用指令语法: v-指令名。如:注册指令时不用v-前缀,但使用时一定要加v-前缀

指令的值

需求: 实现一个 color 指令 - 传入不同的颜色, 给标签设置文字颜色

语法:

  1. 绑定指令时,可以通过“等号”的形式为指令 绑定 具体的参数值
1
<div v-color="color">我是内容</div>
  1. 通过 binding.value 可以拿到指令值,指令值修改会 触发 update 函数
1
2
3
4
5
6
7
8
9
10
directives: {
color: {
inserted (el, binding) {
el.style.color = binding.value
},
update (el, binding) {
el.style.color = binding.value
}
}
}

实现案例需求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<template>

<!-- 类名·最好和当前组件名同名 -->
<div>
<!--显示红色-->
<h2 v-color="color1">指令的值1测试</h2>
<!--显示蓝色-->
<h2 v-color="color2">指令的值2测试</h2>
<button @click="color1 = 'blue'">
改变第一个h1的颜色
</button>
</div>

</template>
<script>
//导入组件

export default {
data () {
return {
color1: 'red',
color2: 'blue'
}
},

methods: {


},

// mounted (){
// let input = this.$refs.inp.focus()
// console.log("input"+ input)
// },

// 注册组件 (对于导入的组件名和名称一样时, 我们可以直接使用)
components: {

}
,

directives: {
color: {
inserted (el, binding) {
// 可以对 el 标签,扩展额外功能
//这个el 就相当于是document.querySelecter('color')的意思
el.style.color = binding.value // 可以获取对应的data中的数据 就是我们指令的值color2 ----》 <h2 v-color="color1">指令的值1测试</h2>
el.focus()

},
// update 指令的值修改的时候触发, 提供值变化后, dom的更新逻辑
update (el , binding){
console.log('update 指令的val')
el.style.color = binding.value
}
}
}
}
</script>

<style>


</style>

v-loading指令封装

需求: 实际开发过程中,发送请求需要时间,在请求的数据未回来时,页面会处于空白状态 => 用户体验不好
封装一个 v-loading 指令,实现加载中的效果

类似于这样
image.png

分析

  1. 本质 loading效果就是一个蒙层,盖在了盒子上
  2. 数据请求中,开启loading状态,添加蒙层
  3. 数据请求完毕,关闭loading状态,移除蒙层

实现

  1. 准备一个 loading类,通过伪元素定位,设置宽高,实现蒙层
  2. 开启关闭 loading状态(添加移除蒙层),本质只需要添加移除类即可
  3. 结合自定义指令的语法进行封装复用
1
2
3
4
5
6
7
8
9
10
11
<style>
.loading:before {
content: "";
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: #fff url("./loading.gif") no-repeat center;
}
</style>

插槽

**作用: **让组件内部的一些 结构 支持 自定义
image.png

需求: 将需要多次显示的对话框,封装成一个组件

插槽的基本语法

  1. 组件内需要定制的结构部分,改用****占位
  2. 使用组件时, ****标签内部, 传入结构替换slot
  3. 给插槽传入内容时,可以传入纯文本、html标签、组件

默认插槽

如果想要修改其中的内容该怎么做呢 ?
image.png
封装组件时,可以为预留的 插槽提供后备内容(默认内容)
自定义的组件MyDialog.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div class="dialog">
<div class="dialog-header">
<h3>友情提示</h3>
<span class="close">✖️</span>
</div>

<div class="dialog-content">
<!-- 通过slot插槽来进行占位, 然后就可以在App中进行自定义传输 -->
<slot></slot>
</div>
<div class="dialog-footer">
<button>取消</button>
<button>确认</button>
</div>
</div>
</template>

如果想要实现组件的占位操作:
App.vue中 即可通过自定义内容来实现

1
2
3
<Mydialog>
<p>are you shuer</p>
</Mydialog>

image.png

后被内容

但是这样的操作 ,如果我们不传输内容, 那么就会显示为空。 这样明显不符合要求, 所以我们需要给slot来实现给其中的内容进行赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<div class="dialog">
<div class="dialog-header">
<h3>友情提示</h3>
<span class="close">✖️</span>
</div>

<div class="dialog-content">
<!-- 通过slot插槽来进行占位, 然后就可以在App中进行自定义传输 -->
<slot>这是后被内容</slot>
</div>
<div class="dialog-footer">
<button>取消</button>
<button>确认</button>
</div>
</div>

image.png
image.png

具名插槽

image.png
一个组件内有多处结构,需要外部传入标签,进行定制
上面的弹框中有三处不同,但是默认插槽只能定制一个位置,这时候怎么办呢?

具名插槽的使用

通过在slot标签中使用 name属性来进行区分不同的插槽, 然后在需要使用的组件中, 通过v-slot:name属性value来进行赋值。 从而达到多个弹框出现多个值的情况

image.png

作用域插槽

插槽分类

  • 默认插槽
  • 具名插槽

插槽只有两种,作用域插槽不属于插槽的一种分类

作用

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

使用步骤

  1. 给 slot 标签, 以 添加属性的方式传值
1
<slot :id="item.id" msg="测试文本"></slot>
  1. 所有添加的属性, 都会被收集到一个对象中
1
{ id: 3, msg: '测试文本' }
  1. 在template中, 通过 #插槽名= “obj” 接收,默认插槽名为 default
1
2
3
4
5
<MyTable :list="list">
<template #default="obj">
<button @click="del(obj.id)">删除</button>
</template>
</MyTable>

组件的封装 - 综合案例实现

my-tag 组件的封装

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

实现双击显示输入框, 并且获取输入框的焦点

  1. 首先, 双击显示输入框, 我们可以通过双点击事件dblclick="handleClick"实现,然后在实现的函数中 通过使v-if的内容为true, 实现点击显示输入框

  2. 获取输入框的焦点可以有两种方式:

  • 方式一: 通过双击, 然后在其中的函数里通过this.$nextTick(()=> {})的方式 实现
  • 方式二: 通过在main.js中全局注册, 然后封装全局指令focus,然后就可以直接通过v-focus来进行使用

image.png

失去焦点

因为获取焦点,我们是通过v-if来向选择的, 所以如果想要失去焦点, 可以直接将if中的信息修改即可。
所以就可以通过@blur="isEdit = false"实现失去焦点

回显标签信息

回显的信息是通过父组件传入的, 可以通过v-model实现, 也可以通过前面所学的props实现。
这里我们使用v-model实现, v-model ==> :value 和 input的组合

  1. 父组件中<MyTag v-model="tempText"></MyTag>, 通过v-model将需要修改的信息传入子标签
  2. 子标签中通过props来进行接受标签内容
  3. 接下来就可以在结构页面通过{{ value}}实现内容的显示。

通过上述的步骤就可以实现数据从父标签传入子标签, 实现标签内容的回显

回车修改标签内容

上述的回显示标签信息是通过父标签传子标签的形式实现的, 但是如何实现子标签传入父标签呢 ?
这里通过回车实现事件的触发, 那么我们就·在回车事件内实现数据的回显。

  1. 首先,我们知道, 回显的内容是在我们输入的input标签中, 同样,我们回车触发事件的内容也是在input中, 所以我们可以通过e.target.value获取触发事件的标签的内容, 也就是我们input标签中的内容, 也就是我们需要回显 的内容。
  2. 所以在回车事件中, 我们就可以通过this.$emit('input', e.target.value)实现子标签的内容向父标签传递的功能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<template>
<div class="my-tag">
<input
v-if="isEdit"
refs="inp"
v-focus
:value="value"
class="input"
type="text"
placeholder="输入标签"
@blur="isEdit = false"
@keyup.enter="handleEnter"
/>
<!--
:value="values"绑定
v-focus 自动聚焦的方法二 : 在main.js中封装全局指令
@blur="isEdit = false" 失去焦点隐藏 -->
<div class="text" v-else
@dblclick="handleClick">{{ value }}</div>
</div>

</template>
<!--
(1) 双击显示输入框,输入框获取焦点
(2) 失去焦点,隐藏输入框
(3) 回显标签信息
信息是由外部父组件传入的
(4) 内容修改,回车 → 修改标签信息
-->
<script>
export default {
//接受父组件传入的信息
props: {
value: String
},
data() {
return {
isEdit: false
}
},
methods: {
// 使用双击点击事件
handleClick(){
// 需要实现自动聚焦的方式一: ,通过this.$nextTick()
// this.$nextTick(()=> {
// //立刻获取焦点
// this.$refs.inp.focus()
// })
this.isEdit = true
},
//e :可以获取触发事件的事件源
handleEnter(e) {
if(e.target.value.trim() === '') return alert("标签内容不为空")
//获取回车之后里面的内容, 所以获取内容, 然后更新给父组件
this.$emit('input', e.target.value)
this.isEdit= false //隐藏输入框
}
}
}
</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>

my-table 组件的封装

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

动态定义表

通过<slot name="head"></slot>占位, 然后在父标签中实现内容传递
image.png

在父组件App.vue中, 通过使用<template #head...>的方式, 实现组件插槽的自定义编辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<template>
<div class="table-case">
<!-- 动态的定义表头 -->
<MyTable :data="goods">
<template #head>
<th>编号</th>
<th>图片</th>
<th>名称</th>
<th width="100px">标签</th>

</template>

<!-- 通过表头 ,自定义主体部分. 通过obj对象来接收传入的内容 -->
<template #body="obj">
<td>{{ obj.index + 1}}</td>
<td> <img :src="obj.item.picture" /> </td>
<td> {{ obj.item.name }} </td>
<td> <MyTag v-model="obj.item.tag"></MyTag> </td>

</template>
</MyTable>

</div>
</template>

在子组件MyTable.vue组件中,通过<slot name="body" :item="item" :index="index"></slot>占位符的方式, 实现组件内容的占位, 然后再通过:参数=“参数”的方式, 实现占位中的内容传输

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>

<table class="my-table">
<thead>
<tr>
<slot name="head"></slot>
</tr>
</thead>
<tbody >
<tr v-for="(item, index) in data" :key="item.id">
<!-- 通过slot插槽占位 ,之后又使用:item 进行插槽传值 -->
<slot name="body" :item="item" :index="index"></slot>
</tr>

</tbody>
</table>

</template>