This is probably the most concise tutorial on Vue custom directives

Basic introduction

In addition to the default built-in directives for core functions (v-model and v-show), Vue also allows registration of custom directives. Custom directives can perform low-level operations on common DOM elements.

basic grammar

Local instructions:

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

Global directives:

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

Function writing of local instructions

Requirements: Define a v-big command, which is similar to v-text, but can increase the binding value by 10 times.

<!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('big==========================================',this) 
          console.log(binding)
          console.log(element)
          console.dir(element)
				},
			}
		})
	</script>
</body>
</html>

Timing of command execution

Open the developer debugging tool directly in the browser, you can see that the content of console.log has been printed

Among them, this in the instruction points to the global window object

This means: when the instruction is written in the label, the instruction function will be called and executed! In addition, changing the value behind v-bid will also execute the command.

1. When the instruction is successfully bound to the element (one up).

2. When the template where the directive is located is reparsed.

Parameters accepted by the command

The big instruction accepts two parameters (both formal parameters)

element binding

binding

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

Except for el, other parameters should be read-only and should not be modified. If you need to share data between hooks, it is recommended to do it through the element's dataset .

element

  • element : The element to which the directive is bound, which can be used to directly manipulate the DOM.
<span></span>

console.log(element) prints out the span tag (actually the Dom object of span)

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: ""

perfect example

In the above example, the function of v-big has not been realized, because we have not operated in the big function of the custom plug-in.

We need to assign the obtained value (binding.value) * 10 to the innerText of the dom element

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>

Object Writing for Local Instructions

Define a v-fbind command, which is similar to v-bind, but allows the input element it is bound to get the focus by default.

<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>

After opening the page, it does not get the focus immediately

But after clicking the button, it gets the focus

Why is this? Let's review the operation of native Dom first.

Native Dom creates an input box that automatically gets focus

<!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>

The above code creates an input box and automatically gains focus when it is inserted into the page. It works.

However, if the code for obtaining the focus is written before the input is inserted into the page, the function cannot be realized, because the page does not have an input element at this time, and naturally the focus cannot be obtained.

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

Supplement: Reference for writing timing of other Dom operations

input.className = ‘demo’

input.value = 99

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

document.body.appendChild(input) //Insert the created input element into the page

input.parentElement.style.backgroundColor = ‘skyblue’

It can be seen that the timing of input getting focus is crucial.

Analysis of the reasons why element.focus() does not take effect in Vue

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

v-fbind:value="n" is the binding process of the element, which refers to the binding between v-fbind and the value of the input. After the binding is completed, the function in the directives is executed. but at this time

The element is not actually rendered on the page, and vue has not actually rendered the template yet! Therefore, at this time, element.focus() will naturally not take effect!

When the button button is clicked, the input input box has been actually rendered on the page at this time, so at this time, the input input box will automatically get the focus.

It can be seen that due to the problem of the execution timing of the callback function in the definition method of " directives:{instruction name: callback function} ", element.focus() cannot realize the function of automatically obtaining the focus.

To solve this problem, you can use the object writing method of directives.

The object writing method of directives

Grammar review: directives: {directive name: configuration object}

There are three built-in functions bind, inserted, and update in the configuration object .

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

Therefore, we can optimize the code to complete the automatic focus function of the input input box.

<!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:'尚硅谷',
				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>

Command summary

Definition syntax:

(1). Local instructions:

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

(2). Global instructions:

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

Three commonly used callbacks in configuration objects:

(1).bind: Called when the directive is successfully bound to the element.

(2).inserted: Called when the element where the instruction is located is inserted into the page.

(3).update: Called when the template structure where the directive is located is reparsed.

Remark

  • Do not add v- when the instruction is defined, but add v- when using it;
  • If the instruction name is multiple words, use kebab-case naming method instead of camelCase naming method.

Ideas for using global components

Use directly

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

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

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

Use the plug-in form

// 自定义 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) {
    
    
    // 逻辑...
  }
}

Globally register multiple custom plugins

Directly writing multiple Vue.directives can register multiple custom plug-ins, but this is more troublesome

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

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

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

Therefore, we can use the way of plug-in installation for optimization.

For example, we now define an allDirectives object, in which many custom instructions are written, and written in a function to expose them.

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

Then introduce and use it in main.js

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

Next, simplify 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]))
}

Guess you like

Origin blog.csdn.net/weixin_46769087/article/details/131550643