Vue2、3组件通信、双向绑定、插槽slot、内置指令、事件修饰符
目录
子props=defineProps({属性名:type...})
attrs父作用域(除 class、 style 、 props )属性集合
父child @事件名"="parentClick",parentClick(msg)
子emit = defineEmits(["事件名",...]),emit("key",msg)
子emit = defineEmits(["事件名",...]),emit("事件名",值)
父child ref="childComp",childComp.value.属性/方法
子defineExpose({属性: "msg",方法(){ }})
跨框架:不依赖 Vue 实例,React / Vue/jQuery
B.对象:props:{属性名:{type:,default:}}
attrs父作用域(除 class、 style 、 props )属性集合
父child @事件名"="parentClick",parentClick(msg)
this.$listeners:父组件.native 除外的监听事件集合
父child ref="childComp",childComp.所有的属性/方法
祖传孙provide / inject依赖注入:推荐传递常量/方法
子inject[“name”,“msg”],this.msg
v-on (简写@): 监听DOM事件、触发Vue实例中的方法
v-model:modelValue=v-modelv-model=v-bind(:)+v-on(@)
.capture = addEventListener ('click', handleClick, true)捕获阶段监听
.once = addEventListener ('click', handleClick,{once: true}):事件处理只触发一次,之后不再生效
.self = event.target ==event.currentTarget 只在事件目标是当前元素时处理事件
.passive =element.addEventListener('scroll', handleScroll, { passive: true });优化滚动事件的性能
Vuex
Vue3
Vue3 从实例中完全删除了 $on、$off 和 $once 方法
父子传值:props(attrs)/emit
父传子
props
父child :属性名="变量"
子props=defineProps({属性名:type...})
// Parent.vue 传送
<child :msg2="msg2"></child>
<script setup>
import child from "./child.vue"
import { ref, reactive } from "vue"
const msg2 = ref("这是传给子组件的信息2")
// 或者复杂类型
const msg2 = reactive(["这是传级子组件的信息2"])
</script>
// Child.vue 接收
<script setup>
// 不需要引入 直接使用
// import { defineProps } from "vue"
const props = defineProps({
// 写法一
msg2: String
// 写法二
msg2:{
type:String,
default:""
}
})
console.log(props) // { msg2:"这是传级子组件的信息2" }
</script>
attrs父作用域(除 class、 style 、 props )属性集合
父child :属性名="变量",属性名="常量"
子 attrs = useAttrs()
// Parent.vue 传送
<child :msg1="msg1" :msg2="msg2" title="3333"></child>
<script setup>
import child from "./child.vue"
import { ref, reactive } from "vue"
const msg1 = ref("1111")
const msg2 = ref("2222")
</script>
// Child.vue 接收
<script setup>
// 3.2版本不需要引入,直接用
import { useAttrs } from "vue"
// 如果没有用 props 接收 msg1 的话就是 { msg1: "1111", msg2:"2222", title: "3333" }
const props = defineProps({
msg1: String
})
const attrs = useAttrs()
console.log(attrs) // { msg2:"2222", title: "3333" }
</script>
子传父emits+@=v-on:
父child @事件名"="parentClick",parentClick(msg)
子emit = defineEmits(["事件名",...]),emit("key",msg)
//父组件
<template>
<my-son @childClick="childClick" />
</template>
<script lang="ts" setup>
import MySon from "./MySon.vue";
let childClick = (e: any):void => {
console.log('from son:',e);
};
</script>
//子组件
<template>
<span @click="sonToFather">信息:{{ props.foo }}</span>
</template>
<script lang="ts" setup>
const emit = defineEmits(["childClick"]); // 声明触发事件 childClick
const sonToFather = () =>{
emit('childClick' , props.foo)
}
</script>
双向绑定v-model
可以指定参数,完全取代了 .sync 的功能
一个元素可多个v-model
<!-- Vue 3 -->
<!-- 取代 v-model -->
<Child v-model:title="pageTitle" />
<!-- 取代 .sync -->
<Child v-model:can-search="isSearching" v-model:visible="isVisible"/>
父child v-model:属性名="变量"
子emit = defineEmits(["事件名",...]),emit("事件名",值)
//child.vue
<template>
<span @click="changeInfo">我叫{{ modelValue }},今年{{ age }}岁</span>
</template>
<script setup>
// import { defineEmits, defineProps } from 'vue'
// defineEmits和defineProps在<script setup>中自动可用,无需导入
// 需在.eslintrc.js文件中【globals】下配置【defineEmits: true】、【defineProps: true】
defineProps({
modelValue: String,
age: Number
})
const emit = defineEmits(['update:modelValue', 'update:age'])
const changeInfo = () => {
// 触发父组件值更新
emit('update:modelValue', 'Tom')
emit('update:age', 30)
}
</script>
//parent.vue
<template>
// v-model:modelValue简写为v-model
// 可绑定多个v-model
<child
v-model="state.name"
v-model:age="state.age"
/>
</template>
<script setup>
import { reactive } from 'vue'
// 引入子组件
import child from './child.vue'
const state = reactive({
name: 'Jerry',
age: 20
})
</script>
父调子事件/属性:expose / ref
父child ref="childComp",childComp.value.属性/方法
子defineExpose({属性: "msg",方法(){ }})
// Child.vue
<script setup>
// 方法一 不适用于Vue3.2版本,该版本 useContext()已废弃
import { useContext } from "vue"
const ctx = useContext()
// 对外暴露属性方法等都可以
ctx.expose({
childName: "这是子组件的属性",
someMethod(){
console.log("这是子组件的方法")
}
})
// 方法二 适用于Vue3.2版本, 不需要引入
// import { defineExpose } from "vue"
defineExpose({
childName: "这是子组件的属性",
someMethod(){
console.log("这是子组件的方法")
}
})
</script>
// Parent.vue 注意 ref="comp"
<template>
<child ref="comp"></child>
<button @click="handlerClick">按钮</button>
</template>
<script setup>
import child from "./child.vue"
import { ref } from "vue"
const comp = ref(null)
const handlerClick = () => {
console.log(comp.value.childName) // 获取子组件对外暴露的属性
comp.value.someMethod() // 调用子组件对外暴露的方法
}
</script>
祖传孙provide / inject依赖注入【不推荐】
耦合性增加->依赖关系复杂化->重用性和可维护性低
耦合性:关联度
父provide("key",msg)
子inject("key")
// Parent.vue
<script setup>
import { provide } from "vue"
provide("name", "沐华")
</script>
// Child.vue
<script setup>
import { inject } from "vue"
const name = inject("name")
console.log(name) // 沐华
</script>
跨组件事件触发/监听器mitt:on/off,emit
代替vue2的eventbus
轻量级:仅有200字节
支持全部事件的监听和批量移除
跨框架:不依赖 Vue 实例,React / Vue/jQuery
原理:map 保存函数
export default function mitt(all) {
all = all || new Map();
//命名为type的事件
return {
all,
//监听type事件,绑定handler
on(type, handler) {
const handlers = all.get(type);
const added = handlers && handlers.push(handler);
if (!added) {
all.set(type, [handler]);
}
},
//移除type事件,解绑handler
off(type, handler) {
const handlers = all.get(type);
if (handlers) {
handlers.splice(handlers.indexOf(handler) >>> 0, 1);
}
},
//触发type事件,传递evt事件参数给handler(遍历执行与其绑定的所有handler)
emit(type, evt) {
((all.get(type) || [])).slice().map((handler) => { handler(evt); });
((all.get('*') || [])).slice().map((handler) => { handler(type, evt); });
},
clear(){
// 清空所有事件处理程序
all.clear();
},
};
}
直接在组件内导入使用:分散式更方便管理和排查问题
npm i mitt -save
import mitt from 'mitt'
const emitter = mitt()
// listen to an event
emitter.on('foo', e => console.log('foo', e) )
// listen to all events
emitter.on('*', (type, e) => console.log(type, e) )
// fire an event
emitter.emit('foo', { a: 'b' })
// clearing all events
emitter.all.clear()
// working with handler references:
function onFoo() {}
emitter.on('foo', onFoo) // listen
emitter.off('foo', onFoo) // unlisten
Vue2
父子传值:props(attrs)/emit
父传子
props
父child :属性名="父组件实例变量"
子
A.数组:props:['属性名']
B.对象:props:{属性名:{type:,default:}}
// Child.vue 接收
export default {
// 写法一 用数组接收
props:['msg'],
// 写法二 用对象接收,可以限定接收的数据类型、设置默认值、验证等
props:{
msg:{
type:String,
default:'这是默认数据'
}
},
mounted(){
console.log(this.msg)
},
}
attrs父作用域(除 class、 style 、 props )属性集合
父child :属性名="变量",属性名="常量"
子 this.$attrs
子传父emits+@=v-on:
父child @事件名"="parentClick",parentClick(msg)
子this.$emit("事件名",msg)
// Child.vue 派发
export default {
data(){
return { msg: "这是发给父组件的信息" }
},
methods: {
handleClick(){
this.$emit("sendMsg",this.msg)
}
},
}
// Parent.vue 响应
<template>
<child v-on:sendMsg="getChildMsg"></child>
// 或 简写
<child @sendMsg="getChildMsg"></child>
</template>
export default {
methods:{
getChildMsg(msg){
console.log(msg) // 这是父组件接收到的消息
}
}
}
回调函数:违反单向数据流
-
action:特定按钮的专属逻辑 -
@button-click:所有按钮的通用处理 -
@close:弹窗关闭的通用处理(不限于按钮)
//父组件
<GlobalModal
:buttons="[{ text: '关闭弹窗', type: 'primary', action: this.handleClose }]"
@button-click="handleClose"
@close="handleClose"
//子组件
<div v-if="showButtons && buttons && buttons.length > 0" class="modal-buttons" :class="buttonLayoutClass">
<van-button
v-for="(button, index) in buttons"
:key="index"
:type="button.type || 'default'"
:size="button.size || 'large'"
:loading="button.loading"
:disabled="button.disabled"
:class="button.class"
@click="handleButtonClick(button, index)"
>
{{ button.text }}
</van-button>
</div>
//两个方法二选一
handleButtonClick(button: Button, index: number) {
if (button.action) {
button.action()
}
this.$emit('button-click', { button, index })
// 点击按钮后关闭弹窗
}
语法糖:『数据向下,事件向上』 的单向数据流。
| 特性 | v-model |
.sync 修饰符 |
|---|---|---|
| 设计初衷 | 专门为表单元素/输入组件设计,实现双向绑定。 | 为任何需要“双向”意义的普通prop设计,实现状态同步。 |
| 语法糖 | :value + @input 事件。 |
:prop + @update:prop 事件。 |
| 父组件设置 | v-model="val"只能一个 |
|
| 子组件触发事件 | this.$emit('input', newValue) |
this.$emit('update:propName', newValue) |
.sync:状态同步(父子)
如控制子组件的显示隐藏(visible)、加载状态(loading)
<!-- 父组件 -->
<Child :can-search.sync="isChildSearching" :visible.sync="isChildVisible"/>
<!-- 等价于 -->
<Child
:can-search="isChildSearching"
@update:can-search="isChildSearching = $event"
:visible="isChildVisible"
@update:visible="isChildVisible = $event"
/>
// 子组件
this.$emit('update:can-search', false)
this.$emit('update:visible', true)
v-model:双向绑定(输入元素专用)
除了传递基本的值之外,还可以传递其他类型,比如对象或数组。
主要用于表单元素(如输入框、复选框、单选框等input, textarea,select,radio,checkbox等用户输互动入事件)
<input v-model="searchText">
语法糖 等价于:
<input
:value="searchText"
@input="searchText = $event.target.value"
>
当用户在输入框中输入文本时,userMessage 的值会实时更新,
并且当 userMessage 的值改变时,输入框中的值也会自动更新。
v-model 在内部相当于使用 :value 和 @input 来实现数据的绑定和监听。
可自定义prop+event名
// Parent.vue
<template>
<child v-model="value"></child>
</template>
<script>
export default {
data(){
return {
value:1
}
}
}
// Child.vue
<template>
<input :value="value" @input="handlerChange">
</template>
export default {
props:["value"],
methods:{
handlerChange(e){
this.$emit("input", e.target.value)
}
}
}
</script>
父子传属性/方法
父传子
this.$parent
this.$root : App.vue
this.$listeners:父组件.native 除外的监听事件集合
调用:this.$listeners.事件名
将父组件传递的事件监听器绑定到子组件上,以实现一种“透明传递”的效果。这种方式使得子组件不需要显式声明和监听来自父组件的事件
在父组件 自定义的所有事件,都被保存在子组件的vm.$listeners属性里,和$attrs一样可以层层传递
son组件可以响应father组件的2个自定义事件speak()、write()
grandson组件可以响应father组件的2个自定义事件speak()、write(),并且还可以响应son组件的1个自定义事件sonCry()
子传父
this.$children[0]
ref
父child ref="childComp",childComp.所有的属性/方法
// Child.vue
export default {
data(){
return {
name:"沐华"
}
},
methods:{
someMethod(msg){
console.log(msg)
}
}
}
// Parent.vue
<template>
<child ref="child"></child>
</template>
<script>
export default {
mounted(){
const child = this.$refs.child
console.log(child.name) // 沐华
child.someMethod("调用了子组件的方法")
}
}
</script>
祖传孙provide / inject依赖注入:推荐传递常量/方法
要注意的是 provide 和 inject 传递的数据不是响应式的,除非传入的就是一个可监听的对象
父provide(){return obj }
obj={name:,msg:}
子inject[“name”,“msg”],this.msg
跨组件事件通信EventBus中央事件总线
定义
| 方法 | 优点 | 缺点 |
|---|---|---|
| 方法一(独立文件) |
- 代码模块化,解耦清晰 - 易于维护 |
- 不适合超大型项目,可能需要更多的状态管理方案 |
| 方法二(原型挂载) |
- 访问简洁方便 - 适用于小型项目 |
- 可能污染原型,增加耦合性 - 调试困难 |
| 方法三(根实例注入) |
- 避免污染 Vue 原型 - 适度解耦 |
- 访问稍显麻烦 - 根实例复杂化 |
// 方法一:独立的事件总线文件
// 抽离成一个单独的 js 文件 Bus.js导出 ,然后在需要的地方导入
// 保持了代码的模块化和清晰性。事件总线和组件逻辑分离,使得维护和理解代码变得更容易。
// Bus.js
import Vue from "vue"
export default new Vue()
import bus from './Bus.js'
bus.$emit('eventName', data) // 触发事件
// 方法二 全局挂载到Vue原型
// main.js
import Vue from "vue"
Vue.prototype.$bus = new Vue()
// 组件通过this.$bus访问
// 方法三 注入到 Vue 根对象上,作为Vue实例的数据属性
// main.js
import Vue from "vue"
new Vue({
el:"#app",
data:{
Bus: new Vue()
}
})
// 组件通过this.$root.bus访问
使用:emit、on、off
// 在需要向外部发送自定义事件的组件内
<template>
<button @click="handlerClick">按钮</button>
</template>
import Bus from "./Bus.js"
export default{
methods:{
handlerClick(){
// 自定义事件名 sendMsg
Bus.$emit("sendMsg", "这是要向外部发送的数据")
}
}
}
// 在需要接收外部事件的组件内
import Bus from "./Bus.js"
export default{
mounted(){
// 监听事件的触发
Bus.$on("sendMsg", data => {
console.log("这是接收到的数据:", data)
})
},
beforeDestroy(){
// 取消监听
Bus.$off("sendMsg")
}
}
扩展插件
- 对于以
ai:开头的事件,会特别存储相关 Vue 实例的映射关系。 - 在触发这些事件时,会将事件传播到所有监听该事件的 Vue 实例。
- 在 Vue 实例销毁时,移除与该实例相关的事件监听和映射,避免内存泄漏。
1.配置
function plugin(Vue) {
// NOOP:一个空函数,默认的回调函数,防止没有传递回调时出现错误
const NOOP = () => {};
// 判断插件是否已经安装,如果已经安装则不执行插件逻辑,避免重复安装
// @ts-ignore:忽略 TypeScript 的检查,允许访问 `plugin.installed`
if (plugin.installed) {
return;
}
// 标记插件已经安装
// @ts-ignore:忽略 TypeScript 的检查,允许访问 `plugin.installed`
plugin.installed = true;
// 定义存储事件和 Vue 实例映射的对象
const eventMap = {}; // 存储事件名称和监听该事件的 Vue 实例的映射
const vmEventMap = {}; // 存储 Vue 实例的 UID 和其监听的事件映射
const aiRE = /^ai:/; // 正则匹配以 'ai:' 开头的事件名称
// 扩展 Vue 的事件功能
function mixinEvents(Vue) {
const on = Vue.prototype.$on; // 获取原始的 $on 方法
Vue.prototype.$on = function proxyOn(eventName, fn = NOOP) {
const vm = this;
// 如果传入的是一个事件数组,则分别为每个事件添加监听器
if (Array.isArray(eventName)) {
eventName.forEach(item => {
vm.$on(item, fn);
});
} else {
// 如果事件名称以 'ai:' 开头,则进行特殊处理
if (aiRE.test(eventName)) {
// 将事件名称和当前 Vue 实例 UID 记录在 vmEventMap 中
(vmEventMap[vm._uid] || (vmEventMap[vm._uid] = [])).push(eventName);
// 将当前 Vue 实例记录在 eventMap 中,供后续触发时使用
(eventMap[eventName] || (eventMap[eventName] = [])).push(vm);
}
// 调用原始的 $on 方法来注册事件
on.call(vm, eventName, fn);
}
return vm;
};
const emit = Vue.prototype.$emit; // 获取原始的 $emit 方法
Vue.prototype.$emit = function proxyEmit(eventName, ...args) {
const vm = this;
// 如果传入的是事件名称数组,则分别触发每个事件
if (eventName instanceof Array) {
for (const itemName of eventName) {
if (aiRE.test(itemName)) {
// 如果事件名称以 'ai:' 开头,则将事件传播到所有相关的 Vue 实例
const vmList = eventMap[itemName] || [];
vmList.forEach((item) => emit.apply(item, [itemName, ...args]));
} else {
// 否则直接触发事件
emit.apply(vm, [itemName, ...args]);
}
}
} else {
// 处理单个事件
if (aiRE.test(eventName)) {
const vmList = eventMap[eventName] || [];
// 如果事件名称以 'ai:' 开头,则将事件传播到所有相关的 Vue 实例
vmList.forEach((item) => emit.apply(item, [eventName, ...args]));
} else {
// 否则直接触发事件
emit.apply(vm, [eventName, ...args]);
}
}
return vm;
};
}
//Vue.mixin 会为每个 Vue 实例注入一段代码,确保该代码在 Vue 实例的生命周期内被执行。
// 在 Vue 中应用插件的混入
function applyMixin(Vue) {
Vue.mixin({
// 在 Vue 实例销毁之前,清理与该实例相关的事件监听器
beforeDestroy() {
const vm = this;
// 获取当前实例已经监听的事件
const events = vmEventMap[vm._uid] || [];
events.forEach((event) => {
// 在 eventMap 中找到当前实例,移除该事件的监听
const targetIdx = eventMap[event].findIndex((item) => item._uid === vm._uid);
eventMap[event].splice(targetIdx, 1);
});
// 删除当前实例的事件记录
delete vmEventMap[vm._uid];
// 如果 eventMap 中某个事件没有剩余的 Vue 实例监听,则删除该事件记录
Object.entries(eventMap).forEach(([eventName, vmList]) => vmList.length || delete eventMap[eventName]);
},
});
}
// 扩展 Vue 的事件功能
mixinEvents(Vue);
// 在 Vue 实例中应用销毁时的清理逻辑
applyMixin(Vue);
}
export default plugin;
2.注册
src/main.js
import EventBus from './utils/aiEventProxy';
Vue.use(EventBus);
3.使用
this.$on('ai:sent-msg',this.sendMsg)
this.$emit('ai:sent-msg',item.sendText);
slot:父传子组件,子传父数据
适用于跨项目 + 多类型的子组件备用选项
-
✅ 自己项目 → 优先用 Props,把常见业务封装到组件内部,哪怕只用一次
-
✅ 跨项目组件库 → 提供插槽给使用者深度定制
-
✅ 特殊场景 → 用业务组件 + 插槽,避免污染业务页面
template 是 Vue 的编译指令。它在编译阶段就被处理,不会出现在最终 DOM 中。
slot没有 template 时,Vue 会把内容直接当作普通子节点,无法实现:
-
作用域插槽的数据传递
-
多个插槽的精准定位
-
编译时的优化处理
使用
<template>的主要原因:
包裹多个元素 - 避免多个根元素错误
不渲染为 DOM - 保持 HTML 结构清晰
更好的可读性 - 明确插槽边界
<!-- 没有 template 的歧义 --> <LayoutComponent> <div>这个 div 要去哪个插槽?</div> <p>这个 p 标签呢?</p> </LayoutComponent> <!-- 使用 template 明确指定 --> <LayoutComponent> <template #header> <div>我去 header 插槽</div> </template> <template #main> <p>我去 main 插槽</p> </template> </LayoutComponent>简单规则:
单个元素插入默认插槽 → 可直接使用
多个元素或具名插槽 → 必须用
<template>需要接收子组件数据 → 必须用
<template>
单个/默认/匿名插槽slot
<template>
<div class="child">
<h3>子组件标题</h3>
<!-- 默认插槽 -->
<slot></slot>
<p>子组件底部</p>
</div>
</template>
<template>
<ChildComponent>
<!-- 插入到子组件插槽位置的内容 -->
<p>这是插入到插槽的内容</p>
<button>点击按钮</button>
</ChildComponent>
</template>
具名插槽:slot name="":多个插槽,需要区分
v-slot="name",在2.6后可缩写为#"name"
<template>
<div class="layout">
<header>
<!-- 具名插槽 -->
<slot name="header"></slot>
</header>
<main>
<!-- 默认插槽 -->
<slot></slot>
</main>
<footer>
<!-- 具名插槽 vue2.6写法 -->
<slot name="footer"></slot>
</footer>
</div>
</template>
<template>
<LayoutComponent>
<!-- 使用 # 简写 -->
<template #header>
<h1>页面标题</h1>
</template>
<p>主要内容</p>
<template v-slot:footer>
<p>版权信息</p>
</template>
</LayoutComponent>
</template>
作用域插槽 :接收子组件数据
父v-slot="子slot中的变量"
子slot :属性名="变量"
<template>
<ul>
<li v-for="item in items" :key="item.id">
<!-- 向父组件传递数据 -->
<slot :item="item" :index="index"></slot>
</li>
</ul>
</template>
<script>
export default {
data() {
return {
items: [
{ id: 1, name: '项目A' },
{ id: 2, name: '项目B' }
]
}
}
}
</script>
<template>
<ListComponent>
<!-- 接收子组件传递的数据 -->
<template v-slot:default="slotProps">
<span>{{ slotProps.item.name }} - 索引: {{ slotProps.index }}</span>
</template>
</ListComponent>
</template>
解构写法:
<template>
<ListComponent>
<template v-slot:default="{ item, index }">
<span>{{ item.name }} - 索引: {{ index }}</span>
</template>
</ListComponent>
</template>
应用:如el 的slot
绑定触发元素
https://element.eleme.cn/#/zh-CN/component/popover
slot="reference"
<el-popover
placement="bottom-start"
trigger="hover">
<span slot="reference">弹出图片</span>
<img :src="guidePicUrl"/>
</el-popover>
内置指令:数据/事件关联DOM
将数据和事件处理程序与 DOM 元素关联,以实现动态数据渲染、事件处理、条件渲染、循环渲染等功能。
v-bind(简写:):绑定属性到 Vue 实例中的数据
它允许你在模板中动态设置元素属性的值
<div v-bind:id="id"></div>
<div :id="id"></div>
v-on (简写@): 监听DOM事件、触发Vue实例中的方法
<button @click="doSomething">Click me</button>
vue父子组件之间双向数据绑定的(vue2/vue3)_vue3父子组件双向绑定_前端一枚的博客-CSDN博客
Vue3的8种和Vue2的12种组件通信,值得收藏 - 掘金
v-model:modelValue=v-modelv-model=v-bind(:)+v-on(@)
v-html
内容直接作为普通 HTML 插入
事件修饰符和JS原生方法
串联
例如 @click.prevent.self 会阻止所有点击的默认行为,而 @click.self.prevent 只会阻止对元素自身的点击。
.passive 与 .prevent 不能共用:因为 .passive 已经向浏览器承诺不会阻止默认行为。
点击事件
<div @click="handleDivClick">
<button @click.stop="handleButtonClick">Click Me</button>
</div>
| 修饰符 | 等效原生 JS | 描述 | 常用场景 |
|---|---|---|---|
.stop(常用) |
event.stopPropagation() |
阻止事件冒泡 | 点击按钮时,防止触发父元素的点击事件 |
.prevent(常用) |
event.preventDefault() |
阻止默认行为 |
阻止表单提交(页面不会刷新)、链接跳转等 |
.self |
if (event.target !== event.currentTarget) return |
仅当事件在元素自身(而非子元素)触发时生效 | 点击弹窗背景(非内容区)关闭弹窗 |
.capture |
addEventListener(..., {capture: true}) |
使用事件捕获模式(默认是冒泡模式) | 需要先在外层处理事件时 |
.once |
element.addEventListener('click', func, { once: true }) |
事件只触发一次 | 表单提交按钮,防止重复提交 |
.passive |
addEventListener(..., {passive: true}) |
优化滚动性能,告知浏览器不会阻止默认行为 | 移动端滚动、触摸事件,提升流畅度 |
.native |
- | 监听组件根元素的原生事件 (Vue 2 only) | 在自定义组件上监听原生点击事件 |
.left |
if (event.button !== 0) return |
只当点击鼠标左键时触发 | - |
.right |
if (event.button !== 2) return |
只当点击鼠标右键时触发 | 自定义右键菜单 |
.middle |
if (event.button !== 1) return |
只当点击鼠标中键时触发 | - |
.exact |
- | 精确控制系统修饰键的组合 | 精确监听只有 Ctrl 被按下的情况 |
.native = addEventListener
.native 修饰符表示监听组件根元素的原生DOM事件keydown,而不是组件内部触发的自定义事件(emit keydown)。原生事件不会自动冒泡到组件根元素(除非手动实现)
-
不加
.native:
Vue 会在组件实例上查找this.$emit('keydown')触发的自定义事件 -
加
.native:
Vue 直接监听组件根元素的addEventListener('keydown')
vue3 已移除native
.native 废弃:
改为通过 emits 选项显式声明自定义事件
未声明的事件默认视为原生事件
自定义组件上的 v-on 默认监听的是组件发出的自定义事件。要监听原生事件,需要将事件名全部小写,如 @click 变为 @click.native 在 Vue 3 中应改为 @click(如果组件没有发射该事件,Vue 会将其作为原生事件 fallback)或使用 $attrs。
.capture = addEventListener ('click', handleClick, true)捕获阶段监听
希望最外层的组件能够最先处理点击事件
<div @click.capture="handleCaptureClick">
<button @click="handleButtonClick">Click Me</button>
</div>
.once = addEventListener ('click', handleClick,{once: true}):事件处理只触发一次,之后不再生效
弹出提示
.self = event.target ==event.currentTarget 只在事件目标是当前元素时处理事件
防止在子元素上点击时触发事件处理程序
点击 div 的空白区域会触发 handleDivClick 方法,但点击按钮不会触发该方法:
<template>
<div @click.self="handleDivClick">
<button>Click Me</button>
</div>
</template>
<script>
export default {
methods: {
handleDivClick() {
console.log('Div clicked');
}
}
}
</script>
.passive =element.addEventListener('scroll', handleScroll, { passive: true });优化滚动事件的性能
告诉浏览器事件处理程序不会调用 event.preventDefault() 来提高性能,不会等待事件处理程序执行完毕再进行滚动渲染。
键盘事件( @keydown, @keyup)
| 类别 | 修饰符示例 | 描述 |
|---|---|---|
| 键码 (Vue 2) | .enter, .tab, .delete, .esc, .space, .up, .down, .left, .right |
只在按下特定按键时触发 |
| 系统修饰键 | .ctrl, .alt, .shift, .meta (Cmd/Win) |
需要同时按下修饰键才触发 |
.exact |
@keydown.ctrl.exact |
有且只有 Ctrl 被按下时触发 |
更多推荐



所有评论(0)