vue指令的开发看这篇文章就够了!超详细,赶快收藏!

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 【10月更文挑战第8天】vue指令的开发看这篇文章就够了!超详细,赶快收藏!

基础介绍

除了核心功能默认内置的指令 (v-modelv-show),Vue 也允许注册自定义指令。自定义指令可以对普通 DOM 元素进行底层操作。

基本语法

局部指令:

 new Vue({
   
    directives:{
   
      {
   指令名:配置对象},
      {
   指令名:配置对象}
    }  
    //或    
    directives:{
   
      {
   指令名:回调函数},
      {
   指令名:回调函数}
    }  
   //两种写法可以混合使用,如
     directives:{
   
      {
   指令名:回调函数},
      {
   指令名:配置对象}
    } 
})

全局指令:

Vue.directive(指令名:配置对象) 
//或   
Vue.directive(指令名:回调函数)
//两种写法可以混合使用

局部指令的函数写法

需求:定义一个v-big指令,和v-text功能类似,但可以把绑定的数值放大10倍。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue自定义指令练习</title>
</head>
<body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <div id="root">
        <h2>当前的n值是:<span v-text="n"></span> </h2>
        <h2>放大10倍后的n值是:<span v-big="n"></span> </h2>
    </div>

    <script type="text/javascript">
        Vue.config.productionTip = false
        new Vue({
    
            el:'#root',
            data:{
    
                    n:1
            },
      //局部指令------directives:{指令名:回调函数}    写法
            directives:{
    
                  big(element,binding){
    
                      console.log(binding)
                      console.log(element)
                      console.dir(element)
                },
        }
})
</script>
</body>
</html>

指令的执行时机

直接在浏览器中打开开发者调试工具,可以看到console.log的内容已经被打印

其中,指令里的this指向的是全局window对象

这说明:当指令写在标签内时,指令函数就被调用执行! 此外,更改v-bid后面的值,指令也会执行。

1.指令与元素成功绑定时(一上来)。

2.指令所在的模板被重新解析时。

指令接受的参数

big指令接受了两个参数(均为形参)

element binding

binding(绑定)

{
   
    "name": "big",
    "rawName": "v-big",
    "value": 1,
    "expression": "n",
    "modifiers": {
   },
    "def": {
   }
}
  • binding:一个对象,包含以下 property:

  • name:指令名,不包括 v- 前缀。

  • rawname:指令名,包括 v- 前缀。
  • value:指令的绑定值,例如:v-big="n" 中,绑定值为 1。
  • expression:字符串形式的指令表达式。例如 v-big="n"中,表达式为 "n"。
  • def: {bind: ƒ, update: ƒ}:。
  • modifiers:。

除了 el 之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的 dataset 来进行。

element

  • element :指令所绑定的元素,可以用来直接操作 DOM。
<span></span>

console.log(element)打印出了span标签(实际上span这个Dom对象)

accessKey: ""
ariaAtomic: null
ariaAutoComplete: null
ariaBusy: null
ariaChecked: null
ariaColCount: null
ariaColIndex: null
ariaColSpan: null
ariaCurrent: null
ariaDescription: null
ariaDisabled: null
ariaExpanded: null
ariaHasPopup: null
ariaHidden: null
ariaKeyShortcuts: null
ariaLabel: null
ariaLevel: null
ariaLive: null
ariaModal: null
ariaMultiLine: null
ariaMultiSelectable: null
ariaOrientation: null
ariaPlaceholder: null
ariaPosInSet: null
ariaPressed: null
ariaReadOnly: null
ariaRelevant: null
ariaRequired: null
ariaRoleDescription: null
ariaRowCount: null
ariaRowIndex: null
ariaRowSpan: null
ariaSelected: null
ariaSetSize: null
ariaSort: null
ariaValueMax: null
ariaValueMin: null
ariaValueNow: null
ariaValueText: null
assignedSlot: null
attributeStyleMap: StylePropertyMap {
   size: 0}
attributes: NamedNodeMap {
   length: 0}
autocapitalize: ""
autofocus: false
baseURI: "file:///D:/%E5%AD%A6%E4%B9%A0%E8%B5%84%E6%96%99/Vue%E7%9B%B8%E5%85%B3%E4%BB%A3%E7%A0%81/vue2_%E5%9F%BA%E7%A1%80/16_%E8%87%AA%E5%AE%9A%E4%B9%89%E6%8C%87%E4%BB%A4/index.html"
childElementCount: 0
childNodes: NodeList []
children: HTMLCollection []
classList: DOMTokenList [value: '']
className: ""
clientHeight: 0
clientLeft: 0
clientTop: 0
clientWidth: 0
contentEditable: "inherit"
dataset: DOMStringMap {
   }
dir: ""
draggable: false
elementTiming: ""
enterKeyHint: ""
firstChild: null
firstElementChild: null
hidden: false
id: ""
innerHTML: ""
innerText: ""
inputMode: ""
isConnected: true
isContentEditable: false
lang: ""
lastChild: null
lastElementChild: null
localName: "span"
namespaceURI: "http://www.w3.org/1999/xhtml"
nextElementSibling: null
nextSibling: null
nodeName: "SPAN"
nodeType: 1
nodeValue: null
nonce: ""
offsetHeight: 32
offsetLeft: 245
offsetParent: body
offsetTop: 72
offsetWidth: 0
onabort: null
onanimationend: null
onanimationiteration: null
onanimationstart: null
onauxclick: null
onbeforecopy: null
onbeforecut: null
onbeforepaste: null
onbeforexrselect: null
onblur: null
oncancel: null
oncanplay: null
oncanplaythrough: null
onchange: null
onclick: null
onclose: null
oncontextmenu: null
oncopy: null
oncuechange: null
oncut: null
ondblclick: null
ondrag: null
ondragend: null
ondragenter: null
ondragleave: null
ondragover: null
ondragstart: null
ondrop: null
ondurationchange: null
onemptied: null
onended: null
onerror: null
onfocus: null
onformdata: null
onfullscreenchange: null
onfullscreenerror: null
ongotpointercapture: null
oninput: null
oninvalid: null
onkeydown: null
onkeypress: null
onkeyup: null
onload: null
onloadeddata: null
onloadedmetadata: null
onloadstart: null
onlostpointercapture: null
onmousedown: null
onmouseenter: null
onmouseleave: null
onmousemove: null
onmouseout: null
onmouseover: null
onmouseup: null
onmousewheel: null
onpaste: null
onpause: null
onplay: null
onplaying: null
onpointercancel: null
onpointerdown: null
onpointerenter: null
onpointerleave: null
onpointermove: null
onpointerout: null
onpointerover: null
onpointerrawupdate: null
onpointerup: null
onprogress: null
onratechange: null
onreset: null
onresize: null
onscroll: null
onsearch: null
onsecuritypolicyviolation: null
onseeked: null
onseeking: null
onselect: null
onselectionchange: null
onselectstart: null
onslotchange: null
onstalled: null
onsubmit: null
onsuspend: null
ontimeupdate: null
ontoggle: null
ontransitioncancel: null
ontransitionend: null
ontransitionrun: null
ontransitionstart: null
onvolumechange: null
onwaiting: null
onwebkitanimationend: null
onwebkitanimationiteration: null
onwebkitanimationstart: null
onwebkitfullscreenchange: null
onwebkitfullscreenerror: null
onwebkittransitionend: null
onwheel: null
outerHTML: "<span></span>"
outerText: ""
ownerDocument: document
parentElement: h2
parentNode: h2
part: DOMTokenList [value: '']
prefix: null
previousElementSibling: null
previousSibling: text
scrollHeight: 0
scrollLeft: 0
scrollTop: 0
scrollWidth: 0
shadowRoot: null
slot: ""
spellcheck: true
style: CSSStyleDeclaration {
   accentColor: '', additiveSymbols: '', alignContent: '', alignItems: '', alignSelf: '',}
tabIndex: -1
tagName: "SPAN"
textContent: ""
title: ""
translate: true
virtualKeyboardPolicy: ""

完善实例

上述例子中,v-big的功能还没有实现,因为在自定义插件的big函数中我们还没有进行操作。

我们需要将拿到的值(binding.value)* 10 赋值给dom元素的innerText

element.innerText = binding.value *10
<!DOCTYPE html>
<html lang="en">
<head>
    .....
</head>
<body>
   ...
    <script type="text/javascript">
        Vue.config.productionTip = false
        new Vue({
    
            el:'#root',
            data:{
    n:1},
            directives:{
    
                big(element,binding){
    
                    element.innerText = binding.value *10
                },
            }
        })
    </script>
</body>
</html>

局部指令的对象写法

定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点。

<body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <div id="root">
        <input type="text" v-fbind:value="n">
        <button @click = 'n++'>点击加1</button>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false
        new Vue({
   
            el:'#root',
            data:{
   
                n:100
            },
            directives:{
   
                fbind(element,binding){
   
                    element.value = binding.value   //将绑定的值n(data中的100)赋值给input的value值
          element.focus()                 //input自动获取焦点
                },
            }
        })
    </script>
</body>
</html>

打开页面后并没有立即获取到焦点

但是点击按钮后获取到了焦点

这是为什么呢?先来回顾一下原生Dom的操作。

原生Dom创建一个自动获取焦点的输入框

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>Document</title>
        <style>
                .demo{
    background-color: orange;}
        </style>
    </head>
    <body>
        <button id="btn">点我创建一个输入框</button>
        <script type="text/javascript" >
        const btn = document.getElementById('btn')
        btn.onclick = ()=>{
    
                const input = document.createElement('input')
                document.body.appendChild(input)   //将创建的input元素插入到页面中
                input.focus()
        }
        </script>
    </body>
</html>

上述代码创建了一个输入框,并将其插入页面后自动获取焦点。这是可行的。

但是,如果将获取焦点的代码写在input插入页面之前,功能是无法实现的,因为这个时候页面还没有input元素,自然无法获取焦点。

btn.onclick = ()=>{
   
   const input = document.createElement('input')
   input.focus()
   document.body.appendChild(input)   //将创建的input元素插入到页面中
}

补充:其他Dom操作的书写时机参考

input.className = 'demo'

input.value = 99

input.onclick = ()=>{alert(1)}

document.body.appendChild(input) //将创建的input元素插入到页面中

input.parentElement.style.backgroundColor = 'skyblue'

可见,input获取焦点的时机是至关重要的。

vue中element.focus() 不生效的原因解析

<div id="root">
    <input type="text" v-fbind:value="n">
    <button @click = 'n++'>点击加1</button>
</div>

v-fbind:value="n" 是元素的绑定过程,指v-fbind与input的value指进行绑定,绑定完成后,执行了directives内的函数。但此时

...
元素并没有真正渲染在页面上,vue还没有将该模板进行真正的渲染!因此,此时, element.focus() 自然是不会生效的!

当点击button按钮式,此时input输入框已经真正的渲染在页面上了,因此,此时,input输入框会自动获取焦点。

可见,由于“directives:{指令名:回调函数} ”的定义方法中回调函数执行时机的问题,导致element.focus() 无法实现自动获取焦点的功能。

解决这一问题,可以使用 directives的对象写法。

directives的对象写法

语法回顾:directives:{指令名:配置对象}

配置对象中拥有三个内置函数bind、inserted、update

bind:{
    
  //1.指令与元素成功绑定时(一上来)
  bind(element,binding){
    
    console.log('首先执行')
  },
  //2.指令所在元素被插入页面时
  inserted(element,binding){
    
    console.log('其次执行')
  },
  //3.指令所在的模板被重新解析时
  update(element,binding){
    
    console.log('点击执行')
  }
}

因此,我们可以将代码进行优化,完成input输入框自动获取焦点功能。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>自定义指令</title>
        <script type="text/javascript" src="../js/vue.js"></script>
    </head>
    <body>
        <div id="root">
            <h2>{
   {name}}</h2>
            <h2>当前的n值是:<span v-text="n"></span> </h2>
            <!-- <h2>放大10倍后的n值是:<span v-big-number="n"></span> </h2> -->
            <h2>放大10倍后的n值是:<span v-big="n"></span> </h2>
            <button @click="n++">点我n+1</button>
            <hr/>
            <input type="text" v-fbind:value="n">
        </div>
    </body>

    <script type="text/javascript">
        Vue.config.productionTip = false
        new Vue({
     
        el:'#root',
        data:{
     
            name:'222',
            n:1
        },
        directives:{
     
            big(element,binding){
     
            element.innerText = binding.value * 10
          },
      fbind:{
     
        //指令与元素成功绑定时(一上来)
        bind(element,binding){
     
          element.value = binding.value
        },
        //指令所在元素被插入页面时
        inserted(element,binding){
     
          element.focus()
        },
        //指令所在的模板被重新解析时
        update(element,binding){
     
          element.value = binding.value
        }
      }
    }
  })
    </script>
</html>

指令总结

定义语法:

(1).局部指令:

new Vue({
    
     directives:{
    指令名:配置对象}   或    directives{
    指令名:回调函数}
})

(2).全局指令:

Vue.directive(指令名,配置对象) 或   Vue.directive(指令名,回调函数)

配置对象中常用的3个回调:

(1).bind:指令与元素成功绑定时调用。

(2).inserted:指令所在元素被插入页面时调用。

(3).update:指令所在模板结构被重新解析时调用。

备注

  • 指令定义时不加v-,但使用时要加v-;
  • 指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。

全局组件的使用思路

直接使用

Vue.directive(big, {
     
  inserted(element,binding){
    
    element.text = "哈哈哈哈哈哈"
  }
})

Vue.directive(big2, function(element,binding){
    
    element.text = "你是狗"
})

Vue.directive(big3, (element,binding) => {
    
    element.text = "你是狗!!!"
})

使用插件形式

// 自定义 MyPlugin 插件
//MyPlugin必须暴露一个install方法给Vue.use,没有install方法会把里面的函数作为install方法
Vue.use(MyPlugin)

new Vue({
    
  // ...组件选项
})
MyPlugin = function(Vue, options){
    

  Vue.directive('my-directive', {
    ...})
  Vue.directive('my-directive', {
    ...})

}

Vue.use(MyPlugin)
MyPlugin.install = function (Vue, options) {
    
  // 1. 添加全局方法或 property
  Vue.myGlobalMethod = function () {
     // 逻辑...}

  // 2. 添加全局资源()自定义指令
  Vue.directive('my-directive', {
    
    bind (el, binding, vnode, oldVnode) {
    
      // 逻辑...
    }
    ...
  })

  // 3. 注入组件选项
  Vue.mixin({
     created: function () {
    
      // 逻辑...
    }
    ...
  })

  // 4. 添加实例方法
  Vue.prototype.$myMethod = function (methodOptions) {
    
    // 逻辑...
  }
}

全局注册多个自定义插件

直接书写多个Vue.directive可以注册多个自定义插件,但是这样比较麻烦

Vue.directive(big, {
     
  inserted(element,binding){
    
    element.text = "哈哈哈哈哈哈"
  }
})

Vue.directive(big2, function(element,binding){
    
    element.text = "你是狗"
})

Vue.directive(big3, (element,binding) => {
    
    element.text = "你是狗!!!"
})

因此,我们可以使用插件安装的方式进行优化。

比如,我们现在定义了一个allDirectives对象,这个对象里写了很多个自定义指令,并把它写在函数里给暴露出去。

const allDirectives = {
    
  big:{
     
    inserted(element,binding){
      }
  },
  big1:function(element,binding){
      },
  big2(element,binding){
       },
  big3:(element,binding)=> {
        }
}

const allDirectivesFunction = function ( Vue,options ){
    
  Vue.directive('big',allDirectives.big)
  Vue.directive('big1',allDirectives.big1)
  Vue.directive('big2',allDirectives.big2)
  Vue.directive('big3',allDirectives.big3)
}

export default allDirectivesFunction

然后再main.js中引入使用

// 需要注册的全局指令
import directive from './directives'
Vue.use(directive)

接下来,将allDirectivesFunction进行简化

const allDirectives = {
    
  big:{
     
    inserted(element,binding){
      }
  },
  big1:function(element,binding){
      },
  big2(element,binding){
       },
  big3:(element,binding)=> {
        }
}

const allDirectivesFunction = function ( Vue,options ){
    
  Object.keys(allDirectives).forEach((name) => Vue.directive(name, allDirectives[name]))
}

export default allDirectivesFunction
const allDirectives = {
  big:{ 
    inserted(element,binding){  }
  },
  big1:function(element,binding){  },
  big2(element,binding){   },
  big3:(element,binding)=> {    }
}

export default ( Vue,options ) => {
  Object.keys(allDirectives).forEach((name) => Vue.directive(name, allDirectives[name]))
}
相关文章
|
3天前
|
编解码 Java 程序员
写代码还有专业的编程显示器?
写代码已经十个年头了, 一直都是习惯直接用一台Mac电脑写代码 偶尔接一个显示器, 但是可能因为公司配的显示器不怎么样, 还要接转接头 搞得桌面杂乱无章,分辨率也低,感觉屏幕还是Mac自带的看着舒服
|
5天前
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
1546 5
|
1月前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
8天前
|
人工智能 Rust Java
10月更文挑战赛火热启动,坚持热爱坚持创作!
开发者社区10月更文挑战,寻找热爱技术内容创作的你,欢迎来创作!
619 23
|
5天前
|
存储 SQL 关系型数据库
彻底搞懂InnoDB的MVCC多版本并发控制
本文详细介绍了InnoDB存储引擎中的两种并发控制方法:MVCC(多版本并发控制)和LBCC(基于锁的并发控制)。MVCC通过记录版本信息和使用快照读取机制,实现了高并发下的读写操作,而LBCC则通过加锁机制控制并发访问。文章深入探讨了MVCC的工作原理,包括插入、删除、修改流程及查询过程中的快照读取机制。通过多个案例演示了不同隔离级别下MVCC的具体表现,并解释了事务ID的分配和管理方式。最后,对比了四种隔离级别的性能特点,帮助读者理解如何根据具体需求选择合适的隔离级别以优化数据库性能。
207 3
|
12天前
|
Linux 虚拟化 开发者
一键将CentOs的yum源更换为国内阿里yum源
一键将CentOs的yum源更换为国内阿里yum源
594 5
|
11天前
|
JSON 自然语言处理 数据管理
阿里云百炼产品月刊【2024年9月】
阿里云百炼产品月刊【2024年9月】,涵盖本月产品和功能发布、活动,应用实践等内容,帮助您快速了解阿里云百炼产品的最新动态。
阿里云百炼产品月刊【2024年9月】
|
24天前
|
存储 关系型数据库 分布式数据库
GraphRAG:基于PolarDB+通义千问+LangChain的知识图谱+大模型最佳实践
本文介绍了如何使用PolarDB、通义千问和LangChain搭建GraphRAG系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图+向量联合搜索增强了问答准确性。PolarDB支持AGE图引擎和pgvector插件,实现图数据和向量数据的统一存储与检索,提升了RAG系统的性能和效果。
|
8天前
|
XML 安全 Java
【Maven】依赖管理,Maven仓库,Maven核心功能
【Maven】依赖管理,Maven仓库,Maven核心功能
252 3
|
10天前
|
存储 人工智能 搜索推荐
数据治理,是时候打破刻板印象了
瓴羊智能数据建设与治理产品Datapin全面升级,可演进扩展的数据架构体系为企业数据治理预留发展空间,推出敏捷版用以解决企业数据量不大但需构建数据的场景问题,基于大模型打造的DataAgent更是为企业用好数据资产提供了便利。
328 2