第二章 Vue2进阶使用 v1.0
第一部分 Vue自定义
一、自定义指令(directive)
通过directive定义自己的指令,封装一些dom操作,扩展额外功能
在对应使用范围内通过:<标签名 v-指令名>
使用
1.全局注册
全局注册通常在main.js文件中进行,指令可以在任何组件中使用,无需额外的导入或声明。
javascript
Vue.directive('指令名', {
"inserted" (el){ // 钩子函数,inserted在元素添加到页面时进行操作
// 对el标签,扩展额外功能
}
})
text
bind: 只调用一次,指令第一次绑定到元素时调用。
inserted: 被绑定元素插入父节点时调用(仅保证父节点存在,但不一定已被插入文档中)。
update: 所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。
componentUpdated: 所在组件的 VNode 及其子 VNode 更新后调用。
unbind: 只调用一次,指令与元素解绑时调用。
示例(插入元素时聚焦)
javascript
Vue.directive('focus', {
"inserted" (el){
el.focus()
}
})
vue
<template>
<input v-focus>
</template>
2.局部注册
局部注册的指令只能在定义它的组件内使用,通常在组件的directives选项中进行。
vue
<script>
export default {
directives: {
"指令名": {
inserted (el) { //在元素添加到页面时进行操作
//对el标签,扩展额外功能
}
}
}
}
</script>
3.自定义指令传值
- 绑定指令时,可以传入值为指令绑定具体参数值:<标签名 v-指令名="参数值">
- 在注册时可以通过
binding.value
拿到指令值,指令值修改会触发update
函数
javascript
directives: {
color: {
inserted (e1, binding) { // 在元素添加到页面时进行操作
el.style.color = binding.value
},
update (el, binding) { // 在指令值更新时进行操作
el.style.color = binding.value
}
}
}
vue
<template>
<input v-color="#000000">
</template>
4.使用示例(加载指令封装)
v-loading封装
text
场景:实际开发过程中,发送请求需要时间,在请求的数据未回来时,页面会处于空白状态,用户体验不好
分析:
1. 本质loading效果就是一个蒙层,盖在盒子上
2. 数据请求中开启loading状态,添加蒙层;请求结束关闭loading状态,移除蒙层
实现:
1. 准备一个loading类,通过伪元素定位设置宽高,实现蒙层
2. 开启关闭loading状态(添加移除蒙层),本质只需要添加移除类即可
3. 通过自定义指令语法进行封装复用
vue
<template>
<div class="box" v-loading="isLoading"></div>
</template>
<script>
import axios from 'axios'
export default {
data () {
return {
isLoading: false
}
},
async created () {
this.isLoading = true
const res = await axios.get("http://...")
this.list = res.data.data
this.isLoading = false
},
directives: {
loading: {
inserted(el,binding) {
binding.value ? el.classList.add('loading') : el.classList.remove('loading')
},
update(el,binding) {
binding.value ? el.classList.add('loading') : el.classList.remove('loading')
}
}
}
}
</script>
<style>
loading:before{ /* 1.定义loading类实现蒙层 */
content:'';
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: #fff url('./loading.gif') no-repeat center; //url提供gif作蒙层
}
</style>
二、插槽
插槽可以让子组件内部的一些结构支持自定义,允许子组件定义可复用的布局,同时将一些区域保留为可填充的内容。
1.插槽的使用
vue
<template>
<div>
<slot></slot> <!-- 在需要定制的地方指定插槽 -->
</div>
</template>
vue
<template>
<div>
<MyDialog>
插槽的内容
</MyDialog>
</div>
</template>
<script>
import MyDialog from 'MyDialog.vue'
export default {
components: {
MyDialog
}
}
</script>
vue
<!--封装组件时,可以为预留的插槽提供显示的后备内容(默认值),若传值则会替换默认内容-->
<template>
<div>
<slot>插槽的默认内容</slot>
</div>
</template>
2.具名插槽
一个组件内有多处结构,需要外部传入标签,进行定制时需要用到具名插槽
vue
<!-- 使用多个slot和name属性区分名字 -->
<template>
<div class="dialog-header">
<slot name="header">header插槽的默认内容</slot>
</div>
<div class="dialog-content">
<slot name="content">content插槽的默认内容</slot>
</div>
<div class="dialog-footer">
<slot name="footer">footer插槽的默认内容</slot>
</div>
</template>
vue
<!-- template标签配合 v-slot: 分发对应标签,v-slot: 可以简化为 # -->
<template>
<MyDialog>
<template v-slot:head>
header
</template>
<template v-slot:content>
content
</template>
<template #footer> <!-- v-slot: 简化为 # -->
footer
</template>
</MyDialog>
</template>
3.作用域插槽
定义slot插槽的同时,是可以传值的。插槽上可以绑定数据,将来使用组件时可以用。
- slot标签以属性方式传值:
<slot :id="item.id" msg="element"></slot>
- 在template中通过
#插槽名="obj"
接收,默认插槽名为default
vue
<template>
<MyTable :list="list">
<template #default="obj">
<button @click="del(obj.id)">删除</button>
</template>
</MyTable>
</template>
示例:列表管理
vue
<template>
<div>
<MyTable :data="list">
<!-- 3. 通过template #插槽名="变量名" 接收子组件传递的对象 -->
<template #default="obj">
<button @click="del(obj.row.id)">
删除
</button>
</template>
</MyTable>
<MyTable :data="list2">
<template #default="{ row }">
<button @click="show(row)">查看</button>
</template>
</MyTable>
</div>
</template>
<script>
import MyTable from './components/MyTable.vue'
export default {
data () {
return {
list: [
{ id: 1, name: '张小花', age: 18 },
{ id: 2, name: '孙大明', age: 19 },
{ id: 3, name: '刘德忠', age: 17 },
],
list2: [
{ id: 1, name: '赵小云', age: 18 },
{ id: 2, name: '刘蓓蓓', age: 19 },
{ id: 3, name: '姜肖泰', age: 17 },
]
}
},
methods: {
del (id) { //过滤(删除)对应元素
this.list = this.list.filter(item => item.id !== id)
},
show (row) {
alert(`姓名:${row.name}; 年纪:${row.age}`)
}
},
components: {
MyTable
}
}
</script>
vue
<template>
<table class="my-table">
<thead>
<tr>
<th>序号</th>
<th>姓名</th>
<th>年纪</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in data" :key="item.id">
<td>{{ index + 1 }}</td>
<td>{{ item.name }}</td>
<td>{{ item.age }}</td>
<td>
<!-- 1. 给slot标签,添加属性的方式传值 -->
<slot :row="item"></slot>
<!-- 2. 将所有的属性,添加到一个对象中 -->
<!--{ row: { id: 2, name: '孙大明', age: 19 } }-->
</td>
</tr>
</tbody>
</table>
</template>
<script>
export default {
props: {
data: Array
}
}
</script>
<style scoped>
.my-table {
width: 450px;
text-align: center;
border: 1px solid #ccc;
font-size: 24px;
margin: 30px auto;
}
.my-table thead {
background-color: #1f74ff;
color: #fff;
}
.my-table thead th {
font-weight: normal;
}
.my-table thead tr {
line-height: 40px;
}
.my-table th,
.my-table td {
border-bottom: 1px solid #ccc;
border-right: 1px solid #ccc;
}
.my-table td:last-child {
border-right: none;
}
.my-table tr:last-child td {
border-bottom: none;
}
.my-table button {
width: 65px;
height: 35px;
font-size: 18px;
border: 1px solid #ccc;
outline: none;
border-radius: 3px;
cursor: pointer;
background-color: #ffffff;
margin-left: 5px;
}
</style>