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天前
|
JavaScript 前端开发
如何在 Vue 项目中配置 Tree Shaking?
通过以上针对 Webpack 或 Rollup 的配置方法,就可以在 Vue 项目中有效地启用 Tree Shaking,从而优化项目的打包体积,提高项目的性能和加载速度。在实际配置过程中,需要根据项目的具体情况和需求,对配置进行适当的调整和优化。
|
3天前
|
存储 缓存 JavaScript
如何在大型 Vue 应用中有效地管理计算属性和侦听器
在大型 Vue 应用中,合理管理计算属性和侦听器是优化性能和维护性的关键。本文介绍了如何通过模块化、状态管理和避免冗余计算等方法,有效提升应用的响应性和可维护性。
|
2天前
|
JavaScript 前端开发 UED
vue学习第二章
欢迎来到我的博客!我是一名自学了2年半前端的大一学生,熟悉JavaScript与Vue,目前正在向全栈方向发展。如果你从我的博客中有所收获,欢迎关注我,我将持续更新更多优质文章。你的支持是我最大的动力!🎉🎉🎉
|
2天前
|
JavaScript 前端开发 开发者
vue学习第一章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript和Vue的大一学生。自学前端2年半,熟悉JavaScript与Vue,正向全栈方向发展。博客内容涵盖Vue基础、列表展示及计数器案例等,希望能对你有所帮助。关注我,持续更新中!🎉🎉🎉
|
JavaScript 测试技术 容器
Vue2+VueRouter2+webpack 构建项目
1). 安装Node环境和npm包管理工具 检测版本 node -v npm -v 图1.png 2). 安装vue-cli(vue脚手架) npm install -g vue-cli --registry=https://registry.
1047 0
|
3天前
|
存储 缓存 JavaScript
在 Vue 中使用 computed 和 watch 时,性能问题探讨
本文探讨了在 Vue.js 中使用 computed 计算属性和 watch 监听器时可能遇到的性能问题,并提供了优化建议,帮助开发者提高应用性能。
|
3天前
|
存储 缓存 JavaScript
Vue 中 computed 和 watch 的差异
Vue 中的 `computed` 和 `watch` 都用于处理数据变化,但使用场景不同。`computed` 用于计算属性,依赖于其他数据自动更新;`watch` 用于监听数据变化,执行异步或复杂操作。
|
4天前
|
存储 JavaScript 开发者
Vue 组件间通信的最佳实践
本文总结了 Vue.js 中组件间通信的多种方法,包括 props、事件、Vuex 状态管理等,帮助开发者选择最适合项目需求的通信方式,提高开发效率和代码可维护性。
|
4天前
|
存储 JavaScript
Vue 组件间如何通信
Vue组件间通信是指在Vue应用中,不同组件之间传递数据和事件的方法。常用的方式有:props、自定义事件、$emit、$attrs、$refs、provide/inject、Vuex等。掌握这些方法可以实现父子组件、兄弟组件及跨级组件间的高效通信。
|
9天前
|
JavaScript
Vue基础知识总结 4:vue组件化开发
Vue基础知识总结 4:vue组件化开发