Vue learning-in-depth analysis of custom instructions

One, custom instructions

1 Introduction

We can write a custom instruction to manipulate DOM elements to achieve the purpose of code reuse. Note that in Vue, the main form of code reuse and abstraction is components. However, in some cases, you still need to perform low-level operations on ordinary DOM elements, and custom instructions will be used in this case.

Global registration instructions:

Vue.directive('focus', {
    
    /** */})

Partial registration instructions

const vm = new Vue({
    
    
  el: '#app',
  directives: {
    
    
    focus: {
    
    /** */}
  }
})

use:

<input v-focus></input>

For example, write an input box with automatic focus:

<div id="app">
    <!-- <input type="text" v-focus> -->
    <my-input></my-input>//只能同时对焦一个哦
</div>
<script>
    Vue.directive('focus',{
     
     
        inserted: function(el){
     
     
            el.focus();
        }
    });
    Vue.component('my-input',{
     
     
        template: `
            <input type='text' v-focus>
        `
    });
    const vm = new Vue({
     
     
        el: '#app',
        data: {
     
     

        }
    });
</script>

At this point, use the v-focus command on the input element to achieve automatic focus.

2. Hook function

The custom instruction object provides hook functions for us to use, and these hook functions are optional.

1.bind

Only called once, when the instruction is bound to the element for the first time. Here you can perform one-time initialization settings.

2.inserted

Called when the bound element is inserted into the parent node (only the parent node is guaranteed to exist, but not necessarily already inserted in the document).

3.update

Called when the VNode of the component is updated, but may occur before its child VNode is updated .

4.componentUpdated

Called after the VNode of the component where the instruction is located and its sub-VNodes are all updated.

5.unbind

It is called only once, when the instruction is unbound from the element (the bound Dom element is removed by Vue).

<div id="app">
    <my-input :name='name' v-if='show'></my-input>
</div>
<script>
    Vue.directive('focus',{
     
     
        bind () {
     
     
            console.log('bind');
        },
        inserted (el) {
     
     
            // el.focus();
            console.log('inserted');
        },
        update () {
     
     
            console.log('update');
        },
        componentUpdated () {
     
     
            console.log('componentUpdated');
        },
        unbind() {
     
     
            console.log('unbind');
        }
    });
    Vue.component('my-input',{
     
     
        props: ['name'],
        template: `
            <input type='text' v-focus="name">
        `
    });
    const vm = new Vue({
     
     
        el: '#app',
        data: {
     
     
            name: 'jimo',
            show: true
        }
    });
</script>

Insert picture description here

3. Hook function parameters

  • el: The element bound by the instruction can be used to directly manipulate the DOM.

  • binding: object, contains the following attributes:

    • name: The name of the command, excluding the v- prefix.
    • value: the bound value of the instruction, for example: v-focus="name"middle, the bound value 'jimo'.
    • oldValue: The previous value bound by the instruction, only available in the update and componentUpdated hooks. It is available regardless of whether the value changes.
    • expression: command expression in string form. For example v-focus="1 + 1", the expression is "1 + 1".
    • arg: The parameter passed to the instruction, optional. For example v-focus:src, the parameters "src".
    • modifiers: An object containing modifiers. For example: v-focus:src.x.yin, the modifier object is { x: true, y: true }.
  • vnode: A virtual node generated by Vue compilation.
    Insert picture description here

  • oldVnode: The last virtual node, only available in the update and componentUpdated hooks.

4. Practice

1. Simulate v-show

// 绑定的值为false,display为none,值为true,display为""
Vue.directive('myshow',{
    
    
    bind(el,binding){
    
    
        const {
    
     value } = binding;
        const display = value ? '' : 'none';
        el.style.display = display;
    },
    update(el,binding){
    
    
        const {
    
     value } = binding;
        const display = value ? '' : 'none';
        el.style.display = display;
    }
});

2. Simulate v-model

// 1. 通过绑定的数据,给元素设置value
// 2. 当触发input事件时,去更改数据的值
// 3. 更改数据后,同步input的value值
Vue.directive('mymodel', {
    
    
  bind (el, binding, vnode) {
    
    
    const vm = vnode.context;
    const {
    
     value, expression } = binding;
    el.value = value;

    el.oninput = function (e) {
    
    
      const inputVal = el.value;
      vm[expression] = inputVal;
    }
  },
  update (el, binding) {
    
    
    const {
    
     value } = binding;
    el.value = value;
  },
})

3. Write a v-slice (intercept the text box)

<div id="app">
    <input type="text" v-slice:7.number='name'>
    <!-- <my-input :name='name'></my-input> -->
    {
    
    {
    
     name }}

</div>
<script>
	Vue.directive('slice', {
    
    
	    bind (el, binding, vnode) {
    
    
	    	const vm = vnode.context;
	    	let {
    
     value, expression, arg, modifiers } = binding;

	    	if(modifiers.number) {
    
    
	      		value = value.replace(/[^0-9]/g, '');
	    	}

	    	el.value = value.slice(0, arg);
	    	vm[expression] = value.slice(0, arg);

	    	el.oninput = function (e) {
    
    
	      		let inputVal = el.value;

 	     		if(modifiers.number) {
    
    
 	       			inputVal = inputVal.replace(/[^0-9]/g, '');
 	     		}

	      		el.value = inputVal.slice(0, arg);
	      		vm[expression] = inputVal.slice(0, arg);
	    	}
	  },
	  update (el, binding, vnode) {
    
    
	    	const vm = vnode.context;
	    	let {
    
     value, arg, expression, modifiers } = binding;

	    	if(modifiers.number) {
    
    
	      		value = value.replace(/[^0-9]/g, '');
	    	}

	    	el.value = value.slice(0, arg);
	    	vm[expression] = value.slice(0, arg);
	  	},
	});
    const vm = new Vue({
    
    
        el: '#app',
        data: {
    
    
            name: 'jimohuabukai',
            show: true
        }
    });
</script>

Insert picture description here

4. Dynamic command parameters

The parameters of the instruction can be dynamic. Such as: v-directive:[arguments]="value, argumentparameters can be updated according to the data component instance. (At this time, you need to monitor after changing the argument in the update)

Rewrite v-slice

Vue.directive('slice', {
    
    
  bind (el, binding, vnode) {
    
    
    const vm = vnode.context;
    let {
    
     value, expression, arg, modifiers } = binding;

    if(modifiers.number) {
    
    
      value = value.replace(/[^0-9]/g, '');
    }

    el.value = value.slice(0, arg);
    vm[expression] = value.slice(0, arg);

    el.oninput = function (e) {
    
    
      let inputVal = el.value;

      if(modifiers.number) {
    
    
        inputVal = inputVal.replace(/[^0-9]/g, '');
      }

      el.value = inputVal.slice(0, arg);
      vm[expression] = inputVal.slice(0, arg);
    }
  },
  update (el, binding, vnode) {
    
    
    const vm = vnode.context;
    let {
    
     value, arg, expression, modifiers } = binding;
    
    if(modifiers.number) {
    
    
      value = value.replace(/[^0-9]/g, '');
    }

    el.value = value.slice(0, arg);
    vm[expression] = value.slice(0, arg);

    el.oninput = function (e) {
    
    
      let inputVal = el.value;

      if(modifiers.number) {
    
    
        inputVal = inputVal.replace(/[^0-9]/g, '');
      }

      el.value = inputVal.slice(0, arg);
      vm[expression] = inputVal.slice(0, arg);
    }
  }
});

5. Function shorthand

When you want to trigger the same behavior in bind and update without caring about other hooks, you can write it in the form of a function:

Vue.directive('myshow', (el, binding) => {
    
    
  const {
    
     value } = binding;
  const display = value ? '' : 'none';
  el.style.display = display;
});
Vue.directive('slice', (el, binding, vnode) => {
    
    
  const vm = vnode.context;
  let {
    
     value, expression, arg, modifiers } = binding;

  if(modifiers.number) {
    
    
    value = value.replace(/[^0-9]/g, '');
  };

  el.value = value.slice(0, arg);
  vm[expression] = value.slice(0, arg);

  el.oninput = function (e) {
    
    
    let inputVal = el.value;

    if(modifiers.number) {
    
    
      inputVal = inputVal.replace(/[^0-9]/g, '');
    };

    el.value = inputVal.slice(0, arg);
    vm[expression] = inputVal.slice(0, arg);
  }
});

6. Object literal

If the custom instruction requires multiple values, you can pass in a JS object literal. The instruction function can accept all legal JS expressions.

<div v-demo="{ color: 'white', text: 'hello!' }"></div>
Vue.directive('demo', function (el, binding) {
    
    
  console.log(binding.value.color) // => "white"
  console.log(binding.value.text)  // => "hello!"
});

Guess you like

Origin blog.csdn.net/xun__xing/article/details/108517253