关于前端:建议收藏面试官贼喜欢问的-32-vue-修饰符你掌握几种啦

4次阅读

共计 13857 个字符,预计需要花费 35 分钟才能阅读完成。

前言

vue 简洁好用体现在很多个中央,比方其内置了 32+ 修饰符,能够很不便咱们阻止冒泡、阻止默认事件、鼠标事件处理、零碎键盘事件等等,让咱们能够疾速搞定业务,几乎不要太不便噢!!!

耽搁您 15 分钟您能够播种:

  1. 32+ 修饰符(包含事件修饰符、鼠标修饰符、表单修饰符、零碎修饰符等等)的含意和应用
  2. 如何利用 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 的配置大略长上面这个样子,每个配置最重要的局部别离是 pathnamecomponent

[
  {
    "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('我是子节点')
    }
  }
}

您能够猜一下,下面的代码会产生什么,以下三点是能够明确的?

  1. 首先能够明确的是点击下面和上面的子节点都不会触发父节点的点击事件
  2. 点击上面的父节点会打印出 我是父节点 ,然而 不会跳转掘金
  3. 点击下面的父节点 打印出 我是父节点 , 也不会跳转掘金

然而点击下面的子节点,父节点会不会跳转至掘金呢?答案是

为什么?

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 绑定。`。

有啥作用?

  1. 通过自定义属性存储变量,防止裸露数据
  2. 避免净化 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.rightmiddle,来看个例子试试

依据 MDN MouseEvent.button,介绍。

在最外层 div.mouse 监听 mousedown 事件,看下用户点击的是鼠标哪个键,三个 button 别离用三个修饰符快捷方式监听 左键 中键 右键 并打印出leftmiddleright

<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)
    },
  }
}
  1. 第一个输入框的类型是 number,然而失去的值是 string
  2. 第二个输入框的类型是 text, 然而增加了 number 修饰符,失去的值能够是 number(如果这个值无奈被 parseFloat() 解析,则会返回原始的值。)
  3. 第三个输入框的类型是 number,最初失去的值也是 number

零碎修饰符

当点击事件或者键盘事件须要零碎键同时按下才触发时 .ctrl.alt.shift.meta 能够帮大忙噢!

如下代码

  1. 全局监听 keydown 事件,尝试看 .ctrl.alt.shift.meta 是否被按下
  2. 别离给四个按钮加上 .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,对应的两个事件都能够触发

  1. 只想某个零碎润饰键按下时才触发点击
  2. 没有任何零碎修饰符被按下的时候才触发点击

要实现下面的需要 .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+ 的按键修饰符。

如下代码,咱们别离给 entertabdelete 等按键指定了 keydown 事件,当在指定的输入框中按下指定的键盘,会打印出 entertabdelete 等,其余按键在输入框中无奈触发该 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 哈哈~

文章中例子都放在了 github 源码上,也能够点击间接看例子

正文完
 0