上一篇Vue 相关文章:
自定义指令基础回顾
生命周期
const myDirective = {
// 在绑定元素的 attribute 前
// 或事件监听器应用前调用
created(el, binding, vnode) {
// 下面会介绍各个参数的细节
},
// 在元素被插入到 DOM 前调用
beforeMount(el, binding, vnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都挂载完成后调用
mounted(el, binding, vnode) {},
// 绑定元素的父组件更新前调用
beforeUpdate(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都更新后调用
updated(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载前调用
beforeUnmount(el, binding, vnode) {},
// 绑定元素的父组件卸载后调用
unmounted(el, binding, vnode) {}
}
参数
指令的钩子会传递以下几种参数:
el:指令绑定到的元素。这可以用于直接操作 DOM。
binding:一个对象,包含以下属性。
value:传递给指令的值。例如在 v-my-directive="1 + 1" 中,值是 2。
oldValue:之前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否更改,它都可用。
arg:传递给指令的参数 (如果有的话)。例如在 v-my-directive:foo 中,参数是 "foo"。
modifiers:一个包含修饰符的对象 (如果有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }。
instance:使用该指令的组件实例。 dir:指令的定义对象。 vnode:代表绑定元素的底层 VNode。
prevVnode:代表之前的渲染中指令所绑定元素的 VNode。仅在 beforeUpdate 和 updated 钩子中可用。
指令的钩子会传递以下几种参数:
el:指令绑定到的元素。这可以用于直接操作 DOM。
binding:一个对象,包含以下属性。
value:传递给指令的值。例如在 v-my-directive="1 + 1" 中,值是 2。
oldValue:之前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否更改,它都可用。
arg:传递给指令的参数 (如果有的话)。例如在 v-my-directive:foo 中,参数是 "foo"。
modifiers:一个包含修饰符的对象 (如果有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }。 instance:使用该指令的组件实例。 dir:指令的定义对象。 vnode:代表绑定元素的底层 VNode。
prevVnode:代表之前的渲染中指令所绑定元素的 VNode。仅在 beforeUpdate 和 updated 钩子中可用。
v-my-show 的实现
新建一个directive 文件夹
新建index.js : 所有指令的出口文件
新建myshow.js v-my-show的实现
myShow.js
function mounted (el, binding, vnode) {
var isShow = binding.value
el.style.display = isShow ? '' : 'none'
}
function updated (el, binding, vnode) {
var isShow = binding.value
el.style.display = isShow ? '' : 'none'
}
export default {
mounted,
updated
}
index.js
import myShow from './myshow'
export {
myShow
}
使用:
<script setup>
import { ref } from 'vue'
import { myShow } from './directive'
const showHello = ref(false)
const vMyShow = myShow
</script>
<template>
<div>
<p v-my-show="showHello">你好, Vue</p>
<button @click="showHello = !showHello">改变v-show {{ showHello }}</button>
</div>
</template>
<style scoped>
</style>
效果:
可以看到,我们点击按钮, showHello 的值为true 的时候就显示了你好,Vue,为false 的时候就不显示,实现了和v-show 一样的功能。
代码优化: 从上面的代码我们看到,mounted 函数和updated 函数实现的功能逻辑是一样的, 在只使用了mounted 和updated生命周期函数的时候 可以直接导出函数,所以 myshow.js 可以改造成
export default function updated (el, binding, vnode) {
var isShow = binding.value
el.style.display = isShow ? '' : 'none'
}
再来看效果,还是一样的。
v-my-if实现
新建 myif.js
function mounted(el, binding) {
var isShow = binding.value
el.comNode = document.createComment('v-if')
!isShow && el.parentNode.replaceChild(el.comNode, el)
}
function updated (el, binding) {
var isShow = binding.value
!isShow ? el.parentNode.replaceChild(el.comNode, el) : el.comNode.parentNode.replaceChild(el, el.comNode)
}
export default {
mounted,
updated
}
核心注意点:
(1)v-if 注释节点做占位符, Vue原生的v-if 也是用的v-if 注释节点,操作的时候可以打开控制台观察下。
(2) el 是一个对象, mounted的时候我们将注释节点保存在了这个对象上,方便再更新的时候使用。
index.js 导入
import myShow from './myshow'
import myIf from './myIf'
export {
myShow,
myIf
}
使用
<script setup>
import { ref } from 'vue'
import { myShow, myIf } from './directive'
const showHello = ref(false)
const vMyShow = myShow
const showHelloIf = ref(false)
const vMyIf = myIf
</script>
<template>
<div>
<p v-my-show="showHello">你好, Vue</p>
<button @click="showHello = !showHello">改变v-show {{ showHello }}</button>
<hr>
<p v-my-if="showHelloIf">hello vMyIf</p>
<button @click="showHelloIf = !showHelloIf">改变v-if {{ showHelloIf }}</button>
</div>
</template>
<style scoped>
</style>
效果:
点击改变v-if,showHelloIf 为true 的时候, hello vMyIf 就显示出来了, showHelloIf 为false 的时候就hello vMyIf 就消失了。
问题: 现在我们再来点击下改变v-show的时发现控制台有报错
什么原因呢? 是因为,updated 在组件更新的时候凑会触发, 在点击改变v-show的时候,也触发了v-if里面的updated,所以报错了。
解决办法: 利用binding参数上的oldValue 和value 做对比, 如果两个值不一样,说明是当前指令的值变化导致的更新导致的,这种条件下才更新, 具体代码如下:
function updated (el, binding) {
var isShow = binding.value
var oldVal = binding.oldValue
if (isShow !== oldVal) {
!isShow ? el.parentNode.replaceChild(el.comNode, el) : el.comNode.parentNode.replaceChild(el, el.comNode)
}
}
这时候我们再来操作下改变v-show ,就没有报错了
利用Vue 的指令实现v-show, v-if就分享到这里了,感谢收看