关于javascript:vue指令总结一次帮你学会使用

指令

vue 内置了一些指令,也提供了自定义指令的接口。

指令的作用:可把一些可复用的逻辑封装成指令,以实现逻辑复用

指令依照应用范畴看,分为全局指令和部分指令(在某个组件外部应用的)。

指令和组件一样,具备一些在特定期间执行的函数,就是通过它们定义指令的。

应用形式

有一个v-test指令。

<template>
  <div v-test:disabled.foo="'directive'">
    <h1>{{ msg }}</h1>
  </div>
</template>

:之后的是指令参数,相似v-on:keyup中的 keyup
.之后的时指令修饰符,foo 是修饰符。

参数只能有一个,修饰符可有多个。

v-test:disabled:boo.foo.zoo="msg"

{
  arg: "disabled:boo"
  modifiers: {foo: true, zoo: true}
}

心愿不同状况下绑定不同的参数,可应用动静参数

v-test:[argu].foo.zoo="msg"

参数必须在修饰符之前。

指令的等号前面是指令表达式,其值对应binding对象的 value 属性。

binding 一个对象:

{
  arg: "disabled",
  expression: "msg",
  modifiers: {foo: true},
  name: "test",
  value: "你好",
}

vue2 指令定义形式

以插件的模式定义一个全局v-click-outside:

export const clickOutside = (Vue, options) => {
  Vue.directive('clickOutside', {
    inserted(el, binding, vnode) {
      const { value } = binding
      // const _this = vnode.context
      // NOTE 技巧:处理函数挂载在元素上,不便解绑时移出事件监听
      el.onClick = ({ target }) => {
        if (el.contains(target)) {
          // 点击外部
          console.log('clickInside')
        } else {
          // 点击内部
          console.log('clickOutside')
          value && value()
        }
      }
      document.addEventListener('click', el.onClick, false)
    },
    unbind(el, binding, vnode) {
      document.removeEventListener('click', el.onClick, false)
    },
  })
}

注册插件:

main.js

Vue.use(clickOutside)

应用:

<template>
  <div>
    <h3>测试 v-click-outside</h3>
    <div v-click-outside="onClickOutside">
      <p>测试点击内部</p>
      <p>测试点击内部</p>
      <p>测试点击内部</p>
      <p>测试点击内部</p>
    </div>
  </div>
</template>

<script>
  import { Button } from 'element-ui'
  export default {
    name: 'WebComponent',
    components: {
      Button,
    },
    data() {
      return {
        msg: 'Hello web components in stencil!',
      }
    },
    methods: {
      onClickOutside() {
        console.log('onClickOutside')
        console.log(this.msg)
      },
    },
  }
</script>

点击到div内部时,就会执行onClickOutside

Vue.directive('directive-name', options)

options 是一个对象,蕴含一些生命周期钩子,这些生命周期都是可选的。

依照执行程序可:

bind # 只调用一次,指令绑定到元素时调用,父元素可能不存在。做初始化工作
⬇️
inserted # 只调用一次,被绑定的元素插入到父节点,父节点存在,此时可能被绑定元素还每插入文档中。
⬇️
update # 此时组件还没更新结束,拿不到最新的数据
⬇️
componentUpdated # 此时组件曾经更新结束 能拿到最新
⬇️
unbind # 只调用一次,指令和元素解绑,可做一些收尾工作

简写形式:

Vue.directive('color-swatch', function (el, binding) {
  el.style.backgroundColor = binding.value
})

在 bind 和 update 时触发雷同行为,而不关怀其它的钩子。

钩子的参数

bind(el, binding, vnode)
inserted(el, binding, vnode)
update(el, binding, vnode, oldVnode) //  update componentUpdated 还有额定的 oldVnode
componentUpdated(el, binding, vnode, oldVnode)
unbind(el, binding, vnode)

指令钩子函数的参数,次要关注 elbinding

el 是绑定指令的元素,可对其进行 DOM 操作。

binding 一个对象:

{
  arg: "disabled",
  expression: "msg",
  modifiers: {foo: true},
  name: "test",
  value: "你好",
}

updatecomponentUpdated 钩子函数,binding 对象还有 oldArgoldValue 属性。

指令钩子和组件生命周期的执行程序是怎么的?

挂载组件

beforeCreate
⬇️
created
⬇️
beforeMount
⬇️
bind # 指令 绑定到元素时调用,父元素可能不存在
⬇️
inserted # 指令 被绑定元素插入父元素时调用,父元素肯定存在
⬇️
mounted

更新组件

beforeUpdate
⬇️
update # 指令,此时组件还没更新结束,拿不到最新的数据
⬇️
componentUpdated # 指令 此时组件曾经更新结束 能拿到最新的数据
⬇️
updated

销毁组件

beforeDestroy
⬇️
unbind # 指令 组件在销毁之前调用,依然能拿到组件的数据
⬇️
destroyed

重建组件时

beforeCreate
⬇️
created (重建)
⬇️
beforeMount
⬇️
bind  # 留神这里,指令绑定这个钩子函数,将会拿不到重建后的最新数据
⬇️
beforeDestroy (组件销毁)
⬇️
unbind # 指令
⬇️
destroyed
⬇️
inserted # 指令 应用该钩子函数,能拿到重建后的数据
⬇️
mounted

论断:只有 insertedcomponentUpdated 生命周期钩子,在执行时组件的 DOM 曾经更新结束,可放心使用。它们可获取到组件更新后的数据,指令绑定的元素的父元素也曾经存在。

如何在指令生命周期中应用 this 或者拜访组件实例?

间接应用this为 undefined,可应用vnode.context获取:

inserted(el, binding, vnode) {
  // setTile 是组件 methods 里的办法
  vnode.context.setTile(el)
},
componentUpdated(el, binding, vnode, oldVnode) {
  vnode.context.setTile(el)
},

vue3 中的 指令

vue3 的指令钩子、钩子的参数和定义形式有变动。

定义形式

全局指令

app.directive('directive-name', directiveOptions)

简写模式:

app.directive('directive-name', (el, binding) => {}) // mounted 和 updated 操作雷同

部分指令

script setup 应用v结尾命名。

<script setup>
  // 注册一个部分的自定义指令,须要以小写v结尾
  const vFocus = {
    mounted(el, binding, vNode) {
      console.log(el)
      console.log(binding)
      console.log(vNode)
      // 获取input,并调用其focus()办法
      el.focus()
    },
  }
</script>

<template>
  <input v-focus />
</template>

setup 函数形式:

<script>
  export default {
    directives: {
      focus: {
        mounted(el, binding, vNode) {
          // 获取input,并调用其focus()办法
          el.focus()
        },
      },
    },
    setup() {},
  }
</script>

指令钩子的变动:

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) {},
}

罕用的钩子:

beforeMount # 相似 vue2 bind
mounted # 相似 vue2 inserted
updated # 相似 vue2 componentUpdated
beforeUnmount # 相似 vue2 unbind

binding 的参数有变动:减少了instance属性用于获取组件实例。

创立一个检测点击 div 内部的指令:

import type { App } from 'vue'

export const clickOutside = (app: App, options: any) => {
  app.directive('clickOutside', {
    mounted(el, binding) {
      const { value, instance } = binding
      // NOTE 技巧:处理函数挂载在元素上,不便解绑时移出事件监听
      el.onClick = (event: Event) => {
        if (el.contains(event.target)) {
          console.log('clickInside')
        } else {
          // 点击内部
          console.log('clickOutside')
          // console.log(instance)// 组件实例
          value && value()
        }
      }
      document.addEventListener('click', el.onClick, false)
    },
    beforeUnmount(el) {
      console.log('beforeUnmount')
      document.removeEventListener('click', el.onClick, false)
    },
  })
  return app
}

雷同的性能应用 hook 实现:

import { onMounted, onBeforeUnmount, ref } from 'vue'

export function useOnClickOutside(DOM = null, callback) {
  const isClickOutside = ref(false)
  function handleClick(event) {
    if (DOM.value && !DOM.value.contains(event.target)) {
      callback()
      isClickOutside.value = true
    }
  }

  onMounted(() => {
    document.addEventListener('mousedown', handleClick)
  })

  onBeforeUnmount(() => {
    document.removeEventListener('mousedown', handleClick)
  })
  return isClickOutside
}

hook vs 指令

应用形式:hook 在 script 里,指令绑定到模板

定义形式:hook 更加简略,只需记住组件的生命钩子,复用更加不便,指令稍微繁琐,须要额定记忆指令钩子。

hook 能返回响应是对象,指令做不到。

hook 劣势更大,更加灵便

指令钩子函数和组件生命周期的执行程序

挂载阶段:

setup
onBeforeMount
created # 指令钩子
beforeMount # 指令钩子
mounted # 指令钩子
onMounted

销毁阶段:

onBeforeUnmount
beforeUnmount # 指令钩子
unmounted # 指令钩子
onUnmounted

比拟好实际:只定义 mounted、updated 和 beforeUnmount 钩子。

其余问题

如何应用代码测试指令呢?

我搜寻不到相干教程。

github 的一个 issue

有请打大佬解答。

总结

指令是复用逻辑的无效形式,尤其是遇到须要间接操作 DOM 的状况。

vue2 中罕用的指令钩子:inserted、componentUpdated 和 unbind。

vue3 中罕用的指令钩子:mounted、updated 和 beforeUnmount。

vue3 中 script setup 定义部分指令应用 v 结尾。

vue3 的 binding 对象增加了instance属性。

雷同的性能 hook 和指令都能实现,hook 劣势更大。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理