前言
vue簡潔好用體現在很多個地方,比如其內建了32+修飾符,可以很方便我們阻止冒泡、阻止預設事件、滑鼠事件處理、系統鍵盤事件等等,讓我們可以快速搞定業務,簡直不要太方便噢!!!
耽誤您15分鐘您可以收穫:
- 32+修飾符(包括事件修飾符、滑鼠修飾符、表單修飾符、系統修飾符等等)的含義和使用
- 如何利用webpack動態註冊vue路由,再也不手寫路由配置啦!
文章中例子都放在了github原始碼上,也可以點選直接看例子
如何動態註冊路由?
文中的每個修飾符例子都由一個頁面承載,聰明的你肯定不想手動引入幾十個.vue檔案並配置路由.
有什麼辦法可以幫我們自動完成路由註冊呢?
1. 檔案目錄結構
目錄結構(已去除其他檔案目錄)大概如下
├── package.json
└── src
├── App.vue
├── main.js
├── router.js
└── views
├── About.vue
├── Home.vue
└── modifiers
├── capture.vue
├── once.vue
├── order.vue
├── passive.vue
├── prevent.vue
├── self.vue
└── stop.vue
└── ...
2. 期望的路由配置
最終給到vue-router
的配置大概長下面這個樣子,每個配置最重要的部分分別是path
、name
和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"
}
},
... // 其他路由配置
]
3. require.context實現動態註冊路由
藉助webpack require.context 的能力,可以非常方便地實現上面目錄到路由配置的對映工作,原始碼如下
const registerRoutes = () => {
const contextInfo = require.context('./views', true, /.vue$/)
const routes = contextInfo.keys().map((filePath) => {
// filePath 形如 ./Home.vue、./modifiers/capture.vue
// path我們希望是/home、/modifiers/capture
// 所以需要把開頭的./和.vue都替換為空
const path = filePath.toLowerCase().replace(/^\.|\.vue/g, '')
// name的話將/home、/modifiers/capture轉成小駝峰即可
// 把開頭的/先替換掉,再把第一個/後的單詞變成大寫就可以了
const name = path.replace(/^\//, '').replace(/\/(\w)/, ($0, $1) => $1.toUpperCase())
// 通過require去讀取.vue檔案內容
const component = require(`./views${filePath.replace(/^\./, '')}`).default
return {
path,
name,
component
}
})
return routes
}
效果
經過上面的簡單處理,動態註冊路由就完成啦!您也可以點選vue-demos檢視效果
事件修飾符
1. 阻止冒泡的兩種方式
<template>
<div class="parent" @click="onClickParent">
我是爸爸
<div class="child" @click="onClickChild">
我是兒子
</div>
</div>
</template>
export default {
name: 'stop',
methods: {
onClickParent () {
console.log('我是爸爸')
},
onClickChild () {
console.log('我是兒子')
}
}
}
點擊子節點的時候因為事件冒泡的緣故不僅會列印出我是兒子
還會列印我是爸爸
。有什麼辦法可以阻止子節點的事件冒泡呢?
1 .stop
只要加.stop修飾符即可,阻止事件冒泡的及簡方式,很方便是不是。
當新增上.stop
修飾符時,只會出現我是兒子
<template>
<div class="parent" @click="onClickParent">
我是爸爸
<div class="child" @click.stop="onClickChild">
我是兒子
</div>
</div>
</template>
2. event.stopPropagation
當然了,我們也可以通過呼叫event.stopPropagation
來阻止冒泡。不過更加推薦修飾符的做法,這樣你的函式會更加專注在邏輯處理上,而不用關心DOM事件細節
export default {
name: 'stop',
methods: {
onClickChild (event) {
console.log('我是兒子')
event.stopPropagation()
}
}
}
2. 阻止預設事件的兩種方式
vue中阻止冒泡有兩種方式,那阻止預設事件呢?
1 .prevent
<template>
<div class="prevent">
<a href="https://juejin.cn/" @click="onNoPrevent">點選跳轉掘金</a>
<br />
<br />
<a href="https://juejin.cn/" @click.prevent="onPrevent">阻止預設事件,無法跳轉掘金</a>
</div>
</template>
export default {
name: 'prevent',
methods: {
onNoPrevent () {
console.log('未阻止預設事件')
},
onPrevent () {
console.log('阻止預設事件')
}
}
}
只要新增.prevent
輕鬆實現阻止預設事件
2.event.preventDefault()
和阻止冒泡一樣,我們也可以通過呼叫事件物件的preventDefault
方法來阻止預設事件
export default {
name: 'prevent',
methods: {
onPrevent (event) {
console.log('阻止預設事件')
event.preventDefault()
}
}
}
3 .capture
預設情況下,事件流是以冒泡
(由裡向外)的形式傳遞的,如果想以捕獲(由外向裡)
的形式應該怎麼辦呢?
<template>
<div class="capture parent" @click.capture="onClickParent">
父節點
<div class="child" @click.capture="onClickChild">自節點</div>
</div>
</template>
export default {
name: 'capture',
methods: {
onClickParent () {
console.log('我是父節點')
},
onClickChild () {
console.log('我是子節點')
}
}
}
不加catpture
修飾符,點選子節點會陸續列印我是父節點以及我是子節點,加了之後,則是反過來了
4 .self
只有當event.target
是當前元素自身時才會觸發事件回撥函式
<template>
<div class="self" @click.self="onClickSelf">
<div class="inner" @click="onClickInner"></div>
</div>
</template>
export default {
name: 'self',
methods: {
onClickSelf () {
console.log('我是self節點')
},
onClickInner () {
console.log('我是inner節點')
}
}
}
不加self
修飾符的話,點選inner
節點也會觸發self
的事件,加了之後只有觸發事件的元素本身是self
,才會列印出我是self節點
暫停一下:修飾符的順序如何理解?
已經回顧了4個修飾符,單獨使用的時候很容易理解,但是注意官網有這麼一句話
怎麼理解呢?我們來看兩個栗子
<template>
<div class="order">
<div class="order-0">
<a href="https://juejin.cn/" class="order-parent" @click.self.prevent="onClickParent">
我是父節點,會跳轉掘金
<br />
<span class="order-child" @click="onClickChild">
我是子節點
</span>
</a>
<hr />
</div>
<div class="order-2">
<a href="https://juejin.cn/" class="order-parent" @click.prevent.self="onClickParent">
我是父節點,無法跳轉掘金
<br />
<span class="order-child" @click="onClickChild">
我是子節點
</span>
</a>
</div>
</div>
</template>
export default {
name: 'order',
methods: {
onClickParent () {
console.log('我是父節點')
},
onClickChild () {
console.log('我是子節點')
}
}
}
您可以猜一下,上面的程式碼會發生什麼,以下三點是可以明確的?
- 首先可以明確的是點選上面和下面的子節點都不會觸發父節點的點選事件
- 點選下面的父節點會列印出我是父節點,但是不會跳轉掘金
- 點選上面的父節點會列印出我是父節點,也不會跳轉掘金
但是點選上面的子節點,父節點會不會跳轉至掘金呢?答案是會
為什麼?
a@click.self.prevent="onClickParent"
的意思是當點選的元素是a元素本身時,會阻止預設事件(可以解釋3,不會發生跳轉),並且執行onClickParent
回撥。
而點選span元素時,由於冒泡的緣故,點選事件會傳遞給a,但是此時a會判斷出該事件不是由自身觸發的也就不會阻止預設事件
(此時也就發生跳轉了),當然也不會觸發onClickParent
回撥
同理來我們分析一下a@click.prevent.self="onClickParent"
不管是子節點還是自身點選,都是先阻止預設事件,只有當觸發點選事件是a元素本身時才會執行onClickParent
回撥函式。
回過頭看,你理解事件的順序含義了嗎?
5. once
顧名思義,事件只會觸發一次
<template>
<div class="once" @click.once="onClickOnce">
只觸發一次
</div>
</template>
export default {
name: 'once',
methods: {
onClickOnce () {
console.log('once,我只會觸發一次點選事件回撥')
}
}
}
觸發一次點選之後,任我再怎麼點,回撥怎也不會觸發了。
6 .native
我們知道在自定義元件上,只能監聽自定義事件,一些原生事件(比如click)是沒有辦法直接觸發的,但是使用.native
修飾符可以幫我們辦到這點
native.vue
<template>
<div class="native-custom">
<input type="text" @keydown="onKeydown">
</div>
</template>
export default {
name: 'nativeCustom',
methods: {
onKeydown () {
this.$emit('onKeydown')
}
}
}
custom.vue
<template>
<div class="native">
<!-- 加上.native之後原生事件才得以監聽成功 -->
<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')
}
}
}
7 .passive
vue對應addEventListener
中的passive
選項提供了.passive
修飾符
<!-- 滾動事件的預設行為 (即滾動行為) 將會立即觸發 -->
<!-- 而不會等待 `onScroll` 完成 -->
<!-- 這其中包含 `event.preventDefault()` 的情況 -->
<div v-on:scroll.passive="onScroll">...</div>
這個修飾符對於滾動效能的提升,一直沒找到合適的例子,跪求廣大掘友有例子啊
這個修飾符對於滾動效能的提升,一直沒找到合適的例子,跪求廣大掘友有例子啊
這個修飾符對於滾動效能的提升,一直沒找到合適的例子,跪求廣大掘友有例子啊
v-bind修飾符
8 .sync
當我們想要在父元件
和子元件
之間對某個屬性值進行雙向繫結時,有什麼便捷的方式?是的只要.sync
修飾符即可辦到
父元件
<template>
<div class="sync-parent">
我是父元件: {{ text }}
<Child :text.sync="text" />
</div>
</template>
import Child from './child.vue'
export default {
name: 'SyncParent',
data () {
return {
text: 'parent'
}
},
components: {
Child,
}
}
子元件
<template>
<div class="child">
我是子元件:
<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 () {
// 注意這裡,必須是update:xxx的形式xxx即屬性prop
this.$emit('update:text', this.value)
}
}
}
9 .camel
.camel
修飾符允許在使用 DOM 模板時將v-bind
property 名稱駝峰化,例如 SVG 的viewBox
property:
<svg :view-box.camel="viewBox"></svg>
10 .prop
關於.prop修飾符官網只有這句話 .prop
作為一個 DOM property 繫結而不是作為 attribute 繫結。`。
有啥作用?
- 通過自定義屬性儲存變數,避免暴露資料
- 防止汙染 HTML 結構
比如有以下程式碼
<template>
<div class="prop">
<div class="prop-item" :my-name="prop"></div>
// 最終變成了 <div my-name="hello prop" class="prop-item"></div>
<div class="prop-item" :my-name.prop="prop2"></div>
// 最終變成了<div class="prop-item"></div>
<button @click="onGetResult">獲取結果</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
}
}
}
從示例上可以看出未使用.prop
修飾符的my-name
屬性會繫結到dom節點的attribute,從而出現暴露的情況。
滑鼠修飾符
當我們們想監聽使用者點選了左鍵
、右鍵
或者中鍵
時也有修飾符可以快捷使用,分別是.left
、.right
、middle
,來看個例子試試
根據MDN MouseEvent.button,介紹。
在最外層div.mouse
監聽mousedown
事件,看下使用者點選的是滑鼠哪個鍵,三個button
分別用三個修飾符快捷方式監聽左鍵
、中鍵
、右鍵
並列印出left
、middle
、right
<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: '滑鼠左鍵',
1: '滑鼠中鍵',
2: '滑鼠右鍵'
}
console.log('點選了', mosueMsgMap[event.button])
}
}
}
沒有帶滑鼠回來,中鍵點選暫時不能演示,後續會補上
11 .left
同上例子,監聽滑鼠左鍵點選
12 .right
同上例子,監聽滑鼠右鍵點選
13 .middle
同上例子,監聽滑鼠中鍵點選
表單相關修飾符
14 .trim
對於輸入的內容,希望可以過濾首尾空格
應該怎麼做呢?
<template>
<div class="trim">
<div class="trim-item">
<input type="text" v-model="name">
<p>使用者名稱:<span>{{ name }}</span></p>
</div>
<div class="trim-item">
<input type="text" v-model.trim="name2">
<p>使用者名稱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}----'`)
},
}
}
.trim修飾符可以很方便做到
15 .lazy
v-model
大家都很熟悉,預設情況下,每次input事件
觸發的時候都會將輸入框的值與其繫結的資料進行實時同步。但是如果想要實現游標離開的時候再更新資料如何實現呢?
思路1: 繫結change事件,在事件回撥中手動獲取target的值
思路2: 直接使用.lazy
修飾符即可達到效果
<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: ''
}
}
}
可以看到新增了.lazy修飾符之後,第二個輸入框輸入的值不會實時反應在下面,而是游標離開實,text2
的資料才更新了
16 .number
我們知道input
輸入框的type
哪怕是number
得到的值的型別也是string
,如果我們想直接拿到number
型別的資料,有不想麻煩的手動轉換應該怎麼辦呢?
<template>
<div class="number">
<div class="number-item">
<p>無.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)
},
}
}
- 第一個輸入框的型別是number,但是得到的值是string
- 第二個輸入框的型別是text,但是新增了number修飾符,得到的值可以是number(如果這個值無法被
parseFloat()
解析,則會返回原始的值。) - 第三個輸入框的型別是number,最後得到的值也是number
系統修飾符
當點選事件或者鍵盤事件需要系統鍵同時按下才觸發時.ctrl
、.alt
、.shift
、.meta
可以幫大忙噢!
如下程式碼
- 全域性監聽keydown事件,嘗試看
.ctrl
、.alt
、.shift
、.meta
是否被按下 - 分別給四個按鈕加上
.ctrl
、.alt
、.shift
、.meta
修飾符並配合點選事件,驗證是否同時按下指定按鍵,再點選才會生效
註明:電腦ctrl鍵 + 點選估計和瀏覽器快捷配置衝突了,導致沒觸發
<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 = '按下了'
if (event.ctrlKey) {
msg += 'ctrl鍵'
} else if (event.altKey) {
msg += 'alt鍵'
} else if (event.shiftKey) {
msg += 'shift鍵'
} else if (event.metaKey) {
msg += 'meta鍵'
} else {
msg += '其他鍵'
}
this.msg = msg
}, false)
},
onClickButon (key) {
console.log(`只有同時按下${key}鍵,點選事件才會發生`)
}
}
}
17 .ctrl
僅在按下ctrl按鍵時才觸發滑鼠或鍵盤事件的監聽器,詳細例子請看上面
18 .alt
僅在按下alt按鍵時才觸發滑鼠或鍵盤事件的監聽器,詳細例子請看上面
19 .shift
僅在按下shift按鍵時才觸發滑鼠或鍵盤事件的監聽器,詳細例子請看上面
20 .meta
僅在按下meta按鍵時才觸發滑鼠或鍵盤事件的監聽器,詳細例子請看上面
21 .exact
嚴格來說這.exact
不屬於系統修飾符,只是上面例子的寫法有一個現象,同時按下幾個系統修飾鍵(例如alt和shift)既可以觸發.alt
也可以觸發.shift
。
還是用上面的例子,看一下下面的gif, 此時我同時按下了alt和shift,對應的兩個事件都可以觸發
- 只想某個系統修飾鍵按下時才觸發點選
- 沒有任何系統修飾符被按下的時候才觸發點選
要實現上面的需求.exact
就派上用場了,用上面的例子稍作改造
<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('非系統鍵')">非系統鍵</button>
</div>
</div>
</template>
export default {
name: 'extra',
data () {
return {
msg: ''
}
},
mounted () {
this.onListenSystemKeyDown()
},
methods: {
onListenSystemKeyDown () {
document.addEventListener('keydown', (event) => {
let msg = '按下了'
if (event.ctrlKey) {
msg += 'ctrl鍵'
} else if (event.altKey) {
msg += 'alt鍵'
} else if (event.shiftKey) {
msg += 'shift鍵'
} else if (event.metaKey) {
msg += 'meta鍵'
} else {
msg += '其他鍵'
}
this.msg = msg
}, false)
},
onClickButon (key) {
console.log(`只有同時按下${key}鍵,點選事件才會發生`)
}
}
}
按鍵修飾符
在監聽鍵盤事件時,我們經常需要檢查詳細的按鍵再執行對應的邏輯,vue也為我們內建了至少11+的按鍵修飾符。
如下程式碼,我們分別給enter
、tab
、delete
等按鍵指定了keydown
事件,當在指定的輸入框中按下指定的鍵盤,會列印出enter
、tab
、delete
等,其他按鍵在輸入框中無法觸發該console
<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)
}
}
}
22 .enter
在按下enter按鍵時才觸發滑鼠或鍵盤事件的監聽器,詳細例子請看上面
23 .tab
在按下tab按鍵時才觸發滑鼠或鍵盤事件的監聽器,詳細例子請看上面
24 .delete
在按下delete按鍵時才觸發滑鼠或鍵盤事件的監聽器,詳細例子請看上面
25 .esc
在按下esc按鍵時才觸發滑鼠或鍵盤事件的監聽器,詳細例子請看上面
26 .space
在按下space按鍵時才觸發滑鼠或鍵盤事件的監聽器,詳細例子請看上面
27 .up
在按下up按鍵時才觸發滑鼠或鍵盤事件的監聽器,詳細例子請看上面
28 .down
在按下down按鍵時才觸發滑鼠或鍵盤事件的監聽器,詳細例子請看上面
29 .left
在按下left按鍵時才觸發滑鼠或鍵盤事件的監聽器,詳細例子請看上面
30 .right
在按下right按鍵時才觸發滑鼠或鍵盤事件的監聽器,詳細例子請看上面
31 .page-down
在按下(fn + down)按鍵時才觸發滑鼠或鍵盤事件的監聽器,詳細例子請看上面
32 .page-up
在按下(fn + up)按鍵時才觸發滑鼠或鍵盤事件的監聽器,詳細例子請看上面
如何自定義按鍵修飾符
vue本身給我們內建了很多實用的按鍵修飾符,大部分情況下可以滿足我們的日常需求了,那麼有沒有辦法可以自定義按鍵修飾符呢?
通過以下配置即可定義一個屬於我們自己的按鍵修飾符, 比如我們定義q為按下q的快捷鍵。
Vue.config.keyCodes = {
q: 81
}
<div class="custom">
<input type="text" @keydown.q="f1Keydown">
</div>
export default {
name: 'custom',
methods: {
f1Keydown () {
console.log('按下了q')
}
}
}
不說再見
以上就是胖頭魚對vue修飾符學習和了解的內容啦!歡迎大家補充和評論交流。O(∩_∩)O哈哈~