我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Vue中的自定义指令怎么实现

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Vue中的自定义指令怎么实现

今天小编给大家分享一下Vue中的自定义指令怎么实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

试炼:实现v-mymodel

定义指令

首先梳理思路:原生input控件与组件的实现方式需要区分,input的实现较为简单,我们先实现一下input的处理。首先我们先定义一个不做任何操作的指令

Vue.directive('mymodel', {        //只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。        bind(el, binding, vnode, oldVnode) {        },        //被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中),需要父节点dom时使用这个钩子        inserted(el, binding, vnode, oldVnode) {        },        //所在组件的 VNode 更新时调用,**但是可能发生在其子 VNode 更新之前**。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。        update(el, binding, vnode, oldVnode) {        },        //指令所在组件的 VNode **及其子 VNode** 全部更新后调用。        componentUpdated(el, binding, vnode, oldVnode) {        },        只调用一次,指令与元素解绑时调用。        unbind(el, binding, vnode, oldVnode) {        },})

上面的注释中详细的说明了各个钩子函数的调用时机,因为我们是给组件上添加input事件和value绑定,因此我们在bind这个钩子函数中定义即可。所以我们把其他的先去掉,代码变成这样。

Vue.directive('mymodel', {        //只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。        bind(el, binding, vnode, oldVnode) {         }})

简单说一下bind函数的几个回调参数,el是指令绑定组件对应的dombinding是我们的指令本身,包含namevalueexpressionarg等,vnode就是当前绑定组件对应的vnode结点,oldVnode就是vnode更新前的状态。

接下来我们要做两件事:

  • 绑定input事件,同步inputvalue值到外部

  • value值绑定,监听value的变化,更新到inputvalue

这对于input原生组件比较容易实现:

//第一步,添加inout事件监听el.addEventListener('input', (e) => {   //context是input所在的父组件,这一步是同步数据   vnode.context[binding.expression] = e.target.value;})//监听绑定的变量vnode.context.$watch(binding.expression, (v) => {     el.value = v;})

这里解释一下上面的代码,vnode.context是什么呢,他就是我们指令所在组件的上下文环境,可以理解就是指令绑定的值所在的组件实例。不熟悉vnode结构的同学建议先看一下官方的文档,不过文档描述的比较简单,不是很全面,所以最好在控制台log一下vnode的对象看一下它具体的结构,这很有助于我们封装自定义指令,对理解Vue原理也很有帮助。

我们可以通过context[binding.expression]获取v-model上到绑定的值,同样可以修改它。上面的代码中我们首先通过在添加的input事件中操作vnode.context[binding.expression] = e.target.value同步inputvalue值到外部(context),与使用@input添加事件监听效果是一样的;然后我们需要做第二件事,做value值的绑定,监听value的变化,同步值的变更到inputvalue上,我们想到我们可以使用Vue实例上的额$watch方法监听值的变化,而context就是那个Vue实例,binding.expression就是我们想要监听的属性,如果我们这样写

<input v-mymodel='message'/>

那么binding.expression就是字符串'message'。所以我们想下面的代码这样监听绑定的响应式数据。

//监听绑定的变量vnode.context.$watch(binding.expression, (v) => {     el.value = v;})

至此,inputv-mymodel的处理就完成了(当然input组件还有typecheckbox,radio,select等类型都需要去特别处理,这里就不再一一处理了,感兴趣的同学可以自己尝试去完善一下),但是对于非原生控件的组件,我们要特殊处理。因此我们完善代码如下:

Vue.directive('mymodel', {        //只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。        bind(el, binding, vnode, oldVnode) {           //原生input组件的处理           if(vnode.tag==='input'){                //第一步,添加inout事件监听                el.addEventListener('input', (e) => {                   //context是input所在的父组件,这一步是同步数据                   vnode.context[binding.expression] = e.target.value;                })                //监听绑定的变量                vnode.context.$watch(binding.expression, (v) => {                     el.value = v;                })           }else{//组件           }        }})

接下来我们要处理的是自定义组件的逻辑,

//vnode的结构可以参见文档。不过我觉得最直观的方法就是直接在控制台打印处理let {    componentInstance,    componentOptions,    context} = vnode;const {   _props} = componentInstance;//处理model选项if (!componentOptions.Ctor.extendOptions.model) {  componentOptions.Ctor.extendOptions.model = {        value: 'value',        event: 'input'  }}let modelValue = componentOptions.Ctor.extendOptions.model.value;let modelEvent = componentOptions.Ctor.extendOptions.model.event;//属性绑定,这里直接修改了属性,没有想到更好的办法,友好的意见希望可以提出_props[modelValue] = binding.value;context.$watch(binding.expression, (v) => {     _props[modelValue] = v;})//添加事件处理函数,做数据同步componentInstance.$on(modelEvent, (v) => {     context[binding.expression] = v;})

声明一下,上面的实现不是vue源码的实现方式,vue源码中实现v-model更加复杂一点,是结合自定义指令、模板编译等去实现的,因为我们是应用级别的封装,所以采用了上述的方式实现。

实现此v-mymodel需要同学去多了解一下VnodeComponentAPI,就像之前说的,最简单的方法就是直接在控制台中直接打印出vnode对象,组件的vnode上有Component的实例componentInstance

接下来简单说一下上面的代码,首先我们可以在componentOptions.Ctor.extendOptions上找到model的定义,如果没有的话需要设置默认值valueinput,然后分别对想原生input的处理一样,分别监听binding.expression的变化和modelEvent事件即可。

需要注意的是,我们上面的代码直接给_prop做了赋值操作,这实际上是不符合规范的,但是我目前没有找到更好的方法去实现。

下面?是完整的源码:

应用实践:4个实用的自定义指令

上文我们通过封装v-mymodel为各位同学展示了如何封装和使用自定义指令,接下来我把自己在生产实践中使用自定义指令的一些经验分享给大家,通过实例,我相信各位同学能够更深刻的理解如何在在应用中封装自己的指令,提高效率。

权限控制

下面我们定义一个v-permission指令用于全平台的权限控制

  • role:角色控制;

  • currentUser:当前登录人判断;当前用户是否是业务数据中的创建人或者负责人

  • bussinessStatus:业务状态判断;

  • every:与操作;

  • some:或操作;

示例代码

//定义权限类型const permissionType = {    ROLE: 'role',    CURRENTUSER:'currentUser',    BUSSINESSSTATUS: 'bussinessStatus',    MIX_EVERY: 'every',    MIX_SOME: 'some'}export default {    //只调用一次,指令第一次绑定到元素时调用    bind: function () {    },    //当前vdom插入到真实dom时,因为是对dom的样式操作,在这里操作    inserted: function (el, binding) {        let show = false;        show=processingType(binding.arg,binding.value);         el.style.display = `${show ? 'inline-block' : 'none'}`    },    //所在组件的VNode更新时调用,状态更新后需要更新显示状态    update: function (el, binding) {        //避免无效的模板更新        if(binding.value===binding.oldValue) return;        let show = false;        show=processingType(binding.arg,binding.value);         el.style.display = `${show ? 'inline-block' : 'none'}`    },    //指令所在组件的 VNode 及其子 VNode 全部更新后    componentUpdated: function (el, binding) {    },    unbind: function () {    },}//处理不同类型的权限控制function processingType(type,value){    let values=[];    switch (type) {        case permissionType.ROLE:            return permissionByRole(value);        case permissionType.CURRENTUSER:            return permissionCreater(value);        case permissionType.BUSSINESSSTATUS:            return permissionBusinessStatus(value);        case permissionType.MIX_EVERY:            for(let type in value){                values.push(processingType(type,value[type]))            }            return values.every(v=>{                return v;            })        case permissionType.MIX_SOME:            for(let type in value){                values.push(processingType(type,value[type]))            }            return values.some(v=>{                return v;            })        default:            return false;    }}//业务状态判断function permissionBusinessStatus(bindingValue){   return bindingValue.status==bindingValue.value;}//当前用户?function permissionCreater(bindingValue){    const userInfo = JSON.parse(sessionStorage.CDTPcookie);    // console.log(userInfo.userInfo.id,bindingValue)    if(bindingValue instanceof Array){        return bindingValue.some(v=>{            return userInfo.userInfo.id==v;        })    }    return userInfo.userInfo.id==bindingValue;}//角色控制export function permissionByRole(bindingValue) {    //这里也可以是store里的用户信息    const userInfo = JSON.parse(sessionStorage.userInfo);      let roles = []    if (userInfo) {        roles = userInfo.roleList    }    let show = false;    if (bindingValue instanceof Array) {        return roles.some(role => {//多角色处理            return bindingValue.some(item => {                return role.roleCode === item            })        })    } else if (typeof bindingValue == 'string') {        show = roles.some(role => {            return role.roleCode === bindingValue;        })    }    return show;}

简单说一下上面?指令的定义思路和使用方法。整体思路就是通过processingType处理权限逻辑,使用el.style.display控制组件显示或隐藏。我在这里从日常应用中提取了一些通用的processingType中的权限处理方式,方便大家理解也供大家参考。

下面逐一说一下权限指令各个类型的使用方法:

//角色权限<component v-permission:role='leader'></component>//判断当前登录人<component v-permission:currentUser='orderInfo.createUser'></component>//判断业务状态<component v-permission:bussinessStatus='{status:orderStatus.RUNNING,value:orderInfo.status}'></component>//角色是leader或者是当前订单的创建者,有权限<component v-permission:some="{role:'leader',currentUser:'orderInfo.createUser'}"></component>//角色是leader并且是当前订单的创建者,有权限<component v-permission:every="{role:'leader',currentUser:'orderInfo.createUser'}"></component>

输入限制

v-input 输入框限制,限制数字、保留n位小数点等。

export default {    inserted: function (el, binding, vnode) {        el.addEventListener('input', function (e) {            if (binding.arg == 'toFixed') {                //限制输入n位小数点                toFiexd(e.target, vnode, binding.value)            } else {                //限制数字输入                Integer(e.target, vnode)            }        })    },}function toFiexd(target, vnode, v) {    console.log(v);    let ln = 2;    if (v) {        ln = v;    }    var regStrs = [        ['^0(\\d+)$', '$1'], //禁止录入整数部分两位以上,但首位为0        ['[^\\d\\.]+$', ''], //禁止录入任何非数字和点        ['\\.(\\d?)\\.+', '.$1'], //禁止录入两个以上的点        ['^(\\d+\\.\\d{' + ln + '}).+', '$1'] //禁止录入小数点后两位以上    ];    for (var i = 0; i < regStrs.length; i++) {        var reg = new RegExp(regStrs[i][0]);        target.value = target.value.replace(reg, regStrs[i][1]);    }    //对于封装的像el-input组件,因为其需要通过input事件同步状态    if(vnode.componentInstance){      vnode.componentInstance.$listeners.input(target.value)    }}function Integer(target, vnode) {    let valueStr = target.value    if (valueStr.length == 1) {        //第一个数字不为0        valueStr = valueStr.replace(/[^0-9]/g, "");    } else {        //只能输入正整数        valueStr = valueStr.replace(/\D/g, "");    }    target.value = valueStr;    if(vnode.componentInstance){      vnode.componentInstance.$listeners.input(target.value)    }}

这里需要特别注意的是下面这行代码

vnode.componentInstance.$listeners.input(target.value)

登录后复制

我们为什么需要添加这一句呢,我们明明已经为target.value做了赋值。
实际上这一句代码相当于指令作用组件内部的$emit('input',target.value),这是因为如果我们是在antd或者elementui中的输入框组件上添加我们定义的v-input指令,直接为target.value赋值是不能生效的,修改的只是原生input控件value值,并没有修改自定义组件的value,还需要通过触发input事件去同步组件状态,修改value值。(这里不了解为什么需要触发input事件区同步状态的同学了解一下v-model的语法糖原理即可理解,使用方法:

<!-- 限制输入两位小数数字 --><input v-input:toFixed="2"/><!-- 限制输入正整数 --><el-input v-input:integer/>

内容处理

我们也可以通过自定义指令做对内容到处理,比如

  • 空值处理

  • 数字千分数逗号分割

export default {    bind:function(){    },    inserted:function(el,binding){        dealContent(el,binding)    },    update:function(el,binding){        dealContent(el,binding)    },    componentUpdated:function(){    },    unbind:function(){    },}function dealContent(el,binding){   const {arg}=binding;   if(arg=='empty'){       if(!el.textContent){//空值显示            el.textContent=binding.value||'暂无数据';        }   }else if(arg=='money'){//金额千分位逗号分割,如10000000显示为100,000,00        if (binding.value) {            el.textContent = dealMoney(binding.value);        }else {            el.textContent = dealMoney(el.textContent);        }   }}

千分位分割代码:

//金额处理export function dealMoney(money, places = 2) {    const zero = `0.00`;    if (isNaN(money) || money === '') return zero;    if (money && money != null) {        money = `${money}`;        let left = money.split('.')[0]; // 小数点左边部分        let right = money.split('.')[1]; // 小数点右边        // 保留places位小数点,当长度没有到places时,用0补足。        right = right ? (right.length >= places ? '.' + right.substr(0, places) : '.' + right + '0'.repeat(places - right.length)) : ('.' + '0'.repeat(places));        var temp = left.split('').reverse().join('').match(/(\d{1,3})/g); // 分割反向转为字符串然后最多3个,最少1个,将匹配的值放进数组返回        return (Number(money) < 0 ? '-' : '') + temp.join(',').split('').reverse().join('') + right; // 补齐正负号和货币符号,数组转为字符串,通过逗号分隔,再分割(包含逗号也分割)反向转为字符串变回原来的顺序    } else if (money === 0) {        return zero;    } else {        return zero;    }}

使用方法:

<span v-content:empty="'无'">{{message}}</span><!-- 金额千分位逗号分割 --><span v-content:money>100000</span>

文件预览

v-preview方便的实现文件预览功能

  • 预览图片;

  • 预览文件;

  • 其他预览类业务功能

import {isOffic,isPdf,isImage} from '@/utils/base'import {previewWithOffice} from '@/utils/fileUtils.js'export default {    inserted:function(el,binding){        el.onclick=function(e){            let params = binding.value            if(isOffic(params.name)){                e.preventDefault()                e.stopPropagation()                previewWithOffice(params.url)//使用office在线预览打开            }else if(isPdf(params.name) || isImage(params.name)){                e.preventDefault()                e.stopPropagation()                if(params.url){//直接打开url                    previewFile(params)                }            }        }    },    //指令所在组件的 VNode 及其子 VNode 全部更新后    componentUpdated: function (el, binding) {        el.onclick=function(e){            let params = binding.value            if(isOffic(params.name)){                //使用插件预览Office文件                e.preventDefault()                e.stopPropagation()                previewWithOffice(params.url)            }else if(isPdf(params.name) || isImage(params.name)){               //预览图片和pdf等能直接打开的文件                e.preventDefault()                e.stopPropagation()                previewFile(params)            }        }    },    unbind(el){       el.onclick=null;    }}//预览图片和pdf等能直接打开的文件function previewFile(params) {    let a = document.createElement("a");    a.download = params.name    a.href = params.url;    a.target = "_blank";    a.click();    a = null;}

使用方法:

<!-- 预览图片 --><image :class="lazy" data-src='url' v-preview="{name:file.name,url:file.url}"></image><!-- 预览文件 --><span v-preview="{name:file.name,url:file.url}">{{file.name}}</span>

以上就是“Vue中的自定义指令怎么实现”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网行业资讯频道。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Vue中的自定义指令怎么实现

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Vue中的自定义指令怎么实现

今天小编给大家分享一下Vue中的自定义指令怎么实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。试炼:实现v-mymodel
2023-07-04

Vue中如何实现自定义指令?

Vue.js提供自定义指令功能,用于扩展其功能。创建自定义指令涉及定义指令对象并注册它。指令生命周期钩子函数在不同阶段调用。示例:v-highlight指令可高亮文本。自定义指令在创建可重用组件、扩展Vue功能、优化性能等场景中很有用。最佳实践包括保持简单、考虑性能、遵循命名约定和提供文档。
Vue中如何实现自定义指令?
2024-04-02

vue中自定义指令怎么用

小编给大家分享一下vue中自定义指令怎么用,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!1、v-drag需求:鼠标拖动元素思路:元素偏移量 = 鼠标滑动后的坐标 - 鼠标初始点击元素时的坐标 + 初始点击时元素距离可视区域
2023-06-22

怎么在Vue3中实现自定义指令

这篇文章主要介绍“怎么在Vue3中实现自定义指令”,在日常操作中,相信很多人在怎么在Vue3中实现自定义指令问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么在Vue3中实现自定义指令”的疑惑有所帮助!接下来
2023-07-02

Vue自定义指令深入探讨实现

这篇文章主要介绍了Vue自定义指令的实现,Vue支持自定义指令,开发者可以根据自己的需求,创建自己的指令来扩展Vue的功能,需要详细了解可以参考下文
2023-05-19

Vue中如何自定义指令

这篇文章主要介绍Vue中如何自定义指令,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!Vue中除了内置指令,也允许注册自定义的指令。自定义指令提供一种将数据的变化映射为DOM的行为例如:当我们想用一个函数表示焦点Vue
2023-06-04

vue全局注册自定义指令防抖怎么实现

本文小编为大家详细介绍“vue全局注册自定义指令防抖怎么实现”,内容详细,步骤清晰,细节处理妥当,希望这篇“vue全局注册自定义指令防抖怎么实现”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。全局注册自定义指令防抖
2023-06-30

Vue中怎么手动封装自定义指令

本篇内容介绍了“Vue中怎么手动封装自定义指令”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!在前端基础面试中,Vue的指令算是一个高频面试题
2023-06-29

Vue怎么自定义指令directive使用

本篇内容主要讲解“Vue怎么自定义指令directive使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Vue怎么自定义指令directive使用”吧!1. 一个指令定义对象可以提供如下几个钩
2023-07-06

Vue 非常实用的自定义指令

Vue自定义指令有全局注册和局部注册两种方式。先来看看注册全局指令的方式,通过 Vue.directive( id, [definition] ) 方式注册全局指令。然后在入口文件中进行 Vue.use() 调用。

Vue实现带参数的自定义指令示例

这篇文章主要为大家介绍了Vue实现带参数的自定义指令示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-01-11

vue中有哪些自定义指令

这篇文章主要介绍“vue中有哪些自定义指令”,在日常操作中,相信很多人在vue中有哪些自定义指令问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”vue中有哪些自定义指令”的疑惑有所帮助!接下来,请跟着小编一起来
2023-06-22

怎么完全掌握Vue自定义指令

这篇文章主要介绍“怎么完全掌握Vue自定义指令”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“怎么完全掌握Vue自定义指令”文章能帮助大家解决问题。准备:自定义指令介绍除了核心功能默认内置的指令 (v
2023-07-05

Vue实用的自定义指令有哪些

这篇文章主要讲解了“Vue实用的自定义指令有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Vue实用的自定义指令有哪些”吧!前言:Vue自定义指令有全局注册和局部注册两种方式。先来看看注
2023-06-29

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录