vue2 vue3 中指令总结

vue2 vue3 中指令总结

    • 使用方式
    • vue2 指令定义方式
      • 钩子的参数
      • 指令钩子和组件生命周期的执行顺序是怎样的?
    • vue3 中的指令
      • 定义方式
      • hook vs 指令
      • 指令钩子函数和组件生命周期的执行顺序
    • 指令和 render 函数
      • 指令用在 jsx 上
      • 指令和 render 函数一起使用
    • 最佳实践
    • 其他问题
    • 参考
    • 小结

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

指令的作用:可把一些可复用的逻辑封装成指令,以实现逻辑复用,尤其是需要直接操作 DOM 时,可把这些操作封装成指令,能极大提高代码复用性和可维护性。

指令按照使用范围看,分为全局指令局部指令(在某个组件内部使用的)。

指令和组件一样,具有一些在特定时期执行的函数,叫作指令钩子,就是通过它们定义指令的。

使用方式

有一个 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>export default {name: 'ClickOutsideDemo',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 能组合使用,指令不行。多个指令用在同一个元素上,也算一种组合?

hook 优势更大,更加灵活

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

挂载阶段:

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

销毁阶段:

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

比较好实践:只定义 mounted、updated 和 beforeUnmount 钩子。

指令和 render 函数

指令用在 jsx 上

和用到 html 上一样。

function JSXDemo() {return (<><h3>v-copy 用jsx上</h3><div v-copy={'这是复制的内容'} style={{ color: 'red' }}>点击复制</div><h3>v-auth</h3><button v-auth:disabled={'li'} class="button">无权限,被禁用</button><br /><button v-auth="key3">无权限删除</button><br /><button v-auth="key">有权限</button><br /><ElButton type="primary" v-auth="key">按钮</ElButton></>)
}

指令和 render 函数一起使用

使用 widthDirectives 函数注册指令。

withDirectives(VNode, [[vDirective,value, arg, modifiers]])

function RenderDemo() {// withDirectives(VNode, [[vDirective,value, arg, modifiers]])const Input = h('input', {type: 'text',value: 'hello'})const InputWithFocus = withDirectives(Input, [[directiveObj.focus, true]])const DivWithCopy = withDirectives(h('div', '这是复制的内容'), [[directiveObj.copy, '这是复制的内容'],])const BtnWithAuth = withDirectives(h('button', '无权限,被禁用'), [[directiveObj.auth, 'li', 'disabled'],])return h('div', [h('h3', 'v-copy 用h上'),DivWithCopy,['点击复制', h('br'), InputWithFocus],h('br'),h('h3', 'v-auth'),BtnWithAuth,])
}

不能使用全局指令

最佳实践

  1. 传递多个值,使用对象
<div v-demo="{ color: 'white', text: 'hello!' }"></div>
app.directive("demo", (el, binding) => {console.log(binding.value.color); // => "white"console.log(binding.value.text); // => "hello!"
});
  1. 不在组件上使用自定义指令

原因:当组件有多个根元素时,它不知道绑定到那个元素上。

其他问题

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

我搜索不到相关教程。

github 的一个 issue

有请大佬解答。

参考

Use Vue3 Custom Directives Like a Pro

小结

  • 指令是复用逻辑的有效方式,尤其是遇到需要直接操作 DOM 的情况。
  • vue2 中常用的指令钩子:inserted、componentUpdated 和 unbind。
  • vue3 中常用的指令钩子:mounted、updated 和 beforeUnmount。
  • vue3 中 script setup 定义局部指令使用 v 开头。
  • vue3 的 binding 对象添加了 instance 属性。
  • 相同的功能 hook 和指令都能实现,hook 优势更大。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/27526.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Qt] Qt Creator 以及 Qt 在线安装教程

一、Qt Creator 下载及安装 1、从以下镜像源下载安装包常规安装即可 Qt Creator 也可以在第二步Qt 在线安装时一次性勾选安装&#xff0c;见后文 Qt Creator 中科大源下载地址 二、Qt 在线安装 1、根据所在平台选择对应的安装器下载 Qt 在线安装器下载 2、可能的安装报错…

Windows 11部署FunASR离线语音识别系统

Windows 11部署FunASR离线语音识别系统 官网连接 https://github.com/alibaba-damo-academy/FunASR/blob/main/runtime/docs/SDK_advanced_guide_online_zh.md1-安装Docker 运行Docker Desktop Installer.exe安装Docker 2-Windows添加删除程序增加虚拟机和Linux子系统功能 …

云电脑有多好用?适合哪些人使用?

云电脑作为一种新型的计算模式&#xff0c;其应用场景广泛且多样&#xff0c;适合各类人群使用。云电脑适合什么人群使用&#xff1f;云电脑有哪些应用场景&#xff1f;有什么好的云电脑推荐&#xff1f;以下本文将详细探讨云电脑的主要应用场景及其适用人群的相关内容&#xf…

win10下使用docker和VMware

1. 要使用docker的处理 打开 Hyper-V 管理员身份打开提示符。 输入命令bcdedit /set hypervisorlaunchtype auto 重启电脑 2. 要使用vm虚拟机 关闭 Hyper-V 管理员身份打开提示符。 输入 bcdedit 并回车&#xff0c;找到hypervisorlaunchtype选项发现为auto 输入命令bcdedit /s…

C#使用OpenXml读取Word、PPT、Excel文档内容

DocumentFormat.OpenXml是微软官方推出的一个操作Excel、Word、PPT文件的开源组件&#xff0c;因此它是免费的。以下是一些关于DocumentFormat.OpenXml的详细信息&#xff1a; 开源性质&#xff1a;DocumentFormat.OpenXml是一个开源项目&#xff0c;这意味着其源代码是公开的…

JavaScript内置方法 操作数组和字符串

在JavaScript中&#xff0c;数组和字符串是非常常见的数据类型&#xff0c;它们各自有一套强大的内置方法。以下是一些常用的数组和字符串方法&#xff0c;以及它们的实例和注意事项&#xff1a; 数组操作方法&#xff1a; 1. push() / pop() push()&#xff1a;向数组的末尾…

禁用PS/Photoshop等一系列Adobe旗下软件联网外传用户数据操作

方案一&#xff1a; 下载火绒杀毒&#xff0c;在联网请求上禁用Adobe软件的联网请求&#xff0c;甚至还可以额外发现哪些是它要想要偷偷摸摸干的。 方案二&#xff1a; 最后注意&#xff1a; 用盗版软件只是获得了使用权&#xff01;

Docker系列.Docker Desktop中如何启用Kubernetes

Docker技术概论 Docker Desktop中如何启用Kubernetes - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.…

Linux编辑器 vim使用 (解决普通用户无法进行sudo提权问题)

文章目录 一.vim是什么命令模式底行模式 二.关于vim暂停问题三.注释批量化注释批量化去注释 四.解决普通用户无法进行sudo提权问题五.vim的配置 一.vim是什么 用过VS的都知道&#xff0c;拥有着编辑器编译器调试.编写C&#xff0c;C&#xff0c;python等的功能。就是集成 Linu…

骤旱(Flash drought)相关小结

1. 简介 2. 数据集 3. 识别算法 待更新

1606 - 求一个两位数倒序的结果

问题描述 请输出一个两位的整数 n&#xff0c;倒过来的数&#xff0c;也就是输出这个两位数个位和十位颠倒的结果。 比如&#xff1a;整数 23 倒过来是 32&#xff0c;整数 18 倒过来是 81 &#xff0c;整数 20 倒过来是 2。 输入 两位整数 n。 输出 n 倒过来的整数。 样…

LeetCode | 434.字符串中的单词数

这道题直接使用语言内置的 split 函数可直接分离出字符串中的每个单词&#xff0c;但是要注意区分两种情况&#xff1a;1、空串&#xff1b;2、多个空格连续&#xff0c;分割后会出现空字符的情况&#xff0c;应该舍弃 class Solution(object):def countSegments(self, s):&qu…

java算法:选择排序

文章标题 概述与基本实现优缺点尝试优化 概述与基本实现 选择排序&#xff08;Selection Sort&#xff09;是一种简单直观的排序算法。它的基本思想是每次从待排序的元素中选择最小&#xff08;或最大&#xff09;的元素&#xff0c;放置在已排序的部分的末尾&#xff0c;直到…

JAVA系列---函数式接口

函数式接口的定义 一个函数式接口有且仅有一个抽象方法(SAM&#xff0c;single abstract method)。对于接口来说抽象方法必须重写&#xff0c;默认方法可选重新&#xff0c;静态方法不可重新。Object 类中的 public abstract method 不会被视为单一的抽象方法。这些方法对于函…

java技术专家面试指南100问【java学习+面试宝典】(四)

如何避免“伪共享”&#xff1f; 字节填充&#xff08;创建变量时&#xff0c;使用字段对其进行填充&#xff0c;避免多个变量被分派到同一个缓存行里&#xff09;。JDK8提供了一个Contended注解来解决伪共享。 Netty 的应用场景了解么&#xff1f; Netty 主要用来做网络通信…

数据结构学习笔记-树

1.树的存储结构 &#xff08;1&#xff09;双亲表示法 顺序存储结点数据&#xff0c;结点中保存父节点在数组中的下标。 优点&#xff1a;找父节点方便&#xff1b;缺点&#xff1a;找孩子不方便 #define MAX_TREE_SIZE 100 //树中最多结点数 typedef struct{ //树的…

微信小程序点击打开关联企业微信客服

wx.openCustomerServiceChat({extInfo: {url: https://work.weixin.qq.com/kfid/kfc****},corpId: *****,})

磁盘性能概述与磁盘调度算法

目录 1. 磁盘性能概述 1. 数据传输速率 2. 寻道时间 3. 旋转延迟 4. 平均访问时间 2. 早期的磁盘调度算法 1. FIFO&#xff08;First-In-First-Out&#xff09;调度算法 2. SSTF&#xff08;Shortest Seek Time First&#xff09;调度算法 3. SCAN&#xff08;Elevator…

Dubbo3 服务原生支持 http 访问,兼具高性能与易用性

作者&#xff1a;刘军 作为一款 rpc 框架&#xff0c;Dubbo 的优势是后端服务的高性能的通信、面向接口的易用性&#xff0c;而它带来的弊端则是 rpc 接口的测试与前端流量接入成本较高&#xff0c;我们需要专门的工具或协议转换才能实现后端服务调用。这个现状在 Dubbo3 中得…

SVN 报错Error: Unable to connect to a repository at URL解决方法

1. 报错背景&#xff1a; 使用ssh 用svn拉取仓库代码时&#xff0c;出现如下报错&#xff1a; Can’t create session: Unable to connect to a repository at URL svn://127.0.0.1 …. Can’t connect to host ‘127.0.0.1’: Connection refused at C:/Program Files/Git/mi…