目录
自定义指令
基本语法 (全局&局部注册)
自定义指令:自己定义的指令, 可以封装一些 dom 操作, 扩展额外功能
全局注册 :
Vue.directive('focus',{
"inserted" (el) {
el.focus()
}
})
局部注册:
// 局部的自定义属性
directives: {
//定义一个局部的focus指令
"focus" :{
inserted (el) {
el.focus()
}
},
//定义一个局部的color指令,且内容值修改时也会发生变化
"color" :{
inserted (el,bingding) {
console.log(bingding.value);
el.style.color = bingding.value
},
update(el,bingding){
// console.log(bingding.value);
el.style.color = bingding.value
}
}
}
指令的值
需求:实现一个 color 指令 - 传入不同的颜色, 给标签设置文字颜色
练习:v-loading 指令封装
场景:实际开发过程中,发送请求需要时间,在请求的数据未回来时,页面会处于空白状态 => 用户体验不好
需求:封装一个 v-loading 指令,实现加载中的效果
分析:
1. 本质 loading 效果就是一个蒙层,盖在了盒子上
2. 数据请求中,开启loading状态,添加蒙层
3. 数据请求完毕,关闭loading状态,移除蒙层
实现:
1. 准备一个 loading 类,通过伪元素定位,设置宽高,实现蒙层
2. 开启关闭 loading 状态(添加移除蒙层),本质只需要添加移除类即可
3. 结合自定义指令的语法进行封装复用
总结:
自定义指令的作用?
封装一些 dom 操作,扩展额外功能,例如获取焦点
自定义指令的使用步骤?
1. 注册 (全局注册 或 局部注册)
在 inserted 钩子函数中,配置指令dom逻辑
2. 标签上 v-指令名 使用
指令值的语法:
① v-指令名 = "指令值",通过 等号 可以绑定指令的值
② 通过 binding.value 可以拿到指令的值
③ 通过 update 钩子,可以监听指令值的变化,进行dom更新操作
插槽(slot)
默认插槽
作用:让组件内部的一些 结构 支持 自定义
需求: 将需要多次显示的对话框, 封装成一个组件
问题:组件的内容部分,不希望写死,希望能使用的时候自定义。怎么办?
插槽基本语法:
1. 组件内需要定制的结构部分,改用<slot></slot>占位
2. 使用组件时, <MyDialog></MyDialog>标签内部, 传入结构替换slot
插槽 - 后备内容(默认值)
通过插槽完成了内容的定制,传什么显示什么, 但是如果不传,则是空白
能否给插槽设置 默认显示内容 呢?
插槽后备内容:封装组件时,可以为预留的 `<slot>` 插槽提供后备内容(默认内容)。
具名插槽
需求:一个组件内有多处结构,需要外部传入标签,进行定制
默认插槽:一个的定制位置
具名插槽基本语法:
具名插槽简化语法:
作用域插槽
作用域插槽: 定义 slot 插槽的同时, 是可以传值的。给 插槽 上可以 绑定数据,将来 使用组
场景:封装表格组件
1. 父传子,动态渲染表格内容
2. 利用默认插槽,定制操作列
3. 删除或查看都需要用到 当前项的 id,属于 组件内部的数据
通过 作用域插槽 传值绑定,进而使用
基本使用步骤:
1. 给 slot 标签, 以 添加属性的方式
2. 所有添加的属性, 都会被收集到一个对象中
3. 在template中, 通过 ` #插槽名= "obj" ` 接收,默认插槽名为 default
综合案例:商品列表
需求说明:
1. my-tag 标签组件封装
(1) 双击显示输入框,输入框获取焦点
(2) 失去焦点,隐藏输入框
(3) 回显标签信息
(4) 内容修改,回车 → 修改标签信息
2. my-table 表格组件封装
(1) 动态传递表格数据渲染
(2) 表头支持用户自定义
(3) 主体支持用户自定义
MyTable
<template>
<table class="my-table">
<thead>
<tr>
<slot name="head"></slot>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in goods " :key="item.id">
<!-- 作用域插槽--用于传递数据 -->
<slot name="body" :item="item" :index="index"></slot>
</tr>
</tbody>
</table>
</template>
<script>
export default {
props: {
goods: Array
}
}
</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 .5s;
&.red {
color: red;
}
}
.none {
height: 100px;
line-height: 100px;
color: #999;
}
}
</style>
MyTag
<template>
<div class="my-tag">
<input
v-if="isShow"
ref="inp"
class="input"
type="text"
placeholder="输入标签"
@blur="isShow=false"
@keyup.enter="handleEnter"
:value="value"
/>
<div v-else @dblclick="handleClick" class="text">{{value}}</div>
</div>
</template>
<script>
export default {
data(){
return{
isShow: false,
}
},
props:{
value: String
},
methods: {
handleClick(){
//显示文本框
this.isShow = true
//页面改变后文本框获取焦点
this.$nextTick(()=>{
this.$refs.inp.focus()
})
},
handleEnter(e){
if(!e.target.value.trim()){
return
}
// console.log('回车键被点击');
// 向父组件发送消息 父组件使用的是v-model,所以监听的是input事件
// console.log(this.$refs.inp.value);
this.$emit('input',this.$refs.inp.value)
//输入完成,隐藏输入框
this.isShow = 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>
App.vue
<template>
<div class="table-case">
<MyTable :goods="goods">
<!-- 具名插槽 -->
<template #head>
<th>编号</th>
<th>图片</th>
<th>名称</th>
<th width="100px">标签</th>
</template>
<template #body="{item,index}">
<td>{{index+1}}</td>
<td><img :src="item.picture" /></td>
<td>{{item.name}}</td>
<td>
<!-- 标签组件 -->
<MyTag v-model="item.tag"></MyTag>
</td>
</template>
</MyTable>
</div>
</template>
<script>
//导入组件
import MyTable from '../src/components/MyTable.vue'
import MyTag from '../src/components/MyTag.vue'
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: '儿童服饰' },
]
}
},
// 注册组件
components:{
MyTable,
MyTag
}
}
</script>
<style lang="less" scoped>
.table-case {
width: 1000px;
margin: 50px auto;
img {
width: 100px;
height: 100px;
object-fit: contain;
vertical-align: middle;
}
}
</style>