认识自定义指令
在Vue的模板语法中我们学习过各种各样的指令:v-show、v-for、v-model等等,除了使用这些指令之外,Vue
也允许我们来自定义自己的指令
。
- 注意:在Vue中,代码的复用和抽象主要还是通过组件;
- 通常在某些情况下,你需要
对DOM元素进行底层操作
,这个时候就会用到自定义指令
;
自定义指令分为两种:
- 自定义局部指令:组件中通过
directives
选项,只能在当前组件中使用; - 自定义全局指令:app的
directive
方法,可以在任意组件中被使用;
比如我们来做一个非常简单的案例:当某个元素挂载完成后可以自定获取焦点
实现方式一:如果我们使用默认的实现方式;
<template>
<div>
<input type="text" ref="inputRef" />
</div>
</template>
<script>
import {ref, onMounted} from "vue"
export default {
setup() {
const inputRef = ref(null)
onMounted(() => {
inputRef.value.focus()
})
return {inputRef};
},
};
</script>
<style scoped></style>
实现方式二:自定义一个 v-focus 的局部指令;
<template>
<div>
<input type="text" v-focus />
</div>
</template>
<script>
export default {
directives: {
focus: {
mounted(el, bindings, vnode, preVNode) {
el.focus();
},
},
},
setup() {
return {};
},
};
</script>
<style scoped></style>
实现方式三:自定义一个 v-focus 的全局指令;
main.js
import { createApp } from "vue";
import App from "./4_directives/1.input自动获取焦点.vue";
const app = createApp(App);
app.directive("focus", {
mounted(el, bindings, vnode, preVNode) {
el.focus();
},
});
app.mount("#app");
App.vue
<template>
<div>
<input type="text" v-focus />
</div>
</template>
<script>
export default {
setup() {
return {};
},
};
</script>
<style scoped></style>
指令的生命周期
一个指令定义的对象,Vue提供了如下的几个钩子函数:
-
created
:在绑定元素的 attribute 或事件监听器被应用之前调用; -
beforeMount
:当指令第一次绑定到元素并且在挂载父组件之前调用; -
mounted
:在绑定元素的父组件被挂载后调用; -
beforeUpdate
:在更新包含组件的 VNode 之前调用; -
updated
:在包含组件的 VNode 及其子组件的 VNode 更新后调用; -
beforeUnmount
:在卸载绑定元素的父组件之前调用; -
unmounted
:当指令与元素解除绑定且父组件已卸载时,只调用一次;
<template>
<div>
<input type="text" v-focus />
</div>
</template>
<script>
export default {
directives: {
focus: {
created(el, bindings, vnode, preVNode) {
//此时元素的属性和事件还没绑定
console.log("created", "此时元素的属性和事件还没绑定");
},
beforeMount(el, bindings, vnode, preVNode) {
console.log("beforeMount", "此时绑定元素的父组件还没挂载");
},
mounted(el, bindings, vnode, preVNode) {
//此时绑定元素的父组件已挂载
console.log("mounted", "此时绑定元素的父组件已挂载");
el.focus();
},
beforeUpdate(el, bindings, vnode, preVNode) {
console.log("beforeUpdate", "在更新包含的组件的vnode之前调用");
},
undated(el, bindings, vnode, preVNode) {
console.log("updated", "在包含组件的 VNode 及其子组件的 VNode 更新后调用;");
},
beforeUnmount(el, bindings, vnode, preVNode) {
console.log("beforeUnmount", "在卸载绑定元素的父组件之前调用");
},
unmounted(el, bindings, vnode, preVNode) {
console.log("unmounted", "当指令与元素解除绑定且父组件已卸载时,只调用一次;");
}
},
},
setup() {
return {};
},
};
</script>
<style scoped></style>
指令的参数和修饰符
如果我们指令需要接受一些参数或者修饰符应该如何操作呢?
<template>
<div>
<button v-why:info.aaa.bbb="{ name: 'coderwhy', age: '18' }">
{{ count }}
</button>
</div>
</template>
<script>
import { ref } from "@vue/reactivity";
export default {
directives: {
why: {
mounted(el, bindings, vnode, preVNode) {
//此时绑定元素的父组件已挂载
console.log("mounted", "此时绑定元素的父组件已挂载");
console.log(bindings);
},
},
},
setup() {
const count = ref(100);
return { count };
},
};
</script>
<style scoped></style>
- info是
参数
的名称; - aaa-bbb是
修饰符
的名称; - 后面是
传入的具体的值
;
在我们的生命周期中,我们可以通过 bindings 获取到对应的内容:
image.png例:自定义时间格式化指令
./directives/time-format.js
import dayjs from 'dayjs'
export default function (app) {
app.directive('format-time', {
//在绑定的元素的父组件被挂载后调用
mounted(el,bindings,vnode,preVnode) {
//获取绑定元素的内容
const textContent = el.textContent
let timestamp = parseInt(textContent)
//假如时间戳为10位说明单位为s,为13为,说明单位为毫秒
if(textContent.length === 10) {
timestamp = timestamp * 1000
}
//获取v-format-time的值
let formatString = bindings.value
//假如v-format-time的值不存在,设置默认值
if(!formatString) {
formatString = 'YYYY-MM-DD HH:mm:ss'
}
el.textContent = dayjs(timestamp).format(formatString)
}
})
}
./directives/index.js
import timeFormat from './time-format'
export default function registerDirectives(app) {
timeFormat(app)
}
在main.js中注册
import { createApp } from "vue";
import App from "./4_directives/3.指令的参数和修饰符.vue";
import registerDirectives from "./directives/index";
const app = createApp(App);
app.use(registerDirectives);
app.mount("#app");
使用
<template>
<div>
<!-- 此处需要用单引号引起来,否则会认为YYYY-MM-DD是个变量 -->
<div v-format-time="'YYYY-MM-DD'">1624777616</div>
</div>
</template>
<script>
export default {
}
</script>
例:input值格式化
./directives/nuber-input.js
function onInput(el, elInput, bindings, vnode) {
return function handle() {
// 只保留数字
//获取input输入框的值
let val = elInput.value;
// modifiers为修饰符对象,传入了float,则其float属性为true
//假如使用了.float修饰符,输入框可以输入带小数点的浮点数
if (bindings.modifiers.float) {
// 清除"数字"和"."以外的字符
val = val.replace(/[^\d.]/g, "");
// 只保留第一个, 清除多余的 .. ... ....等都清除 .保留
val = val.replace(/\.{2,}/g, ".");
// 第一个字符如果是.号,则补充前缀0
val = val.replace(/^\./g, "0.");
//假如指令有传值,如:v-number-input.float="2"
if (typeof bindings.value !== "undefined") {
// 期望保留的最大小数位数
let pointKeep = 0;
if (
typeof bindings.value === "string" ||
typeof bindings.value === "number"
) {
pointKeep = parseInt(bindings.value);
}
if (!isNaN(pointKeep)) {
//如果pointkeep是有效数字
if (!Number.isInteger(pointKeep) || pointKeep < 0) {
//如果pointkeep不是整数或者是负数 设置pointkeep为0
pointKeep = 0;
}
const str = "^(\\d+)\\.(\\d{" + pointKeep + "}).*$";
const reg = new RegExp(str);
if (pointKeep === 0) {
// 不需要小数点
val = val.replace(reg, "$1");
} else {
// 通过正则保留小数点后指定的位数
val = val.replace(reg, "$1.$2");
}
}
}
if (Number(val) > 10000000) val = 10000000;
elInput.value = val + "";
} else {
//假如没有使用.float修饰符,输入框只能输入整数 v-number-input 输入框所有非数字替换为空字符串
elInput.value = elInput.value.replace(/[\D]/g, "");
}
};
}
export default function(app) {
app.directive("number-input", {
mounted(el, bindings, vnode, preVnode) {
console.log(vnode);
//获取input元素:假如指令绑定的元素不是input元素,获取其包含的input元素
const elInput = el.tagName === "INPUT" ? el : el.querySelector("input");
//给input元素添加事件
elInput.addEventListener(
"keyup",
onInput(el, elInput, bindings, vnode),
false
);
},
});
}
<input v-number-input />
<input v-number-input.float='2' />
此文档主要内容来源于王红元老师的vue3+ts视频教程
网友评论