This is the first day of my participation in the Gwen Challenge in November. Check out the details: the last Gwen Challenge in 2021.

preface

Vue is simple and easy to use in many places, such as its built-in 32+ modifier, can be very convenient we prevent bubbling, prevent default events, mouse event processing, system keyboard events and so on, so that we can quickly get business, it is not too convenient oh!!

Delay you 15 minutes you can harvest:

  1. The meaning and use of 32+ modifiers, including event modifiers, mouse modifiers, form modifiers, system modifiers, and so on

  2. How to use Webpack to dynamically register vUE routes, no longer handwritten route configuration!

Examples in the article are put on github source code, you can also click to see the example directly

How do I dynamically register routes?

Each modifier example in this article is hosted by a page, and you’d be wise not to manually import dozens of.vue files and configure routing.

Is there a way to automate route registration for us?

1. File directory structure

The directory structure (excluding other file directories) is roughly as follows

├ ─ ─ package. Json └ ─ ─ the SRC ├ ─ ─ App. Vue ├ ─ ─ the main, js ├ ─ ─ the router. The js └ ─ ─ views ├ ─ ─ the About the vue ├ ─ ─ Home. Vue └ ─ ─ modifiers ├ ─ ─ The capture. Vue ├ ─ ─ once. Vue ├ ─ ─ the order. The vue ├ ─ ─ passive. Vue ├ ─ ─ hasting. Vue ├ ─ ─ the self. The vue └ ─ ─ stop. Vue └ ─ ─...Copy the code

2. Configure the desired route

The final configuration given to vue-Router looks something like this, with the most important parts of each configuration being path, name, and Component

[{"path": "/home"."name": "home"."component": {
      "name": "Home"."methods": {},
      "staticRenderFns": []."_compiled": true."_scopeId": "data-v-fae5bece"."beforeCreate": [
        null]."beforeDestroy": [
        null]."__file": "src/views/Home.vue"}}, {"path": "/modifiers/capture"."name": "modifiersCapture"."component": {
      "name": "capture"."methods": {},
      "staticRenderFns": []."_compiled": true."_scopeId": "data-v-63b4eeee"."beforeCreate": [
        null]."beforeDestroy": [
        null]."__file": "src/views/modifiers/capture.vue"}},...// Configure other routes
]

Copy the code

3. Require. context Dynamically registers routes

With the ability of Webpack require.context, it is very easy to implement the mapping of the directory to the route configuration, the source code is as follows

const registerRoutes = () = > {
  const contextInfo = require.context('./views'.true./.vue$/)
  const routes = contextInfo.keys().map((filePath) = > {
    // filePath =./Home. Vue,./ modiFIERS /capture. Vue
    // path we want to be /home, / modiFIERS /capture
    // So we need to replace the starting./ and. Vue with null
    const path = filePath.toLowerCase().replace(/^\.|\.vue/g.' ')
    // name to make /home, / modiFIERS /capture small humps
    // Replace the first/with a capital /
    const name = path.replace(/ ^ / / /.' ').replace(/\/(\w)/.($0, $1) = > $1.toUpperCase())
    // require to read the contents of the.vue file
    const component = require(`./views${filePath.replace(/ ^ \. /.' ')}`).default

    return {
      path,
      name,
      component
    }
  })

  return routes
}

Copy the code

The effect

After the above simple processing, dynamic route registration is complete! You can also click vue-demos to see the effect

Event modifier

1. Two ways to stop bubbling


<template>
  <div class="parent" @click="onClickParent">I am a father<div class="child" @click="onClickChild">I am a son</div>
  </div> 
</template>

export default {
  name: 'stop'.methods: {
    onClickParent () {
      console.log('I am the father')
    },
    onClickChild () {
      console.log('I am a son')}}}Copy the code

When you click on the child node it not only prints I’m the son but also prints I’m the father because the event is bubbling up. Is there any way to prevent events from bubbling up on child nodes?

1 .stop

Just add the.stop modifier to prevent the event from bubbling.

When the.stop modifier is added, it will just say I am the son

<template>
  <div class="parent" @click="onClickParent">I am a father<div class="child" @click.stop="onClickChild">I am a son</div>
  </div> 
</template>

Copy the code

2. event.stopPropagation

Of course, we can also prevent bubbling by calling Event. stopPropagation. Modifiers are recommended, however, so that your functions are more focused on logic rather than DOM event details

export default {
  name: 'stop'.methods: {
    onClickChild (event) {
      console.log('I am a son')
      event.stopPropagation()
    }
  }
}


Copy the code

2. Two ways to block default events

There are two ways to prevent bubbling in Vue, but what about preventing default events?

1 .prevent

<template>
  <div class="prevent">
    <a href="https://juejin.cn/" @click="onNoPrevent">Click to jump to digging gold</a>
    <br />
    <br />
    <a href="https://juejin.cn/" @click.prevent="onPrevent">Prevents default events from jumping to nuggets</a>
  </div>
</template>

export default {
  name: 'prevent'.methods: {
    onNoPrevent () {
      console.log('Did not block the default event')
    },
    onPrevent () {
      console.log('Prevent default events')}}}Copy the code

Simply add.prevent to easily block default events

2.event.preventDefault()

As well as preventing bubbling, we can prevent the default event by calling the event object’s preventDefault method

export default {
  name: 'prevent'.methods: {
    onPrevent (event) {
      console.log('Prevent default events')
      event.preventDefault()
    }
  }
}

Copy the code

3 .capture

By default, event flows are bubbling (from inside out). What if you want to capture (from outside in)?

<template>
  <div class="capture parent" @click.capture="onClickParent">The parent node<div class="child" @click.capture="onClickChild">Since the nodes</div>
  </div>
</template>

export default {
  name: 'capture'.methods: {
    onClickParent () {
      console.log('I'm the parent')
    },
    onClickChild () {
      console.log('I'm a child node')}}}Copy the code

Without the catpture modifier, clicking on the child will print I am the parent and I am the child, and vice versa

4 .self

The event callback is fired only if event.target is the current element itself

<template>
  <div class="self" @click.self="onClickSelf">
    <div class="inner" @click="onClickInner"></div>
  </div>
</template>

export default {
  name: 'self'.methods: {
    onClickSelf () {
      console.log('I'm the self node')
    },
    onClickInner () {
      console.log('I'm the inner node')}}}Copy the code

Without the self modifier, clicking on the inner node will also trigger the self event, which will only print out that I am the self node if the element that triggered the event is itself self

Pause: What about the order of modifiers?

Four modifiers have been reviewed and are easy to understand when used alone, but note the following on the official website

How do you understand that? Let’s look at two chestnuts

<template>
  <div class="order">
    <div class="order-0">
      <a href="https://juejin.cn/" class="order-parent" @click.self.prevent="onClickParent">I am the parent node, will jump to dig gold<br />
        <span class="order-child" @click="onClickChild">I'm a child node</span>
      </a>
      <hr />
    </div>
    <div class="order-2">
      <a href="https://juejin.cn/" class="order-parent" @click.prevent.self="onClickParent">I am the parent node and cannot jump to digging gold<br />
        <span class="order-child" @click="onClickChild">I'm a child node</span>
      </a>
    </div>
  </div> 
</template>

export default {
  name: 'order'.methods: {
    onClickParent () {
      console.log('I'm the parent')
    },
    onClickChild () {
      console.log('I'm a child node')}}}Copy the code

Can you guess what happens to the above code?

  1. The first thing to make clear is that clicking on the children above and below does not trigger a click event for the parent node

  2. Clicking on the parent node below will print out that I am the parent node, but will not jump to digging gold

  3. Clicking on the parent node above will print out that I am the parent node and will not jump to digging gold

But if you click on the child node above, will the parent node jump to gold? The answer is to

Why is that?

[email protected]=”onClickParent” means that the default event is blocked and the onClickParent callback is executed when the clicked element is element A itself.

When you click on the SPAN element, the click event will be passed to A due to bubbles, but a will then determine that the event was not triggered by itself and will not block the default event (and thus jump), and of course will not trigger the onClickParent callback

[email protected]=”onClickParent”

The default event is blocked either by the child node or by the click itself, and the onClickParent callback is executed only if the click event is triggered by the A element itself.

Looking back, do you understand the meaning of the order of events?

5. once

As the name implies, the event only fires once

<template>
  <div class="once" @click.once="onClickOnce">Only trigger once</div>
</template>

export default {
  name: 'once'.methods: {
    onClickOnce () {
      console.log('Once, I'm only going to trigger a click event callback')}}}Copy the code

After I trigger a click, no matter how much I click, the callback will not trigger.

6 .native

We know that on custom components, only custom events can be listened for. Some native events (such as click) cannot be triggered directly, but using the. Native modifier can help us do this

native.vue

<template>
  <div class="native-custom">
    <input type="text" @keydown="onKeydown">
  </div>
</template>

export default {
  name: 'nativeCustom'.methods: {
    onKeydown () {
      this.$emit('onKeydown')}}}Copy the code

custom.vue

<template>
  <div class="native">
    <! -- Add. Native event to listen successfully -->
    <NativeCustom @onKeydown="onKeydown" @click.native="onClick" />
  </div>
</template>

import NativeCustom from '.. /.. /components/native.vue'

export default {
  name: 'native'.components: {
    NativeCustom
  },
  methods: {
    onKeydown () {
      console.log('onKeydown')
    },
    onClick () {
      console.log('onClick')}}}Copy the code

7 .passive

Vue provides the.passive modifier for the passive option in the addEventListener

<! The default behavior of the scroll event (that is, the scroll behavior) will be triggered immediately --> <! Instead of waiting for 'onScroll' to finish --> <! -- this includes' event.preventdefault () '--> <div V-on :scroll. Passive ="onScroll">... </div>Copy the code

This modifier has not been used to improve scrolling performance

This modifier has not been used to improve scrolling performance

This modifier has not been used to improve scrolling performance

V – bind modifier

8 .sync

What is a convenient way to bind a property value bidirectionally between parent and child components? Yeah, just the.sync modifier

The parent component

<template>
  <div class="sync-parent">I am the parent component: {{text}}<Child :text.sync="text" />
  </div>
</template>

import Child from './child.vue'

export default {
  name: 'SyncParent',
  data () {
    return {
      text: 'parent'}},components: {
    Child,
  }
}

Copy the code

Child components

<template>
  <div class="child">I am the child component:<input type="text" v-model="value" @input="onInput">
  </div>
</template>

export default {
  name: 'child'.props: {
    text: {
      type: String
    }
  },
  data () {
    return {
      value: this.text
    }
  },
  methods: {
    onInput () {
      // Note that XXX must be in the form of update: XXX, i.e. property prop
      this.$emit('update:text'.this.value)
    }
  }
}

Copy the code

9 .camel

The. Camel modifier allows the v-bind property name to be humped when using DOM templates, such as the viewBox property for SVG:

<svg :view-box.camel="viewBox"></svg>
Copy the code

10 .prop

Prop is a DOM property binding, not an attribute binding. `.

What does it do?

  1. Store variables with custom attributes to avoid exposing data
  2. Prevents contamination of HTML structures

For example, there is the following code

<template>
  <div class="prop">
    <div class="prop-item" :my-name="prop"></div>// Finally become<div my-name="hello prop" class="prop-item"></div>
    <div class="prop-item" :my-name.prop="prop2"></div>// Finally become<div class="prop-item"></div>
    <button @click="onGetResult">To get the results</button>
  </div>
</template>

export default {
  name: 'prop',
  data () {
    return {
      prop: 'hello prop'.prop2: 'hello prop2'}},methods: {
    onGetResult () {
      const $refProp = this.$refs.prop
      const $refProp2 = this.$refs.prop2

      console.log($refProp.getAttribute('my-name')) // hello prop
      console.log($refProp2.getAttribute('my-name')) // null}}}Copy the code

As you can see from the example, the my-name attribute of the unused. Prop modifier is bound to the attribute of the DOM node, resulting in exposure.

Mouse modifier

There are also modifiers that can be used quickly when we want to listen to the user click on the left, right, or middle key. They are.left,.right, and middle. Let’s try an example

According to MDN mouseEvent. button, introduction.

In the outermost div. Mouse, listen for the mousedown event to see which mouse key the user clicked. The three buttons use three modifier shortcuts to listen for the left, middle, and right buttons and print left, middle, and right buttons

<template>
  <div class="mouse" @mousedown="onMousedown">
    <button @click.left="onClickBtn('left')">left</button>
    <button @click.middle="onClickBtn('middle')">middle</button>
    <button @click.right="onClickBtn('right')">right</button>
  </div>
</template>

export default {
  name: 'mouse',
  mounted () {

  },
  methods: {
    onClickBtn (msg) {
      console.log(msg)
    },
    onMousedown (event) {
      const mosueMsgMap = {
        0: 'Left mouse button'.1: 'Middle mouse button'.2: 'Right mouse button'
      }
      console.log('Clicked', mosueMsgMap[event.button])
    }
  }
}
Copy the code

Without the mouse back, the middle key click temporarily can not demonstrate, will be supplemented later

11 .left

As in the previous example, listen for the left mouse click

12 .right

As in the previous example, listen for right mouse clicks

13 .middle

As in the previous example, listen for the middle mouse click

Form-related modifiers

14 .trim

For input content, you want to filter the beginning and end of the space should do?

<template>
  <div class="trim">
    <div class="trim-item">
      <input type="text" v-model="name">
      <p>User name:<span>{{ name }}</span></p>
    </div>
    <div class="trim-item">
      <input type="text" v-model.trim="name2">
      <p>User Name 2:<span>{{ name2 }}</span></p>
    </div>
  </div>
</template>

export default {
  name: 'trim',
  data () {
    return {
      name: '',
      name2: '',
    }
  },
  watch: {
    name (newVal) {
      console.log(`'----${newVal}----'`)
    },
    name2 (newVal) {
      console.log(`'----${newVal}----'`)
    },
  }
}

Copy the code

The.trim modifier is handy

15 .lazy

The V-model is familiar. By default, each time an input event is triggered, the value of the input box is synchronized in real time with the data bound to it. But what if you want to update data when the cursor leaves?

Idea 1: Bind the change event and get the value of target manually in the event callback

Idea 2: Use the lazy modifier directly

<template>
  <div class="lazy">
    <div class="lazy-item">
      <input type="text" v-model="text">
      <p>Lazy: {{text}}</p>
    </div>

    <div class="lazy-item">
      <input type="text" v-model.lazy="text2">
      <p>.lazy: {{ text2 }}</p>
    </div>
  </div>
</template>

export default {
  name: 'lazy',
  data () {
    return {
      text: '',
      text2: ''
    }
  }
}

Copy the code

You can see that after adding the lazy modifier, the value entered in the second input box is not reflected in real time, but the cursor moves away from the real, and the data in Text2 is updated

16 .number

We know that the type of the input box is a string, even if the value of number is a string. If we want to get the value of number directly, we do not want to bother to convert it manually.

<template>
  <div class="number">
    <div class="number-item">
      <p>There is no number.</p>
      <input type="number" v-model="number">
    </div>
    <div class="number-item">
      <p>type:text .number </p>
      <input type="text" v-model.number="number1">
    </div>
    <div class="number-item">
      <p>type:number .number </p>
      <input type="number" v-model.number="number2">
    </div>
  </div>
</template>

export default {
  name: 'lazy',
  data () {
    return {
      number: 0,
      number1: '',
      number2: '',
    }
  },
  watch: {
    number (newVal) {
      console.log(typeof newVal, newVal)
    },
    number1 (newVal) {
      console.log(typeof newVal, newVal)
    },
    number2 (newVal) {
      console.log(typeof newVal, newVal)
    },
  }
}
Copy the code
  1. The first input box is of type number, but the resulting value is string
  2. The second input field is of type text, but with the number modifier added, the resulting value can be number (if the value cannot beparseFloat()Parsing returns the original value.
  3. The third input box is of type number, and the final value is number

System modifier

.ctrl,.alt,.shift, and.meta can help a lot when clicking events or keyboard events require system keys to be pressed simultaneously.

The following code

  1. Listen for keyDown events globally and try to see if.ctrl,.alt,.shift, and.meta are pressed

  2. Add.ctrl,.alt,.shift, and.meta modifiers to the four buttons and click events to verify whether the specified key is pressed at the same time and then clicked to take effect

Note: computer CTRL + click estimate and browser quick configuration conflict, resulting in no trigger

<template>
  <div class="system">
    <p>{{ msg }}</p>
    <div class="buttons">
      <button @click.ctrl="onClickButon('ctrl')">ctrl</button>
      <button @click.alt="onClickButon('alt')">alt</button>
      <button @click.shift="onClickButon('shift')">shift</button>
      <button @click.meta="onClickButon('meta')">meta</button>
    </div>
  </div>  
</template>

export default {
  name: 'system',
  data () {
    return {
      msg: ' '
    }
  },
  mounted () {
    this.onListenSystemKeyDown()
  },
  methods: {
    onListenSystemKeyDown () {
      document.addEventListener('keydown'.(event) = > {
        let msg = 'Pressed'

        if (event.ctrlKey) {
          msg += 'CTRL key'
        } else if (event.altKey) {
          msg += 'Alt'
        } else if (event.shiftKey) {
          msg += 'the shift key
        } else if (event.metaKey) {
          msg += 'meta keys'
        } else {
          msg += 'Other keys'
        }

        this.msg = msg
      }, false)
    },
    onClickButon (key) {
      console.log('Only press at the same time${key}Key, click the event will happen)}}}Copy the code

17 .ctrl

A listener that fires mouse or keyboard events only when the CTRL key is pressed, see above for a detailed example

18 .alt

A listener that fires mouse or keyboard events only when the Alt key is pressed, see above for a detailed example

19 .shift

A listener that fires mouse or keyboard events only when the Shift key is pressed, see above for a detailed example

20 .meta

A listener that fires mouse or keyboard events only when the meta key is pressed, see above for a detailed example

21 .exact

Exact is not a system modifier, but it is possible to press several system modifiers (Alt and shift, for example) at the same time.

Using the example above, take a look at the GIF below where I hold down Alt and Shift at the same time and both events are triggered

  1. A click is triggered only when a system modifier key is pressed
  2. A click is triggered when no system modifier is pressed

To fulfill the above requirements. Exact will come in handy, use the above example to make a little change

<template>
  <div class="extra">
    <p>{{ msg }}</p>
    <div class="buttons">
      <button @click.ctrl.exact="onClickButon('ctrl')">ctrl</button>
      <button @click.alt.exact="onClickButon('alt')">alt</button>
      <button @click.shift.exact="onClickButon('shift')">shift</button>
      <button @click.meta.exact="onClickButon('meta')">meta</button>
      <button @click.exact="OnClickButon (' non-system key ')">The system key</button>
    </div>
  </div>  
</template>

export default {
  name: 'extra',
  data () {
    return {
      msg: ' '
    }
  },
  mounted () {
    this.onListenSystemKeyDown()
  },
  methods: {
    onListenSystemKeyDown () {
      document.addEventListener('keydown'.(event) = > {
        let msg = 'Pressed'

        if (event.ctrlKey) {
          msg += 'CTRL key'
        } else if (event.altKey) {
          msg += 'Alt'
        } else if (event.shiftKey) {
          msg += 'the shift key
        } else if (event.metaKey) {
          msg += 'meta keys'
        } else {
          msg += 'Other keys'
        }

        this.msg = msg
      }, false)
    },
    onClickButon (key) {
      console.log('Only press at the same time${key}Key, click the event will happen)}}}Copy the code

Key modifier

When listening for keyboard events, we often need to check the detailed key before executing the corresponding logic, and VUE also provides us with at least 11+ key modifiers.

In the following code, we specify the keyDown event for enter, TAB, and Delete keys respectively. When you press the specified keyboard in the specified input box, Enter, TAB, and Delete will be printed. Other keys cannot trigger the console in the input box

<template>
  <div class="key-modifiers">
    <div class="key-modifiers-item">
      enter:
      <input type="text" @keydown.enter="onKeydown('enter')">
    </div>
    <div class="key-modifiers-item">
      tab:
      <input type="text" @keydown.tab="onKeydown('tab')">
    </div>  
    <div class="key-modifiers-item">
      delete:
      <input type="text" @keydown.delete="onKeydown('delete')">
    </div>  
    <div class="key-modifiers-item">
      esc:
      <input type="text" @keydown.esc="onKeydown('esc')">
    </div>  
    <div class="key-modifiers-item">
      space:
      <input type="text" @keydown.space="onKeydown('space')">
    </div> 
    <div class="key-modifiers-item">
      up:
      <input type="text" @keydown.up="onKeydown('up')">
    </div>  
    <div class="key-modifiers-item">
      down:
      <input type="text" @keydown.down="onKeydown('down')">
    </div> 
    <div class="key-modifiers-item">
      left:
      <input type="text" @keydown.left="onKeydown('left')">
    </div>  
    <div class="key-modifiers-item">
      right:
      <input type="text" @keydown.right="onKeydown('right')">
    </div>  
    
    <div class="key-modifiers-item">
      page-down:
      <input type="text" @keydown.page-down="onKeydown('page-down')">
    </div>  
    <div class="key-modifiers-item">
      page-up:
      <input type="text" @keydown.page-up="onKeydown('page-up')">
    </div>  
  </div>
</template>

export default {
  name: 'keyModifiers',
  methods: {
    onKeydown (keyName) {
      console.log(keyName)
    }
  }
}

Copy the code

22 .enter

A listener that fires a mouse or keyboard event only when the Enter key is pressed, see above for a detailed example

23 .tab

A listener that fires mouse or keyboard events only when the TAB key is pressed, see above for a detailed example

24 .delete

A listener that fires a mouse or keyboard event only when the DELETE key is pressed, see above for a detailed example

25 .esc

A listener that fires a mouse or keyboard event only when the ESC key is pressed, see above for a detailed example

26 .space

A listener that fires mouse or keyboard events only when the space key is pressed, see above for a detailed example

27 .up

A listener that fires a mouse or keyboard event only when the up key is pressed, see above for a detailed example

28 .down

A listener that fires a mouse or keyboard event only when the Down key is pressed, see above for a detailed example

29 .left

A listener that fires mouse or keyboard events only when the left key is pressed, see above for a detailed example

30 .right

A listener that fires mouse or keyboard events only when the right key is pressed, see above for a detailed example

31 .page-down

A listener that fires mouse or keyboard events only when the (FN + Down) key is pressed, see above for a detailed example

32 .page-up

A listener that fires mouse or keyboard events only when the (fn + up) key is pressed, see above for a detailed example

How do I customize key decorators

Vue itself has a lot of useful key modifiers built in, and most of the time it can meet our daily needs. Is there a way to customize key modifiers?

To define a key modifier of our own, let’s define q as the shortcut for pressing q.


Vue.config.keyCodes = {
  q: 81
}

<div class="custom">
  <input type="text" @keydown.q="f1Keydown">
</div>

export default {
  name: 'custom'.methods: {
    f1Keydown () {
      console.log('Press the Q')}}}Copy the code

Don’t say goodbye

That’s how bighead fish learned about vue modifiers. Everyone is welcome to add and comment exchange. O ha ha ~ O (studying studying)

Examples in the article are put on github source code, you can also click to see the example directly