news 2026/3/29 15:17:48

最新Quasar 指南4:34803字带你了解Quasar vue指令核心用法 - Quasar 完全教程:从基础到实战 --Ryan

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
最新Quasar 指南4:34803字带你了解Quasar vue指令核心用法 - Quasar 完全教程:从基础到实战 --Ryan

Quasar v-close-popup 指令核心用法总结

v-close-popup 是 Quasar 框架的辅助指令,专为 QDialog(对话框)和 QMenu(菜单)设计,点击绑定该指令的元素时,会自动关闭其祖先链中的弹出组件(优先关闭最近的父级弹出组件)。

核心配置(API)

指令值支持 Boolean、Number 或 String 类型,不同值对应不同关闭行为:

指令值行为说明示例
0 / 'false'禁用指令(点击不关闭任何弹出组件)v-close-popup="0"
< 0(如 -1)关闭链中所有弹出组件v-close-popup="-1"
1 / 'true' / 未定义仅关闭最近的父级弹出组件(默认行为)v-close-popup/v-close-popup="true"
> 1(如 2、3)关闭链中指定数量的父级弹出组件(链式 QMenu 视为 1 个弹出组件)v-close-popup="2"(关闭 2 级父弹出组件)

基础用法

1. 关闭单个弹出组件(QMenu/QDialog)

示例:QMenu 中使用
<q-btn label="打开菜单" color="primary"> <q-menu> <q-list> <!-- 点击关闭当前菜单 --> <q-item clickable v-close-popup> <q-item-section>菜单选项 1</q-item-section> </q-item> <!-- 动态禁用指令(cancelEnabled 为布尔值) --> <q-item clickable v-close-popup="cancelEnabled"> <q-item-section>菜单选项 2</q-item-section> </q-item> </q-list> </q-menu> </q-btn>
示例:QDialog 中使用
<q-dialog v-model="isDialogOpen"> <q-card> <q-card-actions> <!-- 点击关闭当前对话框 --> <q-btn label="取消" v-close-popup /> <q-btn label="确认" v-close-popup /> </q-card-actions> </q-card> </q-dialog>

2. 关闭多个级别的弹出组件

适用于弹出组件嵌套场景(如 “对话框内嵌套菜单”“菜单内嵌套子菜单”):

<!-- 对话框内嵌套菜单,点击关闭菜单和对话框(2 级弹出组件) --> <q-dialog v-model="dialog"> <q-card> <q-btn label="打开菜单"> <q-menu> <q-list> <q-item clickable v-close-popup="2"> <q-item-section>关闭菜单和对话框</q-item-section> </q-item> </q-list> </q-menu> </q-btn> </q-card> </q-dialog> <!-- 嵌套对话框,点击关闭两个对话框(2 级弹出组件) --> <q-dialog v-model="dialog1"> <q-card> <q-btn @click="dialog2 = true" label="打开子对话框" /> <q-dialog v-model="dialog2"> <q-card> <q-btn label="关闭两个对话框" v-close-popup="2" /> </q-card> </q-dialog> </q-card> </q-dialog>

3. 链式菜单特殊规则

链式 QMenu(一个菜单直接嵌套在另一个菜单内)视为 1 个弹出组件,无需指定多级数值,使用v-close-popup即可关闭整个链式菜单:

<q-btn label="主菜单"> <q-menu> <q-list> <q-item clickable> <q-item-section>子菜单</q-item-section> <q-menu> <q-list> <q-item clickable> <q-item-section>三级菜单</q-item-section> <q-menu> <!-- 点击关闭整个链式菜单(视为 1 级) --> <q-item clickable v-close-popup> <q-item-section>关闭所有菜单</q-item-section> </q-item> </q-menu> </q-item> </q-list> </q-menu> </q-item> </q-list> </q-menu> </q-btn>

关键特性与注意事项

  1. 作用范围:仅对 QDialog 和 QMenu 生效,且只关闭其祖先链中的弹出组件(非同级或后代组件);
  2. 动态控制:指令值支持响应式变量(如v-close-popup="isEnabled"),可动态启用 / 禁用关闭功能;
  3. 同级弹出组件:若弹出组件为同级关系(非嵌套),需通过v-model手动控制关闭,指令无法直接关闭;
  4. QPopupProxy 影响:QPopupProxy 会分隔链式菜单,此时嵌套菜单需按实际级别指定数值。

Quasar v-intersection 指令核心用法总结

v-intersection 是 Quasar 基于 Intersection Observer API 封装的指令,用于监听元素是否进入 / 离开视口或指定的根容器,常用于实现懒加载、无限滚动、曝光埋点等功能。

一、核心配置(API)

1. 指令值格式

支持Function(回调函数)或Object(配置对象),两种格式的核心参数如下:

配置项类型默认值说明
handlerFunction-元素交叉状态变化时的回调函数(必传)
optionsObject{}Intersection Observer 配置项(可选)
onceBooleanfalse是否只触发一次回调(触发后自动停止监听)

2. options 详细配置(同原生 API)

子配置项类型默认值说明
rootElement/Stringnull根容器(监听元素相对于该容器的交叉状态),传 CSS 选择器或 DOM 元素,默认是视口
rootMarginString0px根容器的外边距(扩展 / 收缩根容器的检测区域),支持px/%,如100px 20px
thresholdNumber/Array0交叉比例阈值(0~1),触发回调的交叉比例,如0.5表示元素 50% 进入根容器时触发

3. 回调函数参数

(entries, observer, isIntersecting) => { // entries: 原生 IntersectionObserverEntry 数组 // observer: 原生 IntersectionObserver 实例 // isIntersecting: 布尔值,元素是否进入根容器(简化判断) }

二、基础用法

1. 最简用法(监听视口交叉)

直接传入回调函数,默认监听元素是否进入视口:

<template> <!-- 监听元素进入/离开视口 --> <div v-intersection="handleIntersection" class="h-200 bg-grey-2"> 滚动视口观察我 </div> </template> <script setup> const handleIntersection = (entries, observer, isIntersecting) => { if (isIntersecting) { console.log('元素进入视口') // 执行懒加载/曝光埋点等逻辑 } else { console.log('元素离开视口') } } </script>

2. 只触发一次(如懒加载图片)

通过once: true配置,触发一次后停止监听,避免重复执行:

<template> <!-- 图片懒加载:进入视口后加载真实图片,只触发一次 --> <div class="img-placeholder" v-intersection="lazyLoadConfig"> <img ref="imgRef" :src="placeholder" alt="懒加载图片" /> </div> </template> <script setup> import { ref } from 'vue' const imgRef = ref(null) const placeholder = 'https://via.placeholder.com/400x300' const realSrc = 'https://picsum.photos/400/300' const lazyLoadConfig = { once: true, // 只触发一次 handler: (entries, observer, isIntersecting) => { if (isIntersecting) { imgRef.value.src = realSrc // 加载真实图片 } } } </script>

3. 自定义根容器与阈值

监听元素相对于指定容器的交叉状态,设置交叉比例阈值:

<template> <!-- 根容器:带滚动条的区域 --> <div ref="rootContainer" class="h-400 overflow-auto bg-grey-1 p-4"> <div class="h-800 mb-4">占位内容</div> <!-- 监听该元素相对于根容器的交叉状态 --> <div v-intersection="containerIntersectConfig" class="h-200 bg-primary text-white"> 相对于根容器的交叉元素 </div> </div> </template> <script setup> import { ref } from 'vue' const rootContainer = ref(null) const containerIntersectConfig = { handler: (entries, observer, isIntersecting) => { console.log('相对于根容器的交叉状态:', isIntersecting) }, options: { root: rootContainer, // 根容器为 ref 指向的 DOM 元素 threshold: 0.3, // 元素 30% 进入根容器时触发 rootMargin: '50px' // 根容器外边距扩展 50px 检测区域 } } </script>

4. 无限滚动(加载更多数据)

结合滚动容器,监听 “加载更多” 按钮的交叉状态,实现无限滚动:

<template> <div ref="scrollContainer" class="h-500 overflow-auto"> <div v-for="item in list" :key="item.id" class="p-4 border-b"> {{ item.content }} </div> <!-- 监听加载更多按钮进入视口 --> <div v-intersection="loadMoreConfig" class="text-center p-4"> <q-spinner v-if="loading" /> <span v-else>{{ hasMore ? '加载更多' : '没有更多数据了' }}</span> </div> </div> </template> <script setup> import { ref, reactive } from 'vue' const scrollContainer = ref(null) const list = ref([]) const loading = ref(false) const hasMore = ref(true) let page = 1 // 模拟加载数据 const fetchData = async () => { loading.value = true // 模拟接口请求 await new Promise(resolve => setTimeout(resolve, 1000)) const newData = Array.from({ length: 10 }, (_, i) => ({ id: `${page}-${i}`, content: `列表项 ${page}-${i}` })) list.value.push(...newData) loading.value = false if (page >= 5) hasMore.value = false // 模拟最多5页数据 page++ } // 无限滚动配置 const loadMoreConfig = { handler: (entries, observer, isIntersecting) => { // 元素进入视口 + 非加载中 + 有更多数据时,加载下一页 if (isIntersecting && !loading.value && hasMore.value) { fetchData() } }, options: { root: scrollContainer, threshold: 0.1 } } // 初始化加载第一页 fetchData() </script>

特殊得到

<template> <div class="relative-position"> <div class="example-area q-pa-lg scroll"> <div class="example-filler" /> <q-list> <q-item v-for="n in 30" :key="n" :data-id="n" class="q-my-md q-pa-sm bg-grey-3" v-intersection="onIntersection" > <q-item-section class="text-center" style="background: #eee"> Item #{{ n }} </q-item-section> </q-item> </q-list> <div class="example-filler" /> </div> <div class="example-state bg-primary text-white overflow-hidden rounded-borders text-center absolute-top-left q-ma-md q-pa-sm"> <transition-group v-if="inView.length > 0" name="in-view" tag="ul"> <li v-for="i in inView" :key="i" class="in-view-item"> {{i}} </li> </transition-group> </div> </div> </template> <script> import { ref } from 'vue' export default { setup () { const inView = ref([]) function onIntersection (entry) { if (entry.isIntersecting === true) { add(entry.target.dataset.id) } else { remove(entry.target.dataset.id) } } function add (i) { remove(i) inView.value.push(i) inView.value.sort(sortAtoi) } function remove (i) { let index while ((index = inView.value.indexOf(i)) > -1) { inView.value.splice(index, 1) inView.value.sort(sortAtoi) } } function sortAtoi (a, b) { return Number(a) - Number(b) } return { inView, onIntersection } } } </script> <style lang="sass" scoped> .example-state width: 50px height: 226px opacity: 0.85 ul list-style: none margin: 0 padding: 0 li padding: 0.5em .example-area height: 300px .example-filler height: 350px .in-view-item transition: all 0.3s display: block .in-view-enter, .in-view-leave-to opacity: 0 transform: translateX(-30px) .in-view-leave-active position: absolute </style>

三、关键特性与注意事项

1. 核心特性

  • 性能优化:基于原生 Intersection Observer API,异步监听交叉状态,不阻塞主线程(优于 scroll 事件监听);
  • 灵活配置:支持自定义根容器、阈值、触发次数,适配不同业务场景;
  • 简化判断:回调函数直接提供isIntersecting布尔值,无需解析entries数组;
  • 自动清理:组件卸载时自动停止监听并销毁 Observer 实例,避免内存泄漏。

2. 注意事项

  • 根容器要求:若指定root为自定义容器,该容器必须具有overflow: auto/scroll样式(否则无法检测滚动);
  • 阈值理解threshold: 0表示元素刚进入根容器时触发,threshold: 1表示元素完全进入根容器时触发;
  • rootMargin 单位:仅支持px%,不支持rem/em等单位;
  • 浏览器兼容性:支持所有现代浏览器,IE 需 polyfill(Quasar 未内置,需自行引入 intersection-observer polyfill)。

3. 高级技巧

  • 手动停止监听:若需在回调外停止监听,可通过observer.disconnect()销毁 Observer 实例:

    javascript

    运行

    const handleIntersection = (entries, observer) => { if (someCondition) { observer.disconnect() // 手动停止监听 } }
  • 多阈值触发threshold传数组(如[0, 0.5, 1]),元素交叉比例达到每个阈值时都会触发回调:

    javascript

    运行

    options: { threshold: [0, 0.5, 1] // 进入、半进入、完全进入时各触发一次 }

四、常见业务场景

  1. 图片 / 视频懒加载:元素进入视口后加载真实资源,减少首屏加载时间;
  2. 无限滚动列表:监听 “加载更多” 按钮,自动加载下一页数据;
  3. 曝光埋点:元素进入视口时上报埋点数据(如广告曝光、内容曝光);
  4. 滚动动画触发:元素进入视口时执行 CSS/JS 动画;
  5. 吸顶 / 吸底组件:监听元素与视口的交叉状态,动态切换固定定位。

Quasar v-ripple 指令核心用法总结

v-ripple 是 Quasar 框架提供的 Material 风格波纹效果指令,可快速为任意 DOM 元素或组件添加点击 / 触摸波纹反馈,核心信息如下:

核心注意事项

  1. 无需在已有波纹效果的 Quasar 组件(如 QBtn)上使用,需通过组件自身ripple属性配置;
  2. 目标元素必须设置position: relative或 Quasar 辅助类relative-position,否则波纹定位异常。

核心配置(API)

配置项类型说明示例
center(修饰符)Boolean波纹从元素绝对中心触发(默认从触摸点触发)v-ripple.center
early(修饰符 / 属性)Boolean提前触发波纹(用户首次交互即触发,如 mousedown/touchstart)v-ripple.earlyv-ripple="{ early: true }"
stop(修饰符)Boolean阻止点击 / 触摸事件冒泡v-ripple.stop
colorString波纹颜色(使用 Quasar 调色板颜色名)v-ripple:purplev-ripple="{ color: 'yellow' }"
keyCodesArray/Number触发波纹的键盘按键编码v-ripple="{ keyCodes: [13, 32] }"(回车 / 空格)
指令值Boolean控制波纹启用 / 禁用v-ripple="state"(state 为布尔值)

基础用法

1. 基础效果

vue

<div v-ripple class="relative-position p-4 bg-grey-2"> Click/tap me </div>

2. 自定义颜色

  • 直接通过参数指定颜色:v-ripple:purple
  • 通过对象配置颜色:v-ripple="{ color: 'yellow' }"
  • 默认继承元素文本颜色

3. 定位控制

  • 中心触发:v-ripple.center
  • 触摸点触发(默认):v-ripple

4. 提前触发(v1.9.8+)

vue

<div v-ripple.early class="relative-position p-4 bg-grey-2"> 提前触发波纹 </div>

5. 动态启用 / 禁用

vue

<template> <div v-ripple="isRippleEnabled" class="relative-position p-4 bg-grey-2"> 动态控制波纹 </div> <q-toggle v-model="isRippleEnabled" label="启用波纹" /> </template> <script setup> const isRippleEnabled = ref(true) </script>

关键特性

  1. 兼容性:适配桌面端(点击 / 键盘)和移动端(触摸)交互;
  2. 灵活性:支持修饰符(.center/.early/.stop)和对象配置两种方式;
  3. 样式继承:默认继承元素文本颜色,减少配置成本;
  4. 事件控制:可通过stop修饰符阻止事件冒泡,适配复杂交互场景。

Quasar v-mutation 指令深度解析(附实战案例)

v-mutation 是 Quasar 对原生MutationObserver API的封装,用于监听 DOM 元素的属性、子节点、文本内容变化,解决了传统 DOM 监听需手动绑定 / 销毁的痛点,适用于第三方组件 DOM 监控、动态内容渲染检测等场景。

一、核心能力与适用场景

1. 能监听的 DOM 变化类型

  • ✅ 元素属性(class/style/id 等)增删改
  • ✅ 子节点(新增 / 删除 / 移动)
  • ✅ 文本内容修改
  • ✅ 子孙节点变化(通过subtree配置)

2. 典型业务场景

  • 第三方组件状态同步(如监听弹窗display属性变化)
  • 动态渲染内容检测(如富文本编辑器加载完成)
  • DOM 结构合规性校验(如列表渲染后子节点数量检查)
  • 响应式样式变更监听(如主题切换后 class 变化)

二、完整 API 配置

1. 指令值格式(二选一)

格式类型示例说明
回调函数v-mutation="handleChange"最简用法,默认监听所有变化
配置对象v-mutation="{ handler: fn, options: {}, once: true }"精细控制监听规则

2. 配置对象核心参数

参数类型默认值作用
handlerFunction-必传,DOM 变化触发的回调函数
optionsObject见下表监听规则配置(同原生 MutationObserver)
onceBooleanfalse是否仅触发一次(触发后自动停止监听)

3. options 详细配置(精准控制监听范围)

子配置类型默认值作用实战建议
attributesBooleantrue监听属性变化仅需监听属性时设为true,其他设为false
attributeFilterArray[]仅监听指定属性(如['class', 'style']避免无关属性触发回调,提升性能
attributeOldValueBooleanfalse回调中返回属性旧值需要对比属性前后值时开启
childListBooleantrue监听子节点增删列表渲染 / 动态组件场景必开
subtreeBooleantrue监听子孙节点变化仅需监听当前元素时设为false
characterDataBooleantrue监听文本内容变化仅需监听文本时开启
characterDataOldValueBooleanfalse回调中返回文本旧值需要对比文本前后值时开启

4. 回调函数参数详解

// 回调函数完整参数 const handleMutation = (mutations, observer) => { mutations.forEach(mutation => { // 1. 变化类型:attributes/childList/characterData console.log('变化类型:', mutation.type); // 2. 发生变化的DOM元素 console.log('目标元素:', mutation.target); // 3. 不同变化类型的专属信息 if (mutation.type === 'attributes') { console.log('变化属性名:', mutation.attributeName); console.log('属性旧值:', mutation.oldValue); // 需开启 attributeOldValue } else if (mutation.type === 'childList') { console.log('新增节点:', mutation.addedNodes); console.log('移除节点:', mutation.removedNodes); } else if (mutation.type === 'characterData') { console.log('文本旧值:', mutation.oldValue); // 需开启 characterDataOldValue } }); };

三、实战案例(覆盖 80% 业务场景)

案例0

<template> <div> <div class="row no-wrap justify-around q-px-md q-pt-md"> <div v-mutation="handler1" @dragenter="onDragEnter" @dragleave="onDragLeave" @dragover="onDragOver" @drop="onDrop" class="drop-target rounded-borders overflow-hidden" > <div id="box1" draggable="true" @dragstart="onDragStart" class="box navy" /> <div id="box2" draggable="true" @dragstart="onDragStart" class="box red" /> <div id="box3" draggable="true" @dragstart="onDragStart" class="box green" /> <div id="box4" draggable="true" @dragstart="onDragStart" class="box orange" /> <div id="box5" draggable="true" @dragstart="onDragStart" class="box navy" /> <div id="box6" draggable="true" @dragstart="onDragStart" class="box red" /> <div id="box7" draggable="true" @dragstart="onDragStart" class="box green" /> <div id="box8" draggable="true" @dragstart="onDragStart" class="box orange" /> </div> <div v-mutation="handler2" @dragenter="onDragEnter" @dragleave="onDragLeave" @dragover="onDragOver" @drop="onDrop" class="drop-target rounded-borders overflow-hidden" /> </div> <div class="row justify-around items-start"> <div class="col row justify-center q-pa-md"> <div class="text-subtitle1"> Mutation Info </div> <div v-for="status in status1" :key="status"> {{ status }} </div> </div> <div class="col row justify-center q-pa-md"> <div class="text-subtitle1"> Mutation Info </div> <div v-for="status in status2" :key="status"> {{ status }} </div> </div> </div> </div> </template> <script> import { ref } from 'vue' export default { setup () { const status1 = ref([]) const status2 = ref([]) return { status1, status2, handler1 (mutationRecords) { status1.value = [] for (const index in mutationRecords) { const record = mutationRecords[ index ] const info = `type: ${record.type}, nodes added: ${record.addedNodes.length > 0 ? 'true' : 'false'}, nodes removed: ${record.removedNodes.length > 0 ? 'true' : 'false'}, oldValue: ${record.oldValue}` status1.value.push(info) } }, handler2 (mutationRecords) { status2.value = [] for (const index in mutationRecords) { const record = mutationRecords[ index ] const info = `type: ${record.type}, nodes added: ${record.addedNodes.length > 0 ? 'true' : 'false'}, nodes removed: ${record.removedNodes.length > 0 ? 'true' : 'false'}, oldValue: ${record.oldValue}` status2.value.push(info) } }, // store the id of the draggable element onDragStart (e) { e.dataTransfer.setData('text', e.target.id) e.dataTransfer.dropEffect = 'move' }, onDragEnter (e) { // don't drop on other draggables if (e.target.draggable !== true) { e.target.classList.add('drag-enter') } }, onDragLeave (e) { e.target.classList.remove('drag-enter') }, onDragOver (e) { e.preventDefault() }, onDrop (e) { e.preventDefault() // don't drop on other draggables if (e.target.draggable === true) { return } const draggedId = e.dataTransfer.getData('text') const draggedEl = document.getElementById(draggedId) // check if original parent node if (draggedEl.parentNode === e.target) { e.target.classList.remove('drag-enter') return } // make the exchange draggedEl.parentNode.removeChild(draggedEl) e.target.appendChild(draggedEl) e.target.classList.remove('drag-enter') } } } } </script> <style scoped lang="sass"> .drop-target height: 400px width: 200px min-width: 200px background-color: gainsboro .drag-enter outline-style: dashed .box width: 100px height: 100px float: left cursor: pointer @media only screen and (max-width: 500px) .drop-target height: 200px width: 100px min-width: 100px background-color: gainsboro .box width: 50px height: 50px .box:nth-child(3) clear: both .navy background-color: navy .red background-color: firebrick .green background-color: darkgreen .orange background-color: orange </style>

案例 1:监听第三方组件弹窗状态(属性监听)

需求:监听第三方弹窗组件的display属性变化,同步本地弹窗状态

<template> <!-- 第三方弹窗组件(假设无法通过v-model控制) --> <third-party-modal ref="modalRef" v-mutation="modalMutationConfig" /> <div>弹窗状态:{{ isModalOpen ? '打开' : '关闭' }}</div> </template> <script setup> import { ref } from 'vue'; const isModalOpen = ref(false); const modalRef = ref(null); // 弹窗监听配置 const modalMutationConfig = { handler: (mutations) => { const display = mutations[0].target.style.display; isModalOpen.value = display !== 'none'; }, options: { attributes: true, // 仅监听属性 attributeFilter: ['style'], // 仅监听style属性 subtree: false, // 不监听子孙节点 childList: false, // 不监听子节点 characterData: false // 不监听文本 } }; </script>

案例 2:动态列表渲染完成检测(子节点监听)

需求:监听异步加载的列表是否渲染完成,完成后执行统计逻辑

<template> <div ref="listRef" v-mutation="listMutationConfig" class="list-container"> <!-- 异步渲染的列表项 --> <div v-for="item in listData" :key="item.id" class="list-item"> {{ item.name }} </div> </div> </template> <script setup> import { ref, onMounted } from 'vue'; const listRef = ref(null); const listData = ref([]); // 模拟异步加载数据 const fetchList = () => { setTimeout(() => { listData.value = [ { id: 1, name: '商品1' }, { id: 2, name: '商品2' }, { id: 3, name: '商品3' } ]; }, 1000); }; // 列表渲染监听配置 const listMutationConfig = { once: true, // 仅触发一次 handler: () => { console.log('列表渲染完成!当前数量:', listRef.value.children.length); // 执行统计/埋点逻辑 }, options: { childList: true, // 仅监听子节点变化 attributes: false, characterData: false } }; onMounted(() => { fetchList(); }); </script>

案例 3:文本内容修改监控(防篡改场景)

需求:监听重要文本区域的内容修改,记录修改日志

<template> <div ref="textRef" v-mutation="textMutationConfig" contenteditable="true" class="editable-text" > 请输入重要内容(修改会被记录) </div> <div class="log">修改日志:{{ logs.join(', ') }}</div> </template> <script setup> import { ref } from 'vue'; const textRef = ref(null); const logs = ref([]); // 文本修改监听配置 const textMutationConfig = { handler: (mutations) => { const oldText = mutations[0].oldValue; const newText = mutations[0].target.textContent; logs.value.push(`旧值:${oldText} → 新值:${newText}`); }, options: { characterData: true, // 监听文本变化 characterDataOldValue: true, // 返回文本旧值 subtree: true, // 监听子节点文本(contenteditable 元素的文本可能在子节点) attributes: false, childList: false } }; </script>

四、性能优化与避坑指南

1. 性能优化技巧

  • ✅ 最小化监听范围:通过attributeFilter/subtree限制监听内容,避免全量监听
  • ✅ 按需开启once:只需一次监听的场景(如加载完成),开启后自动销毁 Observer
  • ✅ 避免循环触发:回调中修改当前监听元素的 DOM 时,需加条件判断(如通过标记跳过

2. 常见坑点与解决方案

坑点解决方案
回调不触发1. 检查options配置是否正确2. 确保监听元素已挂载(配合nextTick)3. 第三方组件需等待其渲染完成
重复触发回调1. 开启once(单次场景)2. 通过条件过滤无效变化(如属性值未实际改变)
内存泄漏1. 组件卸载时 Quasar 会自动销毁 Observer,无需手动处理2. 手动停止监听用observer.disconnect()
无法监听输入框值变化输入框值变化优先用input事件,DOM 监听仅作为兜底(监听value属性需开启attributes

3. 手动控制监听生命周

五、与原生 MutationObserver 的对比

特性Quasar v-mutation原生 MutationObserver
初始化一行指令绑定需手动创建实例、调用observe
销毁组件卸载自动销毁需手动调用disconnect
配置简化默认配置覆盖常见场景需手动配置所有参数
兼容性自动适配(需 polyfill 时自行引入)需手动处理兼容性
易用性高(Vue 生态无缝集成)低(原生 API 繁琐)

六、扩展场景:结合其他指令使用

1. v-mutation + v-intersection(曝光 + 内容修改监控)

<template> <!-- 元素进入视口后,开始监控内容修改 --> <div v-intersection="initMutation" ref="cardRef" class="content-card" > 曝光后监控修改 </div> </template> <script setup> import { ref } from 'vue'; const cardRef = ref(null); let mutationDirective = null; const initMutation = (entries, observer, isIntersecting) => { if (isIntersecting) { // 元素曝光后,手动绑定v-mutation指令(Quasar 指令可通过vnode动态绑定) mutationDirective = cardRef.value.$q.directives.mutation; mutationDirective.bind(cardRef.value, { value: { handler: () => console.log('内容已修改'), options: { characterData: true } } }); observer.disconnect(); // 停止曝光监听 } }; </script>

Quasar v-morph 指令核心用法总结

v-morph 是 Quasar 提供的元素过渡变形指令,用于实现两个不同 DOM 元素之间的平滑过渡动画(如形状、位置、大小的渐变),核心原理是在过渡过程中创建一个 “替身元素” 模拟变形效果,适用于模态框弹出、组件切换、元素复用等场景,替代传统的淡入淡出 / 滑动过渡,提升交互视觉体验。

一、核心概念与原理

  1. 变形主体:通过指令绑定的 “源元素” 和 “目标元素”(需通过key关联);
  2. 替身元素:过渡期间自动创建的临时元素,继承源元素的样式并逐步过渡到目标元素样式;
  3. 过渡触发:通过修改绑定的key值触发变形(key 变化时,Quasar 识别源 / 目标元素并启动动画);
  4. 样式继承:默认继承元素的width/height/position/background/border-radius等核心样式,确保变形连贯。

二、基础配置(API)

1. 指令绑定格式

<!-- 核心:v-morph="唯一key值",key变化触发变形 --> <component v-morph="morphKey" :is="currentComponent" /> <!-- 或单个元素切换状态 --> <div v-morph="morphKey" :class="isActive ? 'active' : 'inactive'" />

2. 全局 / 局部配置

可通过 Quasar 配置或指令修饰符自定义变形行为:

配置项类型默认值说明
durationNumber300变形动画时长(ms)
easingStringcubic-bezier(0.4, 0, 0.2, 1)动画缓动函数
delayNumber0动画延迟(ms)
opacityBooleantrue是否包含透明度过渡
scaleBooleanfalse是否包含缩放过渡(优先级高于宽高过渡)
zIndexNumber4000替身元素的 z-index(确保覆盖其他元素)

3. 指令修饰符

修饰符作用示例
.no-opacity禁用透明度过渡v-morph.no-opacity="key"
.scale启用缩放过渡(替代宽高过渡)v-morph.scale="key"
.instant立即完成变形(无动画)v-morph.instant="key"

三、实战案例

案例 1:按钮 → 模态框变形(经典场景)

实现点击按钮后,按钮平滑变形为模态框的过渡效果:

<template> <div class="q-pa-md"> <!-- 源元素:按钮 --> <q-btn v-if="!showModal" v-morph="morphKey" label="打开模态框" color="primary" class="q-mb-md" @click="showModal = true" /> <!-- 目标元素:模态框 --> <q-dialog v-if="showModal" v-morph="morphKey" v-model="showModal" maximized="false" > <q-card class="q-pa-md"> <q-card-title>变形后的模态框</q-card-title> <q-card-section> 这是由按钮变形而来的模态框,关闭后会反向变形为按钮~ </q-card-section> <q-card-actions> <q-btn label="关闭" color="negative" @click="showModal = false" /> </q-card-actions> </q-card> </q-dialog> </div> </template> <script setup> import { ref } from 'vue' const showModal = ref(false) const morphKey = ref('btn-to-modal') // 固定key,通过v-if切换元素触发变形 </script>

案例 2:卡片切换变形(组件复用)

实现两个卡片之间的平滑变形切换:

<template> <div class="q-pa-md"> <div class="flex flex-col gap-md"> <q-btn label="切换卡片" color="primary" @click="toggleCard" /> <!-- 变形容器:通过key切换卡片 --> <component v-morph="cardKey" :is="currentCard" class="w-64 h-40 rounded-lg shadow-md flex items-center justify-center" /> </div> </div> </template> <script setup> import { ref, computed } from 'vue' const currentCard = ref('card1') const cardKey = ref('card-switch') // 变形key // 切换卡片组件 const toggleCard = () => { currentCard.value = currentCard.value === 'card1' ? 'card2' : 'card1' } // 卡片组件1 const Card1 = { template: `<div class="bg-primary text-white">卡片 1</div>` } // 卡片组件2 const Card2 = { template: `<div class="bg-secondary text-white">卡片 2</div>` } </script>

案例 3:带缩放的元素变形(修饰符使用)

启用缩放过渡,实现元素大小和位置的同步变形:

<template> <div class="q-pa-md relative"> <!-- 源元素:小方块 --> <div v-if="!expanded" v-morph.scale="scaleKey" class="absolute top-10 left-10 w-20 h-20 bg-primary rounded-md" @click="expanded = true" /> <!-- 目标元素:大方块 --> <div v-if="expanded" v-morph.scale="scaleKey" class="absolute top-20 left-20 w-60 h-60 bg-primary rounded-lg" @click="expanded = false" /> </div> </template> <script setup> import { ref } from 'vue' const expanded = ref(false) const scaleKey = ref('scale-morph') </script>

四、关键特性与注意事项

1. 核心特性

  • 自动样式匹配:替身元素自动计算源 / 目标元素的位置、大小、样式差异,生成平滑过渡;
  • 支持动态元素:通过v-if/component切换的元素均可触发变形;
  • 轻量级实现:基于 CSS 过渡,无额外 DOM 开销(过渡后替身元素自动销毁);
  • 兼容 Quasar 组件:可直接用于 QBtn、QCard、QDialog 等内置组件。

2. 注意事项

  • key 唯一性:同一变形组的源 / 目标元素必须绑定相同的key,否则无法触发变形;
  • 定位要求:变形元素建议使用position: relative/absolute/fixed,避免静态定位导致位置计算错误;
  • 样式冲突:避免为元素设置overflow: hidden(可能裁剪替身元素),或transform手动变换(会干扰变形动画);
  • 性能优化:复杂元素变形(如包含图片 / 大量子节点)建议开启scale修饰符(缩放比宽高过渡更高效);
  • 反向变形:关闭目标元素(如模态框)时,需保持key不变,通过v-if切换回源元素,实现反向变形;
  • z-index 层级:替身元素默认 z-index 为 4000,若被其他元素覆盖,可通过全局配置提高zIndex
    // quasar.config.js framework: { config: { morph: { zIndex: 5000 } } }

3. 高级配置(全局 / 局部)

全局配置(quasar.config.js)
module.exports = { framework: { config: { morph: { duration: 500, // 动画时长500ms easing: 'ease-in-out', // 缓动函数 opacity: false // 禁用透明度过渡 } } } }
局部配置(指令值为对象)
<template> <div v-morph="morphConfig" :key="morphKey">...</div> </template> <script setup> const morphConfig = { key: 'custom-morph', // 变形key duration: 400, easing: 'cubic-bezier(0.1, 0.9, 0.2, 1)', opacity: true } </script>

五、常见问题与解决方案

问题解决方案
变形动画不触发1. 检查源 / 目标元素是否绑定相同key2. 确保通过v-if/component切换元素(而非v-show)3. 元素定位是否为非静态(relative/absolute/fixed)
变形过程中元素闪烁1. 开启opacity过渡(默认开启)2. 提高替身元素 z-index3. 避免元素有box-shadow/border等复杂样式
变形位置偏移1. 统一源 / 目标元素的父容器定位方式2. 避免使用margin控制位置(改用top/left或 flex 布局)3. 检查元素是否有transform手动变换
反向变形失效关闭目标元素时,保持key不变,仅通过v-if切换回源元素(不要修改key值)

六、适用场景总结

  1. 按钮 → 弹窗 / 抽屉:点击按钮后变形为弹窗,提升交互连贯性;
  2. 列表项 → 详情页:列表项点击后放大变形为详情页,替代跳转动画;
  3. 组件切换:不同状态组件(如空状态 / 数据状态)之间的平滑变形;
  4. 菜单展开:按钮变形为下拉菜单,增强视觉关联;
  5. 加载状态过渡:加载中元素变形为加载完成元素,减少视觉断层。

Quasar v-scroll 指令核心用法总结

v-scroll 是 Quasar 提供的滚动监听指令,用于监听元素(或视口)的滚动事件,支持配置滚动触发条件、节流控制等,适用于实现滚动加载、吸顶 / 吸底、滚动动画等场景。

一、核心配置(API)

1. 指令值格式

支持Function(滚动回调)或Object(配置对象),核心参数如下:

配置项类型默认值说明
handlerFunction-滚动触发的回调函数(必传)
targetString/Elementwindow滚动监听目标:window(视口)、CSS 选择器、DOM 元素
throttleNumber0滚动回调节流时间(ms),0 表示无节流
passiveBooleantrue滚动事件是否为被动监听(提升移动端性能)
immediateBooleanfalse是否在指令绑定时立即执行一次回调

2. 回调函数参数

(scrollPosition, event) => { // scrollPosition: 滚动位置对象 { top, left, height, width, direction } // - top/left:垂直/水平滚动距离(px) // - height/width:滚动容器高度/宽度(px) // - direction:滚动方向(up/down/left/right) // event:原生滚动事件对象 }

3. 指令修饰符

修饰符作用示例
.horizontal仅监听水平滚动v-scroll.horizontal="handler"
.vertical仅监听垂直滚动(默认)v-scroll.vertical="handler"
.once仅触发一次滚动回调v-scroll.once="handler"

二、基础用法规则

1. 最简用法(监听视口垂直滚动)

<!-- 监听视口滚动,无节流 --> <div v-scroll="handleScroll" /> <script setup> const handleScroll = (scrollPos) => { console.log('垂直滚动距离:', scrollPos.top) console.log('滚动方向:', scrollPos.direction) } </script>

2. 自定义滚动目标

<!-- 监听指定DOM元素的滚动 --> <template> <div ref="scrollContainer" class="h-400 overflow-auto"> <div class="h-1000" v-scroll="scrollConfig" /> </div> </template> <script setup> const scrollContainer = ref(null) const scrollConfig = { target: scrollContainer, // 监听指定容器 throttle: 100, // 100ms节流 handler: (scrollPos) => { console.log('容器滚动距离:', scrollPos.top) } } </script>

3. 仅监听水平滚动

<div v-scroll.horizontal="handleHorizontalScroll" /> <script setup> const handleHorizontalScroll = (scrollPos) => { console.log('水平滚动距离:', scrollPos.left) } </script>

三、关键特性与注意事项

1. 核心特性

  • 多目标支持:可监听视口、任意 DOM 元素的滚动;
  • 性能优化:支持节流、被动监听,减少高频回调开销;
  • 滚动信息封装:回调直接返回滚动位置、方向等结构化数据,无需手动计算;
  • 自动清理:组件卸载时自动移除滚动监听,避免内存泄漏。

2. 注意事项

  • 滚动容器要求:监听非视口元素时,目标元素需设置overflow: auto/scroll且有固定高度 / 宽度;
  • 节流配置:高频滚动场景(如滚动加载)建议设置throttle: 100-300,避免回调触发过频繁;
  • 被动监听:移动端建议保持passive: true,防止滚动卡顿;
  • 方向判断direction仅在滚动距离变化时更新,静止时无方向值;
  • 兼容性:兼容所有现代浏览器,无需 polyfill。

四、高级配置规则

1. 节流 + 立即执行

<div v-scroll="advancedConfig" /> <script setup> const advancedConfig = { throttle: 200, immediate: true, // 绑定时立即执行一次 handler: (scrollPos) => { // 初始化时执行一次,后续滚动每200ms触发一次 } } </script>

2. 手动停止监听

若需主动移除滚动监听,可通过回调的event对象操作:

const handleScroll = (scrollPos, event) => { if (scrollPos.top > 1000) { // 移除监听 event.target.removeEventListener('scroll', handleScroll) } }

Quasar v-scroll-fire 指令核心用法总结

v-scroll-fire 是 Quasar 专为滚动触发动作设计的指令(基于 v-scroll 扩展),核心功能是:当元素滚动到指定位置时触发一次回调(默认进入视口时触发),且仅触发一次,适用于滚动曝光、滚动加载、滚动动画触发等场景。

一、核心配置(API)

1. 指令值格式

支持Function(触发回调)或Object(配置对象),核心参数如下:

配置项类型默认值说明
handlerFunction-元素滚动到目标位置时的回调函数(必传)
offsetNumber/String0触发偏移量:- 数字:像素值(如100表示元素顶部距离视口顶部 100px 时触发)- 字符串:百分比(如50%表示元素 50% 进入视口时触发)
directionStringvertical监听方向:vertical(垂直滚动)/horizontal(水平滚动)
onceBooleantrue是否仅触发一次(默认开启,关闭后每次滚动到位置都会触发)

2. 指令修饰符

修饰符作用示例
.horizontal切换为水平滚动监听v-scroll-fire.horizontal="handler"
.repeat允许重复触发(覆盖once: falsev-scroll-fire.repeat="handler"

3. 回调函数参数

(element, position) => { // element:触发指令的DOM元素 // position:滚动位置对象 { top, left, direction }(同v-scroll) }

二、基础用法规则

1. 最简用法(元素进入视口触发)

<!-- 元素进入视口时触发一次回调 --> <div v-scroll-fire="handleScrollFire" class="h-200 bg-grey-2"> 进入视口触发 </div> <script setup> const handleScrollFire = (el, pos) => { console.log('元素进入视口:', el) console.log('当前滚动位置:', pos.top) // 执行曝光埋点/加载数据/动画触发等逻辑 } </script>

2. 自定义偏移量触发

<!-- 元素底部进入视口时触发(offset设为元素高度) --> <div ref="targetEl" v-scroll-fire="{ handler: handleScrollFire, offset: '100%' // 元素100%进入视口(底部进入)时触发 }" class="h-200 bg-grey-2" /> <!-- 或像素偏移:元素顶部距离视口顶部200px时触发 --> <div v-scroll-fire="{ handler: handleScrollFire, offset: 200 }" />

3. 水平滚动触发

<!-- 水平滚动时元素进入视口触发 --> <div v-scroll-fire.horizontal="handleHorizontalFire" class="w-200 h-100 bg-grey-2" /> <script setup> const handleHorizontalFire = (el, pos) => { console.log('水平滚动触发,左侧位置:', pos.left) } </script>

4. 重复触发

<!-- 每次滚动到位置都触发(需.repeat修饰符) --> <div v-scroll-fire.repeat="handleRepeatFire" class="h-200 bg-grey-2" /> <script setup> const handleRepeatFire = () => { console.log('重复触发(每次滚动到位置都会执行)') } </script>

三、关键特性与注意事项

1. 核心特性

  • 单次触发默认:默认仅触发一次回调,避免重复执行(如曝光埋点无需重复上报);
  • 偏移量灵活:支持像素 / 百分比偏移,精准控制触发时机;
  • 自动解绑:单次触发后自动移除滚动监听,无性能开销;
  • 依赖滚动容器:默认监听视口滚动,若元素在自定义滚动容器内,需确保容器设置overflow: auto/scroll

2. 注意事项

  • 偏移量正负
    • 正值:元素未完全进入视口时触发(如offset: 100表示元素顶部还在视口上方 100px 时触发);
    • 负值:元素部分离开视口时触发(如offset: -50表示元素顶部进入视口下方 50px 时触发);
  • 百分比偏移:基于元素自身尺寸(如50%表示元素一半进入视口);
  • 重复触发限制:开启.repeat后,每次滚动到目标位置都会触发,需注意性能(避免高频执行复杂逻辑);
  • 动态元素:若元素是动态渲染的(如v-for生成),指令会自动绑定,无需额外处理;
  • 与 v-intersection 区别
    • v-scroll-fire:基于滚动位置触发,更轻量,仅关注滚动到指定位置的单次 / 重复动作;
    • v-intersection:基于元素交叉状态,支持持续监听进入 / 离开视口,功能更全面。

四、适用场景

  1. 滚动曝光埋点:元素进入视口时上报曝光数据(默认单次触发,避免重复上报);
  2. 滚动加载动画:元素进入视口时触发 CSS/JS 动画(如渐入、滑入效果);
  3. 分段加载数据:滚动到页面底部一定偏移量时加载下一页数据(结合offset: '100%');
  4. 吸顶 / 吸底触发:滚动到指定位置时触发吸顶组件显示 / 隐藏;
  5. 阅读进度标记:水平滚动时元素进入视口标记阅读进度。

Quasar v-touch-hold 指令核心用法总结

v-touch-hold 是 Quasar 提供的触摸 / 鼠标长按触发指令,支持监听元素的长按事件(同时适配触摸和鼠标操作),可自定义长按等待时间、操作灵敏度,适用于长按删除、长按预览、长按触发菜单等场景。

一、核心配置(API)

1. 指令参数格式(通过冒号分隔)

支持配置长按等待时间、触摸灵敏度、鼠标灵敏度,参数顺序固定:

v-touch-hold:[等待时间]:[触摸灵敏度]:[鼠标灵敏度]="回调函数"
参数位置类型默认值说明
等待时间Number600触发长按所需的最小时间(毫秒),如2000表示长按 2 秒触发
触摸灵敏度Number5触摸时允许的最大移动距离(像素),超过则中止长按
鼠标灵敏度Number7鼠标点击时允许的最大移动距离(像素),超过则中止长按

2. 指令修饰符

修饰符作用示例
.mouse启用鼠标事件监听(默认仅支持触摸事件)v-touch-hold.mouse="handleHold"
.capture优先捕获事件(内部元素调用stopPropagation仍会触发指令)v-touch-hold.capture="handleHold"
.mouseCapture优先捕获鼠标事件v-touch-hold.mouse.mouseCapture="handleHold"

3. 回调函数参数

(details) => { // details:长按事件详情对象 // - evt:原生事件对象(touchstart/mousedown) // - touch:布尔值,是否为触摸事件触发 // - mouse:布尔值,是否为鼠标事件触发 // - position:长按位置 { top: 距离顶部像素, left: 距离左侧像素 } // - duration:实际长按时长(毫秒) }

二、基础用法规则

1. 最简用法(默认配置)

<!-- 默认长按600ms触发,仅支持触摸事件 --> <div v-touch-hold="handleHold" class="p-4 bg-primary text-white"> 触摸长按600ms触发 </div> <script setup> const handleHold = (details) => { console.log('长按触发,位置:', details.position) console.log('是否触摸触发:', details.touch) } </script>

2. 自定义等待时间

<!-- 长按2秒(2000ms)触发 --> <div v-touch-hold:2000="handleLongHold" class="p-4 bg-purple text-white"> 长按2秒触发 </div>

3. 自定义灵敏度

<!-- 等待600ms + 触摸灵敏度12px + 鼠标灵敏度15px + 启用鼠标监听 --> <div v-touch-hold:600:12:15.mouse="handleCustomSensitivity" class="p-4 bg-grey-2"> 触摸灵敏度12px,鼠标灵敏度15px </div>

4. 禁用局部区域的长按

通过stop阻止事件传播,禁用元素内部指定区域的长按触发:

<div v-touch-hold.mouse="handleHold" class="p-4 bg-primary text-white"> 外部区域支持长按 <!-- 内部区域禁用长按 --> <div @touchstart.stop @mousedown.stop class="mt-2 p-2 bg-white text-black"> 此区域禁用长按 </div> </div>

三、关键特性与注意事项

1. 核心特性

  • 跨端适配:默认支持触摸事件,启用.mouse修饰符后支持鼠标事件,适配移动端和桌面端;
  • 灵活配置:可自定义长按时长、操作灵敏度,适配不同业务场景(如敏感操作需更长长按时间);
  • 容错机制:允许轻微移动(基于灵敏度配置),避免误触导致长按中止,提升用户体验;
  • 事件控制:支持通过stopPropagation禁用局部区域长按,或通过capture修饰符优先捕获事件。

2. 注意事项

  • 修饰符响应式:部分修饰符(如.capture)非响应式,修改后需刷新组件 / 页面才能生效;
  • 灵敏度理解:灵敏度值越大,允许的移动距离越大,长按越容易触发;值越小,长按要求越严格(需保持静止);
  • 事件冲突:若元素同时绑定touchstart/mousedown事件,需避免与长按指令冲突,可通过capture控制事件优先级;
  • 性能优化:避免在长按回调中执行复杂逻辑,可通过防抖 / 节流优化(如数据请求、DOM 操作)。

四、适用场景

  1. 移动端长按操作(如长按删除列表项、长按显示菜单);
  2. 桌面端鼠标长按触发(如长按预览图片、长按拖拽);
  3. 敏感操作确认(如长按提交表单、长按删除重要数据,通过长按时长防止误操作);
  4. 自定义交互(如长按显示详情弹窗、长按触发动画效果)。

Quasar v-touch-pan 指令核心用法总结

v-touch-pan 是 Quasar 提供的触摸 / 鼠标平移(拖拽滑动)指令,同时适配触摸事件和鼠标事件,支持监听元素在任意方向的平移动作,可用于实现拖拽、滑动控制、手势交互等功能,无需依赖第三方手势库(如 Hammer.js)。

一、核心配置(API)

1. 指令值与回调参数

指令值为平移动作触发的回调函数,参数为包含平移详情的对象:

(details) => { // details 核心属性: // - evt: 原生事件对象(touchmove/mousemove) // - touch/mouse: 布尔值,标记是触摸/鼠标触发 // - position: 当前位置 { top: 距离顶部像素, left: 距离左侧像素 } // - direction: 平移方向(up/right/down/left) // - isFirst/isFinal: 布尔值,是否为平移开始/结束的首次/末次回调 // - duration: 平移时长(毫秒) // - distance: 累计平移绝对距离 { x: 水平像素, y: 垂直像素 } // - offset: 累计平移偏移量(含方向,x/y 可正负) // - delta: 本次回调与上次的偏移差 { x: 水平像素, y: 垂直像素 } }

2. 核心修饰符(13 个,常用关键修饰符)

修饰符作用示例
.mouse启用鼠标事件监听(默认仅支持触摸事件)v-touch-pan.mouse="handlePan"
.prevent阻止页面滚动(触摸设备上防止平移时页面滚动)v-touch-pan.prevent="handlePan"
.horizontal仅监听水平方向平移(left/right)v-touch-pan.horizontal="handlePan"
.vertical仅监听垂直方向平移(up/down)v-touch-pan.vertical="handlePan"
.up/.down/.left/.right仅监听指定单个 / 多个方向v-touch-pan.up.right="handlePan"(仅上 / 右方向)
.capture事件捕获模式(优先触发指令,再触发内部元素事件)v-touch-pan.capture="handlePan"
.mouseCapture鼠标事件捕获模式v-touch-pan.mouse.mouseCapture="handlePan"

二、基础用法规则

1. 最简用法(任意方向平移)

<!-- 支持触摸+鼠标平移,阻止页面滚动 --> <div v-touch-pan.prevent.mouse="handlePan" class="w-400 h-400 bg-primary text-white"> 任意方向平移(触摸/鼠标均可) </div> <script setup> const handlePan = (details) => { console.log('平移方向:', details.direction) console.log('累计水平偏移:', details.offset.x) console.log('是否平移结束:', details.isFinal) } </script>

2. 限制平移方向

<!-- 仅水平方向平移 --> <div v-touch-pan.horizontal.prevent.mouse="handleHorizontalPan" class="w-400 h-200 bg-grey-2"> 仅左右平移 </div> <!-- 仅上/右方向平移 --> <div v-touch-pan.up.right.prevent="handleCustomDirPan" class="w-400 h-200 bg-purple text-white"> 仅上/右平移 </div>

3. 禁用局部区域平移

通过stop阻止事件传播,禁用元素内部指定区域的平移触发:

<div v-touch-pan.prevent.mouse="handlePan" class="w-400 h-400 bg-primary text-white"> 外部支持平移 <div @touchstart.stop @mousedown.stop class="mt-4 p-4 bg-white text-black"> 此区域禁用平移 </div> </div>

4. 拖拽元素示例(核心实战)

实现元素跟随平移动作拖拽移动:

<template> <div class="relative w-full h-600"> <div ref="dragEl" v-touch-pan.prevent.mouse="moveDragEl" class="absolute w-20 h-20 bg-accent text-white flex items-center justify-center" :style="{ top: `${elTop}px`, left: `${elLeft}px` }" > 拖拽我 </div> </div> </template> <script setup> import { ref } from 'vue' const dragEl = ref(null) const elTop = ref(100) // 初始top位置 const elLeft = ref(100) // 初始left位置 let startPos = null // 平移开始时的元素位置 const moveDragEl = (details) => { if (details.isFirst) { // 记录平移开始时的元素位置 startPos = { top: elTop.value, left: elLeft.value } } else if (!details.isFinal) { // 平移中:更新元素位置(初始位置 + 平移偏移量) elTop.value = startPos.top + details.offset.y elLeft.value = startPos.left + details.offset.x } } </script>

三、关键特性与注意事项

1. 核心特性

  • 跨端适配:默认支持触摸事件,启用.mouse修饰符后支持鼠标拖拽,适配移动端和桌面端;
  • 方向精准控制:支持任意 / 水平 / 垂直 / 自定义方向监听,满足不同交互需求;
  • 事件细节丰富:回调提供平移方向、偏移量、时长等完整信息,便于精准控制;
  • 滚动冲突处理:通过.prevent修饰符阻止页面滚动,避免平移与滚动冲突;
  • 灵活的事件控制:支持捕获模式、局部禁用,适配复杂 DOM 结构。

2. 注意事项

  • 图像拖拽干扰:若平移元素包含图片,需给图片添加draggable="false",避免浏览器原生拖拽行为干扰;
  • 修饰符响应式:部分修饰符(如.capture)非响应式,修改后需刷新组件 / 页面生效;
  • 性能优化:平移回调触发频率高,避免在回调中执行复杂 DOM 操作,可通过节流优化;
  • 边界控制:拖拽元素时需手动处理边界(如防止元素超出容器),指令不自带边界限制;
  • 事件优先级:若元素同时绑定touchmove/mousemove事件,可通过.capture修饰符确保指令优先触发。

四、适用场景

  1. 拖拽交互(如可拖动的悬浮按钮、拖拽排序、拖拽调整位置);
  2. 滑动控制(如滑动切换图片、滑动调节音量 / 亮度);
  3. 手势操作(如水平滑动删除列表项、垂直滑动刷新);
  4. 自定义组件交互(如可拖拽的面板、拖拽调整大小的容器)。

Quasar v-touch-repeat 指令核心用法总结

v-touch-repeat 是 Quasar 提供的触摸 / 鼠标长按重复触发指令,核心功能是:长按元素时持续触发回调(区别于 v-touch-hold 的 “单次触发”),支持自定义触发间隔、长按等待时间、操作灵敏度,适配触摸和鼠标操作,适用于长按连续操作(如长按增减数量、长按滚动、长按删除等场景)。

一、核心配置(API)

1. 指令参数格式(通过冒号分隔)

参数顺序固定,支持配置 “长按等待时间”“触发间隔时间”“触摸灵敏度”“鼠标灵敏度”:

v-touch-repeat:[长按等待时间]:[触发间隔时间]:[触摸灵敏度]:[鼠标灵敏度]="回调函数"
参数位置类型默认值说明
长按等待时间Number600首次触发回调前的等待时间(毫秒),即长按多久后开始持续触发
触发间隔时间Number100连续触发回调的时间间隔(毫秒),值越小触发越频繁
触摸灵敏度Number5触摸时允许的最大移动距离(像素),超过则中止长按重复触发
鼠标灵敏度Number7鼠标点击时允许的最大移动距离(像素),超过则中止长按重复触发

2. 指令修饰符

修饰符作用示例
.mouse启用鼠标事件监听(默认仅支持触摸事件)v-touch-repeat.mouse="handleRepeat"
.capture事件捕获模式(优先触发指令,再触发内部元素事件)v-touch-repeat.capture="handleRepeat"
.mouseCapture鼠标事件捕获模式v-touch-repeat.mouse.mouseCapture="handleRepeat"
.prevent阻止页面滚动(触摸设备上避免长按重复时页面滚动)v-touch-repeat.prevent="handleRepeat"

3. 回调函数参数

(details) => { // details:长按重复事件详情对象 // - evt:原生事件对象(touchstart/mousedown/touchmove/mousemove) // - touch/mouse:布尔值,标记是触摸/鼠标触发 // - position:当前触摸/鼠标位置 { top: 距离顶部像素, left: 距离左侧像素 } // - duration:累计长按时长(毫秒) // - count:已触发回调的次数(从1开始累加) // - isFirst:布尔值,是否为首次触发回调(长按等待时间结束后第一次触发) }

二、基础用法规则

1. 最简用法(默认配置)

<!-- 默认:长按600ms后开始,每100ms触发一次,仅支持触摸 --> <div v-touch-repeat="handleRepeat" class="p-4 bg-primary text-white"> 触摸长按持续触发 </div> <script setup> const handleRepeat = (details) => { console.log('触发次数:', details.count) console.log('累计时长:', details.duration) // 执行连续操作(如数量+1、滚动偏移等) } </script>

2. 自定义等待时间与触发间隔

<!-- 长按300ms后开始,每50ms触发一次(触发更频繁),启用鼠标支持 --> <div v-touch-repeat:300:50.mouse="handleFastRepeat" class="p-4 bg-purple text-white"> 鼠标/触摸长按,快速连续触发 </div>

3. 结合业务场景(长按增减数量)

<template> <div class="flex items-center gap-4"> <q-btn v-touch-repeat:200:80.mouse.prevent @click="count = Math.max(0, count - 1)" @touch-repeat="() => count = Math.max(0, count - 1)" > - </q-btn> <span>{{ count }}</span> <q-btn v-touch-repeat:200:80.mouse.prevent @click="count += 1" @touch-repeat="() => count += 1" > + </q-btn> </div> </template> <script setup> import { ref } from 'vue' const count = ref(0) </script>

4. 禁用局部区域触发

通过stop阻止事件传播,禁用元素内部指定区域的长按重复触发:

<div v-touch-repeat.mouse.prevent="handleRepeat" class="p-4 bg-primary text-white"> 外部区域支持长按重复触发 <div @touchstart.stop @mousedown.stop class="mt-2 p-2 bg-white text-black"> 此区域禁用长按重复 </div> </div>

三、关键特性与注意事项

1. 核心特性

  • 持续触发区别:与 v-touch-hold 不同,该指令在长按等待时间后持续触发回调,而非仅触发一次;
  • 跨端适配:默认支持触摸事件,启用.mouse修饰符后适配桌面端鼠标长按;
  • 灵活配置:可自定义 “启动等待时间”(避免误触)和 “触发间隔”(控制触发频率);
  • 容错机制:支持灵敏度配置,允许轻微移动,提升用户体验;
  • 事件控制:支持prevent阻止滚动冲突,capture控制事件优先级。

2. 注意事项

  • 参数顺序:指令参数需严格遵循 “等待时间:间隔时间:触摸灵敏度:鼠标灵敏度” 顺序,缺省参数需保留冒号(如仅自定义等待时间:v-touch-repeat:500:::"handleRepeat");
  • 灵敏度配置:灵敏度值越小,长按要求越严格(需保持静止);值越大,允许的移动范围越大,避免误中止;
  • 性能优化:触发间隔过小时(如<50ms)会导致回调高频执行,避免在回调中执行复杂 DOM 操作或数据请求,可通过节流辅助优化;
  • 与 v-touch-hold 区分
    • v-touch-repeat:长按后持续触发(如长按 “+” 号连续加 1);
    • v-touch-hold:长按后仅触发一次(如长按弹出菜单);
  • 修饰符响应式capture/mouseCapture等修饰符非响应式,修改后需刷新组件 / 页面生效。

四、适用场景

  1. 数值连续调整(如长按增减商品数量、音量 / 亮度调节);
  2. 连续滚动控制(如长按滚动列表、长按滑动图片);
  3. 连续删除操作(如长按连续删除列表项、清空内容);
  4. 连续触发动作(如长按连续发送消息、连续生成内容)。

Quasar v-touch-swipe 指令核心用法总结

v-touch-swipe 是 Quasar 提供的触摸 / 鼠标滑动指令,专为 “快速滑动” 交互设计(区别于 v-touch-pan 的 “平移拖拽”),同时适配触摸和鼠标操作,支持监听指定方向的滑动动作,无需依赖第三方手势库,适用于滑动切换、滑动删除、滑动刷新等场景。

一、核心配置(API)

1. 指令值与回调参数

指令值为滑动动作触发的回调函数,参数为包含滑动详情的对象:

(details) => { // details 核心属性: // - evt: 原生事件对象(touchmove/mousemove) // - touch/mouse: 布尔值,标记是触摸/鼠标触发 // - direction: 滑动方向(up/right/down/left,唯一值) // - duration: 滑动总时长(毫秒,快速滑动通常耗时较短) // - distance: 滑动绝对距离 { x: 水平像素, y: 垂直像素 } }

2. 核心修饰符

修饰符作用示例
.mouse启用鼠标事件监听(默认仅支持触摸事件,鼠标需快速滑动触发)v-touch-swipe.mouse="handleSwipe"
.up/.down/.left/.right仅监听指定单个 / 多个方向的滑动v-touch-swipe.right="handleRightSwipe"(仅右滑)、v-touch-swipe.up.left="handleSwipe"(仅上滑 / 左滑)
.capture事件捕获模式(优先触发指令,再触发内部元素事件)v-touch-swipe.capture="handleSwipe"
.mouseCapture鼠标事件捕获模式v-touch-swipe.mouse.mouseCapture="handleSwipe"

二、基础用法规则

1. 最简用法(任意方向滑动)

<!-- 支持触摸+鼠标任意方向滑动 --> <div v-touch-swipe.mouse="handleSwipe" class="w-400 h-400 bg-primary text-white"> 任意方向快速滑动(触摸/鼠标均可) </div> <script setup> const handleSwipe = (details) => { console.log('滑动方向:', details.direction) console.log('滑动距离:', details.distance.x, details.distance.y) // 执行滑动切换/删除等逻辑 } </script>

2. 限制滑动方向

<!-- 仅监听右滑 --> <div v-touch-swipe.right.mouse="handleRightSwipe" class="w-400 h-200 bg-grey-2"> 仅右滑触发 </div> <!-- 仅监听上滑/左滑 --> <div v-touch-swipe.up.left.mouse="handleCustomDirSwipe" class="w-400 h-200 bg-purple text-white"> 仅上滑/左滑触发 </div>

3. 禁用局部区域滑动

通过stop阻止事件传播,禁用元素内部指定区域的滑动触发:

<div v-touch-swipe.mouse="handleSwipe" class="w-400 h-400 bg-primary text-white"> 外部支持滑动 <div @touchstart.stop @mousedown.stop class="mt-4 p-4 bg-white text-black"> 此区域禁用滑动 </div> </div>

三、关键特性与注意事项

1. 核心特性

  • 滑动判定:强调 “快速滑动”,区别于 v-touch-pan 的 “缓慢平移”,触发条件更侧重滑动速度;
  • 跨端适配:默认支持触摸滑动,启用.mouse修饰符后适配桌面端鼠标快速滑动;
  • 方向精准控制:支持单个 / 多个方向过滤,仅响应指定方向的滑动动作;
  • 事件细节完整:回调提供滑动方向、时长、距离等信息,便于业务逻辑判断(如滑动距离达标才触发切换);
  • 无滚动冲突默认:不主动阻止页面滚动,需手动处理复杂场景的滚动冲突。

2. 注意事项

  • 图像干扰:滑动元素包含图片时,需给图片添加draggable="false",避免浏览器原生拖拽行为干扰;
  • 修饰符响应式capture/mouse/mouseCapture修饰符在 HMR 更新时不生效,需刷新组件 / 页面;
  • 与 v-touch-pan 区分
    • v-touch-swipe:针对 “快速滑动”,触发一次回调,适用于切换 / 删除等一次性动作;
    • v-touch-pan:针对 “缓慢平移拖拽”,持续触发回调,适用于拖拽移动 / 实时控制;
  • 鼠标触发条件:鼠标需快速、连续滑动才能触发,缓慢移动不会触发(符合 “滑动” 交互直觉)。

四、适用场景

  1. 滑动切换(如图片轮播、页面切换、标签页切换);
  2. 滑动操作(如列表项左滑删除、右滑收藏);
  3. 滑动刷新 / 加载(如下拉刷新、上拉加载更多);
  4. 手势控制(如滑动调节音量、滑动关闭弹窗)。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/29 2:01:40

20、Linux 恶意 ELF 文件分析全攻略

Linux 恶意 ELF 文件分析全攻略 1. 嵌入式工件提取再探 在成功执行恶意代码样本、进行进程内存轨迹分析或从物理内存中提取可执行文件后,需要重新检查样本以查找嵌入式工件。重新审视未混淆的程序,查看字符串、符号信息、文件元数据和 ELF 结构细节。通过比较文件执行前后的…

作者头像 李华
网站建设 2026/3/25 13:13:58

地理坐标计算神器:Geodesy库的完整使用指南

地理坐标计算神器&#xff1a;Geodesy库的完整使用指南 【免费下载链接】geodesy Libraries of geodesy functions implemented in JavaScript 项目地址: https://gitcode.com/gh_mirrors/ge/geodesy 想要快速实现精准的地理位置计算&#xff1f;Geodesy库为你提供了完整…

作者头像 李华
网站建设 2026/3/27 17:46:03

深度学习理论推导--二分类逻辑回归

文章目录前言二分类问题多元线性函数σ\sigmaσ 函数输出函数似然函数极大似然估计梯度下降法函数准备求偏导损失函数梯度更新python 实战LogisticRegression训练及结果运行结果总结当你迷茫的时候&#xff0c;请回头看看 目录大纲&#xff0c;也许有你意想不到的收获 前言 前…

作者头像 李华
网站建设 2026/3/24 8:18:02

微博超话自动签到神器:告别繁琐签到,享受智能追星新体验

微博超话自动签到神器&#xff1a;告别繁琐签到&#xff0c;享受智能追星新体验 【免费下载链接】weibo_supertopic_sign 基于Python/Nodejs的微博超话签到脚本&#xff0c;支持云函数运行或青龙面板运行 项目地址: https://gitcode.com/gh_mirrors/we/weibo_supertopic_sign…

作者头像 李华
网站建设 2026/3/29 8:46:16

25、系统日志管理与大文件处理全攻略

系统日志管理与大文件处理全攻略 1. 系统日志概述 在系统运行过程中,即使是使用频率较低的系统,在启动和关闭期间也会生成数千行日志文件,而繁忙的应用程序每天轻松就能产生数百万行日志。日志文件往往冗长且枯燥,因此我们通常会借助软件智能过滤出紧急条目,如即将发生故…

作者头像 李华
网站建设 2026/3/27 11:15:47

26、系统监控:日志文件处理与入侵检测

系统监控:日志文件处理与入侵检测 1. 日志文件搜索与分析 在系统管理中,日志文件是发现问题和监控系统状态的重要资源。如果你想确保搜索日志时有结果,可以使用 logger 程序手动生成日志条目,例如: logger "Authentication failure"也可以通过登录用户账户…

作者头像 李华