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:
-
The meaning and use of 32+ modifiers, including event modifiers, mouse modifiers, form modifiers, system modifiers, and so on
-
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?
-
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
-
Clicking on the parent node below will print out that I am the parent node, but will not jump to digging gold
-
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?
- Store variables with custom attributes to avoid exposing data
- 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
- The first input box is of type number, but the resulting value is string
- The second input field is of type text, but with the number modifier added, the resulting value can be number (if the value cannot be
parseFloat()
Parsing returns the original value. - 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
-
Listen for keyDown events globally and try to see if.ctrl,.alt,.shift, and.meta are pressed
-
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
- A click is triggered only when a system modifier key is pressed
- 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