目录
1. 什么叫破坏式更新?
2. Vue3 中的自定义指令
2.1 自定义指令的生命周期
2.1.1 Vue2 Vs Vue3 的自定义指令生命周期
2.1.2 自定义指令的生命周期中,接收的参数
2.2 定义一个自定义指令
2.2.1 在 setup 中定义自定义指令(此处为 对象 形式)
2.2.2 在 setup 中定义自定义指令(此处为 函数简写 形式)
2.3 自定义指令 Demo —— 实现拖动
1. 什么叫破坏式更新?
有一个方法,传入一个对象并返回结果。在方法结束之后,传入的参数对象也被改变了,这就是破坏式更新。
java8函数式编程笔记-破坏式更新和函数式更新_weixin_33946020的博客-CSDN博客破坏式更新和函数式更新什么是破坏式更新和函数式更新:破坏式更新: 有一个方法,传入一个对象并返回结果。在方法结束之后传入的参数对象也被改变了,这就是破坏式更新。你不能保证调用这个方法之后后续是否还会使用传入的参数对象,因此破坏式更新在java的函数式编程中是不被提倡的。这也是另一种副作用。函数式更新: 用函数式编程的方法解决问题,强调没有任何副作用破坏式更新例子:我们有一个类用...https://blog.csdn.net/weixin_33946020/article/details/91403872
前面的 v-model,以及本章 directive 都属于破坏式更新
2. Vue3 中的自定义指令
v-if、v-show、v-for、v-model、v-on、v-bind 等,都是 vue 内置的指令
2.1 自定义指令的生命周期
2.1.1 Vue2 Vs Vue3 的自定义指令生命周期
Vue2 中,自定义指令的生命周期,区别于组件的生命周期:
- bind
- inserted
- update
- componentUpdated
- unbind
Vue3 中,自定义指令的生命周期,类似于组件的生命周期:
- created —— 元素初始化时执行
- beforeMount —— 指令绑定到元素后执行,只执行一次
- mounted —— 元素插入父级 DOM 时执行
- beforeUpdate —— 元素被更新之前执行
- updated —— 元素更新后执行(注意:带 d)
- beforeUnmount —— 元素被移除前执行
- unmounted —— 指令被移除后执行,只执行一次
2.1.2 自定义指令的生命周期中,接收的参数
参数一:el —— 当前绑定的 DOM 元素
参数二:binding —— 自定义指令接收的值
- instance:使用 自定义指令的 组件实例
- value:传递给指令的值(在 v-my-directive="{ color: 'red' }" 中,该值为 { color: 'red' })
- oldValue:先前的值,仅在 beforeUpdate 和 updated 中可用,无论值是否有更改都可用
- arg:传递给指令的参数(在 v-my-directive:test 中,arg 为 "test")
- modifiers:包含修饰符的对象(在 v-my-directive.foo.bar 中,修饰符对象为 { foo: true,bar: true })
- dir:一个对象,在注册指令时,作为参数传递,包含了自定义指令生命周期
参数三:自定义指令绑定的 元素的 虚拟 DOM,也就是 Vnode
参数四:prevNode 上一个虚拟节点,仅在 beforeUpdate 和 updated 钩子中可用
2.2 定义一个自定义指令
自定义指令命名,必须符合一定的规则 —— vNameOfDirective
2.2.1 在 setup 中定义自定义指令(此处为 对象 形式)
自定义指令的大部分生命周期,都能接收到 2.1.2 中提到的参数
根据这些参数,我们可以对绑定指令的 DOM 元素进行各种操作
const vColorDirective: Directive = {
created: () => {
console.log("------ 初始化 ------");
},
beforeMount(...args: Array<any>) {
// 在元素上做些操作
console.log("------ 初始化一次 ------");
},
mounted(el: any, dir: DirectiveBinding<Value>) {
el.style.background = dir.value.background;
console.log("------ 初始化 ------");
},
beforeUpdate() {
console.log("------ 更新之前 ------");
},
updated() {
// 动态绑定的值 改变时 触发
console.log("------ 更新结束 ------");
},
beforeUnmount(...args: Array<any>) {
console.log(args);
console.log("------ 卸载之前 ------");
},
unmounted(...args: Array<any>) {
console.log("------ 卸载完成 ------");
},
};
在模板中使用自定义指令:
<template>
<button @click="show = !show">动态改变的值 —— {{ show }}</button>
<Dialog v-color-directive="{ background: 'red', flag: show }"></Dialog>
</template>
2.2.2 在 setup 中定义自定义指令(此处为 函数简写 形式)
在 mounted 和 updated 时,触发相同的行为,其他生命周期内不做任何操作
<template>
<div>
<input v-model="value" type="text" />
<div v-color-directive="{ background: value }"></div>
</div>
</template>
<script setup lang='ts'>
import { ref, Directive, DirectiveBinding } from 'vue'
// 输入框双向绑定的值,用于输入各种颜色
let value = ref<string>('')
type Dir = {
background: string
}
// 这里用函数定义了自定义指令,而不是对象了
const vColorDirective: Directive = (el: HTMLElement, binding: DirectiveBinding<Dir>) => {
// 每次输入的值改变,都会改变背景颜色
el.style.background = binding.value.background
}
</script>
2.3 自定义指令 Demo —— 实现拖动
实现过程:
- 定义 函数 自定义指令
- 获取可以被拖拽的的 DOM,也就是头部
- 给可拖拽部分的 DOM,添加 鼠标按下 时的事件监听
- 记录鼠标落下的位置(为了避免移动时,计算的距离不对)
- 全局添加移动监听,在移动监听的函数里,获取移动的距离,并且移动元素
- 鼠标抬起时,移除全局添加的移动监听
<template>
<div v-drag class="box">
<div class="header"> 头部 - 可拖拽 </div>
<div> 身体 - 不可拖拽 </div>
</div>
</template>
<script setup lang='ts'>
import { ref, Directive, DirectiveBinding } from 'vue'
// 这里用函数定义了自定义指令,而不是对象了
const vDrag: Directive<any, void> = (el: HTMLElement, binding: DirectiveBinding) => {
// 获取可以被拖拽的的 DOM,也就是头部
let dragElement: HTMLDivElement = el.firstElementChild && HTMLDivElement
// 鼠标按下的事件
const mouseDown = (e: MouseEvent) => {
// 记录鼠标落下的位置(为了避免移动时,计算的距离不对)
let moX = e.clientX - el.offsetLeft;
let moY = e.clientY - el.offsetTop;
// 定义移动事件函数
const move = (e: MouseEvent) => {
// 从这里可以获取移动时的坐标
console.log('获取移动时的坐标 ---', e);
// 移动元素(记得 减掉 鼠标最初按下的位置)
el.style.left = e.clientX - moX + 'px';
el.style.top = e.clientY - moY + 'px';
};
// 全局添加移动监听
document.addEventListener('mousemove', move);
// 鼠标抬起时,取消监听 全局鼠标移动 事件
document.addEventListener('mouseup', () => {
document.removeEventListener('mousemove', move);
});
};
// 给可拖拽部分的 DOM,添加事件监听
dragElement.addEventListener('mousedown', mouseDown);
}
</script>