Quasar Date Utils(日期工具)核心总结
一、核心定位
Quasar Date Utils 是一套轻量级日期处理工具集,无需依赖 Moment.js 等第三方库,封装了日期格式化、创建、验证、加减、比较等常用操作,支持本地时间与 UTC 时间切换,适配 Quasar 跨平台项目,且支持 Tree Shaking 按需引入,减少包体积。
二、核心特性
- 输入灵活:支持 Unix 时间戳、日期字符串(原生 JS Date 可解析格式)、Date 对象作为输入参数;
- 输出统一:所有操作返回原生 JS Date 对象(格式化方法返回字符串),兼容性强;
- 按需引入:支持 ES6 解构导入单个方法,实现 Tree Shaking,仅打包使用的功能;
- 国际化支持:格式化日期时可自定义星期、月份的多语言文本,适配 i18n 场景;
- 轻量高效:无需额外依赖,比 Moment.js 体积更小,性能更优。
三、核心功能与用法
1. 日期格式化(formatDate)
作用
将日期(时间戳 / 字符串 / Date 对象)按指定格式转换为字符串。
基础用法
import { date } from 'quasar'
const { formatDate } = date
// 格式化当前时间为 "YYYY-MM-DD HH:mm:ss"
const now = Date.now()
const formatted = formatDate(now, 'YYYY-MM-DD HH:mm:ss')
关键格式标记(常用)
| 标记 | 说明 | 示例 |
|---|---|---|
| YYYY | 4 位年份 | 2025 |
| MM | 2 位月份(补零) | 03 |
| DD | 2 位日期(补零) | 15 |
| HH | 24 小时制(补零) | 18 |
| mm | 分钟(补零) | 05 |
| ss | 秒(补零) | 30 |
| dddd | 完整星期名 | Sunday |
| MMMM | 完整月份名 | March |
国际化配置
// 自定义罗马尼亚语月份/星期
const roFormatted = formatDate(now, 'MMMM - dddd', {
months: ['Ianuarie', 'Februarie', /* 其余月份 */],
days: ['Duminica', 'Luni', /* 其余星期 */]
})
2. 日期创建与验证
(1)创建日期(buildDate)
按指定年 / 月 / 日 / 时 / 分 / 秒创建 Date 对象,支持 UTC 时间:
import { date } from 'quasar'
const { buildDate } = date
// 创建本地时间:2025-03-15 14:30:00
const customDate = buildDate({ year: 2025, month: 2, day: 15, hours: 14, minutes: 30 })
// 创建 UTC 时间(第二个参数为 true)
const utcDate = buildDate({ year: 2025, month: 2, day: 15 }, true)
(2)验证日期(isValid)
检查日期字符串 / 对象是否有效:
const isValid = date.isValid('2025-02-30') // false(2月无30日)
3. 日期操作(加减 / 调整)
(1)日期加减(addToDate /subtractFromDate)
对日期添加 / 减去指定时间单位(年 / 月 / 日 / 时等):
const { addToDate, subtractFromDate } = date
const baseDate = new Date(2025, 2, 15)
// 加 7 天 + 1 个月
const laterDate = addToDate(baseDate, { days: 7, months: 1 })
// 减 24 小时 + 10 秒
const earlierDate = subtractFromDate(baseDate, { hours: 24, seconds: 10 })
(2)调整日期(adjustDate)
直接设置日期的指定单位(覆盖原有值):
const { adjustDate } = date
const baseDate = new Date(2025, 2, 15)
// 将年份改为 2024,月份改为 5(6月)
const adjustedDate = adjustDate(baseDate, { year: 2024, month: 5 })
4. 日期查询与比较
(1)最小 / 最大日期(getMinDate /getMaxDate)
从多个日期中获取最小 / 最大值(返回时间戳):
const { getMinDate, getMaxDate } = date
const dates = [new Date(2025, 2, 15), new Date(2025, 2, 20), new Date(2025, 2, 10)]
const minDate = getMinDate(dates) // 2025-03-10 时间戳
const maxDate = getMaxDate(dates) // 2025-03-20 时间戳
(2)日期区间判断(isBetweenDates)
检查日期是否在指定区间内,支持包含边界、仅比较日期(忽略时间):
const { isBetweenDates } = date
const target = new Date(2025, 2, 15)
const from = new Date(2025, 2, 10)
const to = new Date(2025, 2, 20)
// 严格区间(不包含 from 和 to)
const isBetween = isBetweenDates(target, from, to)
// 包含边界 + 仅比较日期(忽略时分秒)
const isBetweenWithOpts = isBetweenDates(target, from, to, {
inclusiveFrom: true,
inclusiveTo: true,
onlyDate: true
})
(3)日期差异(getDateDiff)
计算两个日期之间的差值,指定单位(年 / 月 / 日 / 时等):
const { getDateDiff } = date
const date1 = new Date(2025, 2, 15)
const date2 = new Date(2025, 1, 10)
// 计算相差天数(默认单位为 days)
const diffDays = getDateDiff(date1, date2)
// 计算相差月份
const diffMonths = getDateDiff(date1, date2, 'months')
(4)日期相等判断(isSameDate)
判断两个日期在指定单位上是否相等(如仅比较年份、月份):
const { isSameDate } = date
const date1 = new Date(2025, 2, 15)
const date2 = new Date(2025, 3, 20)
// 仅比较年份(返回 true)
const isSameYear = isSameDate(date1, date2, 'year')
// 完整日期比较(返回 false)
const isSameFull = isSameDate(date1, date2)
5. 日历相关工具
- 获取年的第几周(getWeekOfYear):
getWeekOfYear(new Date(2025, 0, 4)) // 1 - 获取年的第几天(getDayOfYear):
getDayOfYear(new Date(2025, 1, 4)) // 35 - 获取当月天数(daysInMonth):
daysInMonth(new Date(2025, 1)) // 28(2025年2月)
6. 日期首尾时间(startOfDate /endOfDate)
设置日期为指定单位的开始 / 结束时间:
const { startOfDate, endOfDate } = date
const baseDate = new Date(2025, 2, 15, 14, 30)
// 当月开始(2025-03-01 00:00:00)
const monthStart = startOfDate(baseDate, 'month')
// 当天结束(2025-03-15 23:59:59.999)
const dayEnd = endOfDate(baseDate, 'day')
7. 其他工具
- 克隆日期(clone):创建日期副本,避免修改原对象;
- 提取日期(extractDate):按指定格式解析字符串为 Date 对象(支持自定义格式);
- 推断日期格式(inferDateFormat):判断输入类型(date/number/string)。
四、关键注意事项
- 月份索引:JS 原生 Date 月份为 0-11(0=1 月,11=12 月),Quasar 工具遵循此规则;
- UTC 支持:buildDate、adjustDate 等方法的第二个参数为 true 时,按 UTC 时间处理;
- 格式转义:格式化时需保留
[]等字符,需用[]包裹(如[今天] YYYY-MM-DD); - Tree Shaking:按需导入单个方法(如
import { addToDate } from 'quasar/dist/quasar.esm.js'),减少打包体积; - 兼容性:支持所有现代浏览器,无需额外 polyfill。
五、优势对比
相比 Moment.js,Quasar Date Utils 更轻量(无额外依赖)、支持 Tree Shaking,且与 Quasar 生态无缝集成,适合 Quasar 项目中替代第三方日期库,满足大部分日常日期处理需求。
Quasar Color Utils(颜色工具)核心总结
一、核心定位
Quasar Color Utils 是一套轻量级颜色处理工具集,专为前端颜色操作设计,无需额外依赖,支持颜色格式转换、色值计算、对比度校验、主题色生成等核心功能,完美适配 Quasar 内置的调色板体系,可快速实现动态换肤、颜色适配等场景。
二、核心特性
- 格式兼容:支持 Hex(
#fff/#ffffff)、RGB/RGBA(rgb(255,255,255))、HSL/HSLA、Quasar 调色板名称(如primary/purple-6)等多种颜色输入格式; - 功能全面:覆盖颜色解析、转换、明暗调整、透明度修改、对比度计算、主题色生成等高频需求;
- 适配 Quasar 生态:可直接解析 Quasar 内置调色板颜色,与 Quasar 组件样式无缝联动;
- 轻量高效:纯 JS 实现,无第三方依赖,支持 Tree Shaking 按需引入。
三、核心功能与用法
1. 颜色解析与格式转换
(1)解析颜色(parseCssColor)
将任意合法颜色值解析为标准化的 RGBA 对象,是所有颜色操作的基础:
import { color } from 'quasar'
const { parseCssColor } = color
// 解析 Hex 颜色
const hexParsed = parseCssColor('#ff0000')
// 返回:{ r: 255, g: 0, b: 0, a: 1, isValid: true }
// 解析 Quasar 调色板颜色(需确保项目已配置该调色板)
const quasarColorParsed = parseCssColor('primary')
// 返回对应 primary 色值的 RGBA 对象
// 解析无效颜色
const invalidParsed = parseCssColor('invalid-color')
// 返回:{ isValid: false }
(2)格式转换(rgbToHex /hexToRgb/rgbToHsl 等)
实现不同颜色格式的互转:
const { rgbToHex, hexToRgb, rgbToHsl } = color
// RGB 转 Hex(支持透明度)
const hex = rgbToHex(255, 0, 0, 0.5) // 返回 "#ff000080"
// Hex 转 RGB
const rgb = hexToRgb('#ff0000') // 返回 "rgb(255,0,0)"
// RGB 转 HSL
const hsl = rgbToHsl(255, 0, 0) // 返回 "hsl(0,100%,50%)"
2. 颜色调整(明暗 / 透明度)
(1)调整明暗度(lighten /darken)
按百分比提亮 / 加深颜色,支持所有合法颜色输入:
const { lighten, darken } = color
// 提亮 20%(输入 Hex 颜色)
const lighterRed = lighten('#ff0000', 20) // 返回提亮后的 Hex 色值
// 加深 30%(输入 Quasar 调色板颜色)
const darkerPrimary = darken('primary', 30) // 返回加深后的 Hex 色值
(2)修改透明度(adjustAlpha)
为颜色添加 / 修改透明度,返回 RGBA/Hex8 格式:
const { adjustAlpha } = color
// 为红色设置 50% 透明度
const semiRed = adjustAlpha('#ff0000', 0.5)
// 返回:"rgba(255,0,0,0.5)"
// 为 Quasar 主题色设置 80% 透明度
const semiPrimary = adjustAlpha('primary', 0.8)
3. 对比度与可读性校验
(1)计算对比度(getContrastRatio)
计算两个颜色的对比度(符合 WCAG 标准),用于校验文字与背景的可读性:
const { getContrastRatio } = color
// 计算白色文字在红色背景上的对比度
const ratio = getContrastRatio('#ffffff', '#ff0000') // 返回 ~4.0(WCAG 最低合格值为 4.5)
// 校验是否满足 WCAG 可访问性标准
const isReadable = ratio >= 4.5 // true/false
(2)自动生成对比色(getReadableColor)
根据背景色自动生成最可读的前景色(黑 / 白),适配动态背景场景:
const { getReadableColor } = color
// 红色背景自动生成白色文字
const textColor = getReadableColor('#ff0000') // 返回 "#ffffff"
// 浅灰色背景自动生成黑色文字
const textColor2 = getReadableColor('#f5f5f5') // 返回 "#000000"
4. 主题色生成
(1)生成互补色(getComplementaryColor)
获取颜色的互补色(色轮上对立的颜色):
const { getComplementaryColor } = color
const complementary = getComplementaryColor('#ff0000') // 红色的互补色为青色,返回 "#00ffff"
(2)生成调色板(generatePalette)
基于基础色生成一套渐变调色板(类似 Quasar 的 *-1 到 *-12 调色板):
const { generatePalette } = color
// 基于 #ff0000 生成 12 级调色板
const palette = generatePalette('#ff0000')
// 返回:{ 1: "#ffebee", 2: "#ffcdd2", ..., 12: "#b71c1c" }
5. 其他实用工具
(1)判断颜色明暗(isDark /isLight)
判断颜色属于深色还是浅色,用于动态调整交互样式:
const { isDark, isLight } = color
const isRedDark = isDark('#ff0000') // false
const isBlackLight = isLight('#000000') // false
(2)混合颜色(mixColors)
按比例混合两个颜色,生成过渡色:
const { mixColors } = color
// 50% 红色 + 50% 蓝色
const mixed = mixColors('#ff0000', '#0000ff', 0.5) // 返回 "#800080"(紫色)
四、典型业务场景示例
场景 1:动态主题色适配
基于用户选择的主色,自动生成配套的深浅色、文字对比色:
import { color } from 'quasar'
const { parseCssColor, lighten, darken, getReadableColor } = color
// 用户选择的主色
const userPrimary = '#2196f3'
// 生成主题配置
const theme = {
primary: userPrimary,
primaryLight: lighten(userPrimary, 20), // 提亮 20% 作为浅色调
primaryDark: darken(userPrimary, 20), // 加深 20% 作为深色调
primaryText: getReadableColor(userPrimary) // 自动生成可读文字色
}
// 应用到 Quasar 全局样式
document.documentElement.style.setProperty('--q-primary', theme.primary)
document.documentElement.style.setProperty('--q-primary-text', theme.primaryText)
场景 2:表单验证颜色适配
根据验证状态(成功 / 失败 / 警告),动态生成渐变背景色:
const { mixColors, adjustAlpha } = color
// 成功状态(绿色渐变)
const successBg = mixColors('#4caf50', '#81c784', 0.5)
// 失败状态(红色半透明)
const errorBg = adjustAlpha('#f44336', 0.1)
// 警告状态(黄色半透明)
const warningBg = adjustAlpha('#ffc107', 0.1)
五、关键注意事项
- Quasar 调色板解析:解析
primary/purple-6等 Quasar 调色板颜色时,需确保项目已在quasar.config.js中配置对应的主题色,否则会解析失败; - 格式规范:Hex 颜色支持 3 位(
#fff)和 6 位(#ffffff),带透明度的 Hex 需 8 位(#ffffff80); - Tree Shaking:按需导入单个方法(如
import { lighten } from 'quasar/dist/quasar.esm.js')可减少打包体积; - 浏览器兼容性:所有方法兼容现代浏览器,无需额外 polyfill;
- 透明度处理:RGBA/HSLA 格式的透明度值范围为 0-1(如 0.5 表示 50% 透明度)。
六、核心优势
相比 chroma-js、tinycolor2 等第三方颜色库,Quasar Color Utils 更轻量(无依赖)、与 Quasar 生态深度融合,能直接复用 Quasar 内置调色板,无需额外适配,适合 Quasar 项目中快速实现颜色相关需求。
总结
- Quasar Color Utils 支持 Hex/RGB/HSL/Quasar 调色板等多格式颜色的解析与转换,是前端颜色操作的轻量解决方案;
- 核心高频功能包括:颜色明暗调整、透明度修改、对比度计算、自动生成可读文字色,可满足动态换肤、样式适配等主流场景;
- 与 Quasar 内置调色板无缝兼容,无需额外配置即可解析
primary/purple-6等主题色,适配性极强。
Quasar DOM Utils(DOM 工具)核心总结
一、核心定位
Quasar DOM Utils 是一套轻量级 DOM 操作工具集,封装了前端高频的 DOM 查找、样式修改、事件处理、元素位置计算等操作,无需依赖 jQuery 等第三方库,适配 Quasar 跨平台(Web / 移动端 / 桌面端)场景,且与 Vue 组件体系无缝兼容。
二、核心特性
- 轻量无依赖:纯原生 JS 实现,替代 jQuery 常用 DOM 操作,减少包体积;
- 跨平台适配:兼容浏览器、Cordova、Electron 等 Quasar 支持的运行环境;
- Vue 友好:可直接配合 Vue 组件的
ref使用,避免手动操作 DOM 引发的响应式问题; - 安全可靠:封装了边界判断、兼容性处理(如不同浏览器的样式前缀);
- 按需引入:支持 Tree Shaking,仅导入使用的方法,进一步优化体积。
三、核心功能与用法
1. DOM 元素查找与判断
(1)元素查找(findDomElement)
兼容多种输入类型(选择器字符串、DOM 元素、Vue ref),返回标准化 DOM 元素:
import { dom } from 'quasar'
const { findDomElement } = dom
// 方式1:选择器字符串
const el1 = findDomElement('#app')
// 方式2:Vue ref(组件内)
const containerRef = ref(null)
const el2 = findDomElement(containerRef)
// 方式3:直接传入 DOM 元素
const el3 = findDomElement(document.body)
// 找不到元素返回 null
const el4 = findDomElement('#non-exist') // null
(2)元素关系判断
isChildOf(el, parentEl):判断元素是否为另一个元素的子节点;isInDocument(el):判断元素是否在文档流中(未被移除);
const { isChildOf, isInDocument } = dom
const child = document.querySelector('.child')
const parent = document.querySelector('.parent')
console.log(isChildOf(child, parent)) // true/false
console.log(isInDocument(child)) // true(元素存在)/false(已移除)
2. 元素样式与类名操作
(1)样式操作(getStyle /setStyle/hasStyle)
获取 / 设置 / 判断元素的计算样式,自动处理浏览器前缀:
const { getStyle, setStyle, hasStyle } = dom
const el = document.querySelector('#box')
// 获取计算样式(返回标准化值,如 "10px")
const width = getStyle(el, 'width')
// 设置样式(支持多属性)
setStyle(el, {
width: '200px',
height: '100px',
'background-color': '#ff0000'
})
// 判断样式是否存在(仅判断内联样式)
const hasBg = hasStyle(el, 'background-color') // true/false
(2)类名操作(addClass /removeClass/toggleClass /hasClass)
简化原生 classList 操作,支持批量处理:
const { addClass, removeClass, toggleClass, hasClass } = dom
const el = document.querySelector('#box')
// 添加单个/多个类名
addClass(el, 'active')
addClass(el, ['shadow', 'rounded'])
// 移除类名
removeClass(el, 'active')
// 切换类名(存在则移除,不存在则添加)
toggleClass(el, 'active')
// 判断类名是否存在
const isActive = hasClass(el, 'active') // true/false
3. 元素尺寸与位置计算
(1)尺寸获取(getDimensions)
返回元素的完整尺寸信息(含 padding、border、margin):
const { getDimensions } = dom
const el = document.querySelector('#box')
const dims = getDimensions(el)
/* 返回结构:
{
width: 200, // 内容宽度(px)
height: 100, // 内容高度(px)
innerWidth: 220, // 内容 + padding
innerHeight: 120,
outerWidth: 240, // 内容 + padding + border
outerHeight: 140,
outerWidthWithMargin: 260, // 内容 + padding + border + margin
outerHeightWithMargin: 160
}
*/
(2)位置获取(getOffset)
返回元素相对于文档 / 视口的位置:
const { getOffset } = dom
const el = document.querySelector('#box')
const offset = getOffset(el)
/* 返回结构:
{
top: 100, // 距离文档顶部(px)
left: 50, // 距离文档左侧(px)
bottom: 800, // 距离文档底部(px)
right: 900, // 距离文档右侧(px)
// 视口相对位置
viewportTop: 50,
viewportLeft: 20
}
*/
(3)滚动位置操作(getScrollTarget /getScrollPosition/setScrollPosition)
获取 / 设置元素 / 窗口的滚动位置:
const { getScrollTarget, getScrollPosition, setScrollPosition } = dom
// 获取元素的滚动容器(无则返回 window)
const scrollTarget = getScrollTarget(el)
// 获取滚动位置(返回 { x: 0, y: 0 })
const scrollPos = getScrollPosition(scrollTarget)
// 设置滚动位置(支持平滑滚动)
setScrollPosition(scrollTarget, { x: 0, y: 200 }, { behavior: 'smooth' })
4. 事件处理(addNativeEventListener /removeNativeEventListener)
封装原生事件绑定,自动处理移除逻辑,避免内存泄漏:
const { addNativeEventListener, removeNativeEventListener } = dom
const el = document.querySelector('#btn')
// 绑定事件(支持事件委托)
const handler = (e) => console.log('点击了按钮')
addNativeEventListener(el, 'click', handler)
// 移除事件(必须传入相同的 handler)
removeNativeEventListener(el, 'click', handler)
// 事件委托示例(监听父元素,处理子元素事件)
addNativeEventListener(
document.body,
'click',
(e) => {
if (e.target.matches('.btn')) {
console.log('委托点击')
}
},
{ delegate: true } // 启用委托
)
5. 其他实用工具
(1)元素滚动到可视区域(scrollIntoView)
兼容不同浏览器的 scrollIntoView 实现,支持自定义对齐方式:
const { scrollIntoView } = dom
const el = document.querySelector('#target')
// 滚动到可视区域(默认顶部对齐)
scrollIntoView(el)
// 自定义配置(居中对齐 + 平滑滚动)
scrollIntoView(el, {
behavior: 'smooth',
position: 'center'
})
(2)获取视口尺寸(getViewportDimensions)
返回当前窗口的可视区域尺寸:
const { getViewportDimensions } = dom
const viewport = getViewportDimensions()
// 返回:{ width: 1920, height: 1080 }(当前窗口尺寸)
(3)禁止 / 允许页面滚动(preventBodyScroll /allowBodyScroll)
常用于弹窗显示时禁止背景滚动:
const { preventBodyScroll, allowBodyScroll } = dom
// 显示弹窗时禁止滚动
preventBodyScroll()
// 关闭弹窗时恢复滚动
allowBodyScroll()
(4)判断元素是否在可视区域(isElementInViewport)
用于懒加载、曝光埋点等场景:
const { isElementInViewport } = dom
const el = document.querySelector('#lazy-img')
// 检查元素是否完全/部分在可视区域
const isInView = isElementInViewport(el, {
threshold: 0.5 // 元素至少 50% 可见时返回 true
})
四、Vue 组件中实战示例
场景 1:弹窗显示 / 隐藏时控制页面滚动
<template>
<q-dialog ref="dialogRef" @show="onShow" @hide="onHide">
<!-- 弹窗内容 -->
</q-dialog>
</template>
<script setup>
import { dom } from 'quasar'
const { preventBodyScroll, allowBodyScroll } = dom
// 弹窗显示时禁止页面滚动
const onShow = () => {
preventBodyScroll()
}
// 弹窗隐藏时恢复滚动
const onHide = () => {
allowBodyScroll()
}
</script>
场景 2:懒加载图片(基于可视区域判断)
<template>
<img
ref="imgRef"
:src="isLoaded ? imgUrl : placeholderUrl"
alt="懒加载图片"
/>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { dom } from 'quasar'
const { isElementInViewport, addNativeEventListener, removeNativeEventListener } = dom
const imgRef = ref(null)
const isLoaded = ref(false)
const imgUrl = 'https://example.com/large-img.jpg'
const placeholderUrl = 'https://example.com/placeholder.jpg'
// 检查图片是否进入可视区域
const checkVisibility = () => {
if (isElementInViewport(imgRef.value) && !isLoaded.value) {
isLoaded.value = true
// 移除滚动监听(避免重复触发)
removeNativeEventListener(window, 'scroll', checkVisibility)
}
}
onMounted(() => {
// 初始检查
checkVisibility()
// 绑定滚动监听
addNativeEventListener(window, 'scroll', checkVisibility)
})
onUnmounted(() => {
// 清理事件监听,避免内存泄漏
removeNativeEventListener(window, 'scroll', checkVisibility)
})
</script>
五、关键注意事项
- Vue ref 配合使用:操作 Vue 组件内的 DOM 时,务必通过
ref获取,且确保在onMounted钩子中执行(组件已挂载,DOM 存在); - 事件清理:通过
addNativeEventListener绑定的事件,需在组件卸载(onUnmounted)或元素销毁时调用removeNativeEventListener移除,避免内存泄漏; - 跨平台兼容:在 Electron/Cordova 环境中,部分方法(如
preventBodyScroll)已做适配,无需额外处理; - 样式单位:设置样式时需显式指定单位(如
'200px',而非200),避免解析异常; - 边界判断:使用
findDomElement后建议先判断是否为null,再执行后续操作,避免报错; - Tree Shaking:按需导入方法(如
import { preventBodyScroll } from 'quasar/dist/quasar.esm.js'),减少打包体积。
六、核心优势
相比 jQuery 或原生 DOM 操作,Quasar DOM Utils 的核心优势:
- 无需引入 jQuery,减少第三方依赖;
- 封装了跨平台 / 跨浏览器的兼容性处理,无需手动写兼容代码;
- 与 Vue 组件体系适配,避免直接操作 DOM 引发的响应式问题;
- 提供了前端高频的 DOM 操作封装(如滚动控制、可视区域判断),提升开发效率。
总结
- Quasar DOM Utils 封装了 DOM 查找、样式 / 类名操作、尺寸 / 位置计算、事件处理等高频操作,是 Vue + Quasar 项目中替代 jQuery 的轻量方案;
- 核心高频功能包括:元素查找、滚动控制、可视区域判断、页面滚动禁止 / 恢复,可满足弹窗、懒加载、滚动监听等主流业务场景;
- 在 Vue 组件中使用时,需配合
ref和生命周期钩子,确保 DOM 存在且避免内存泄漏。
Quasar Morph Utils(形态过渡工具)核心总结
一、核心定位
Quasar Morph Utils 是一套专为元素形态过渡动画设计的工具集,封装了元素位置 / 尺寸 / 样式的精准计算与过渡适配逻辑,无需手动编写复杂的 CSS 动画或 JS 计算,可快速实现元素在不同状态(如展开 / 收起、移动、变形)间的平滑过渡,适配 Quasar 组件体系与跨平台场景。
二、核心特性
- 精准计算:自动计算元素在不同状态下的位置、尺寸、偏移量,消除手动计算的误差;
- 无缝过渡:基于原生 CSS 过渡 / 动画,实现元素从一个形态到另一个形态的平滑切换;
- Vue 友好:可直接配合 Vue 组件的
ref和响应式状态使用,适配组件生命周期; - 轻量无依赖:纯 JS + CSS 实现,无需额外动画库(如 GSAP);
- 跨平台兼容:适配浏览器、Cordova、Electron 等 Quasar 支持的运行环境。
三、核心功能与用法
1. 基础形态过渡(morph)
作用
让元素从 “源状态” 平滑过渡到 “目标状态”,自动计算并应用过渡样式(位置、尺寸、透明度等)。
基础用法
import { morph } from 'quasar'
// 1. 定义源元素和目标元素(或目标状态)
const sourceEl = document.querySelector('#source') // 源元素
const targetEl = document.querySelector('#target') // 目标元素
// 2. 执行形态过渡
morph(sourceEl, targetEl, {
duration: 500, // 过渡时长(毫秒)
easing: 'ease-in-out', // 缓动函数
// 需过渡的属性(默认包含所有关键属性)
props: ['x', 'y', 'width', 'height', 'opacity', 'scale']
}).then(() => {
// 过渡完成回调
console.log('形态过渡完成')
})
核心配置项
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
duration | Number | 300 | 过渡时长(ms) |
easing | String | 'ease-out' | 缓动函数(支持 CSS 所有合法值) |
props | Array | ['x','y','width','height','opacity','scale'] | 需要过渡的属性 |
delay | Number | 0 | 过渡延迟(ms) |
keepSource | Boolean | true | 过渡后是否保留源元素(false 则移除) |
keepTarget | Boolean | true | 过渡后是否保留目标元素(false 则移除) |
offset | Object | { x:0, y:0 } | 目标位置的偏移量(px) |
2. 关键过渡属性说明
| 属性 | 作用 |
|---|---|
x/y | 元素的水平 / 垂直位置(基于文档流) |
width/height | 元素的宽高 |
opacity | 元素透明度 |
scale | 元素缩放比例(1 为原尺寸,0.5 为缩小一半) |
rotate | 元素旋转角度(如 90 表示旋转 90 度) |
borderRadius | 元素圆角 |
3. 状态驱动的形态过渡(Vue 组件示例)
场景:按钮点击后展开为卡片



<template>
<div class="demo-container">
<!-- 源元素:按钮 -->
<q-btn
ref="sourceRef"
label="展开卡片"
color="primary"
@click="triggerMorph"
/>
<!-- 目标元素:卡片(初始隐藏) -->
<q-card
ref="targetRef"
class="hidden"
style="position: absolute; width: 300px; height: 200px;"
>
<q-card-section>形态过渡示例卡片</q-card-section>
</q-card>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { morph } from 'quasar'
const sourceRef = ref(null)
const targetRef = ref(null)
let hasMorphed = false
// 触发形态过渡
const triggerMorph = () => {
const sourceEl = sourceRef.value.$el // Quasar 组件需取 $el 获取原生 DOM
const targetEl = targetRef.value.$el
if (!hasMorphed) {
// 从按钮过渡到卡片
morph(sourceEl, targetEl, {
duration: 600,
easing: 'ease-in-out',
props: ['x', 'y', 'width', 'height', 'borderRadius', 'opacity']
}).then(() => {
hasMorphed = true
targetEl.classList.remove('hidden') // 显示卡片
})
} else {
// 从卡片过渡回按钮
morph(targetEl, sourceEl, {
duration: 600,
easing: 'ease-in-out',
props: ['x', 'y', 'width', 'height', 'borderRadius', 'opacity']
}).then(() => {
hasMorphed = false
targetEl.classList.add('hidden') // 隐藏卡片
})
}
}
</script>
<style scoped>
.hidden {
opacity: 0;
pointer-events: none;
}
.demo-container {
position: relative;
min-height: 300px;
}
</style>

<template>
<div class="q-pa-md">
<div
class="fixed-full image-gallery__blinder bg-grey-8"
:class="indexZoomed !== void 0 ? 'image-gallery__blinder--active' : void 0"
@click="zoomImage()"
/>
<div
class="row justify-center q-gutter-sm q-mx-auto scroll relative-position"
style="max-width: 80vw; max-height: 80vh"
>
<q-img
v-for="(src, index) in images"
:key="index"
:ref="el => { thumbRef[index] = el }"
class="image-gallery__image"
:style="index === indexZoomed ? 'opacity: 0.3' : void 0"
:src="src"
@click="zoomImage(index)"
/>
</div>
<q-img
ref="fullRef"
class="image-gallery__image image-gallery__image-full fixed-center"
:class="indexZoomed !== void 0 ? 'image-gallery__image-full--active' : void 0"
:src="images[indexZoomed]"
@load="imgLoadedResolve"
@error="imgLoadedReject"
@click="zoomImage()"
/>
</div>
</template>
<script>
import { ref, onBeforeUpdate } from 'vue'
import { morph } from 'quasar'
export default {
setup () {
const thumbRef = ref([])
const fullRef = ref(null)
const indexZoomed = ref(void 0)
const images = ref(Array(24).fill(null).map((_, i) => 'https://picsum.photos/id/' + i + '/500/300'))
const imgLoaded = {
promise: Promise.resolve(),
resolve: () => {},
reject: () => {}
}
function imgLoadedResolve () {
imgLoaded.resolve()
}
function imgLoadedReject () {
imgLoaded.reject()
}
function zoomImage (index) {
const indexZoomedState = indexZoomed.value
let cancel = void 0
imgLoaded.reject()
const zoom = () => {
if (index !== void 0 && index !== indexZoomedState) {
imgLoaded.promise = new Promise((resolve, reject) => {
imgLoaded.resolve = () => {
imgLoaded.resolve = () => {}
imgLoaded.reject = () => {}
resolve()
}
imgLoaded.reject = () => {
imgLoaded.resolve = () => {}
imgLoaded.reject = () => {}
reject()
}
})
cancel = morph({
from: thumbRef.value[ index ].$el,
to: fullRef.value.$el,
onToggle: () => {
indexZoomed.value = index
},
waitFor: imgLoaded.promise,
duration: 400,
hideFromClone: true,
onEnd: end => {
if (end === 'from' && indexZoomed.value === index) {
indexZoomed.value = void 0
}
}
})
}
}
if (
indexZoomedState !== void 0 &&
(cancel === void 0 || cancel() === false)
) {
morph({
from: fullRef.value.$el,
to: thumbRef.value[ indexZoomedState ].$el,
onToggle: () => {
indexZoomed.value = void 0
},
duration: 200,
keepToClone: true,
onEnd: zoom
})
}
else {
zoom()
}
}
// Make sure to reset the dynamic refs before each update.
onBeforeUpdate(() => {
thumbRef.value = []
})
return {
thumbRef,
fullRef,
indexZoomed,
images,
zoomImage,
imgLoadedResolve,
imgLoadedReject
}
}
}
</script>
<style lang="sass">
.image-gallery
&__image
border-radius: 3%/5%
width: 150px
max-width: 20vw
cursor: pointer
&-full
width: 800px
max-width: 70vw
z-index: 2002
pointer-events: none
&--active
pointer-events: all
&__blinder
opacity: 0
z-index: 2000
pointer-events: none
transition: opacity 0.3s ease-in-out
&--active
opacity: 0.6
pointer-events: all
+ div > .image-gallery__image
z-index: 2001
</style>
4. 自定义过渡状态(无需目标元素)
无需真实目标元素,可直接定义目标状态的属性值:
import { morph } from 'quasar'
const sourceEl = document.querySelector('#box')
// 过渡到自定义状态(无需目标元素)
morph(sourceEl, {
x: 100, // 目标水平位置(px)
y: 200, // 目标垂直位置(px)
width: 200, // 目标宽度(px)
height: 150, // 目标高度(px)
opacity: 0.8,
scale: 1.2,
borderRadius: 16 // 目标圆角(px)
}, {
duration: 800,
easing: 'cubic-bezier(0.4, 0, 0.2, 1)'
})
5. 过渡中断与清理
(1)中断正在进行的过渡
const morphPromise = morph(sourceEl, targetEl, { duration: 1000 })
// 手动中断过渡(如用户点击取消)
setTimeout(() => {
morphPromise.cancel() // 中断过渡
}, 300)
(2)清理过渡残留样式
过渡完成后自动清理临时样式,若手动中断,可调用清理方法:
import { morphCleanup } from 'quasar'
// 清理元素的过渡残留样式
morphCleanup(sourceEl)
四、典型业务场景
场景 1:列表项点击展开为详情页
// 列表项点击事件
const onItemClick = (itemRef, detailRef) => {
const itemEl = itemRef.value.$el
const detailEl = detailRef.value.$el
// 列表项过渡到详情页
morph(itemEl, detailEl, {
duration: 700,
easing: 'ease-in-out',
props: ['x', 'y', 'width', 'height', 'opacity'],
offset: { x: 0, y: 50 } // 详情页位置偏移 50px
})
}
场景 2:购物车图标数量变化过渡
const cartIconEl = document.querySelector('#cart-icon')
const badgeEl = document.querySelector('#cart-badge')
// 数量变化时触发徽章形态过渡
const updateCartCount = (newCount) => {
morph(badgeEl, {
scale: 1.5, // 先放大
opacity: 0.8
}, { duration: 200 })
.then(() => {
// 放大后缩小回原尺寸,更新数量
morph(badgeEl, {
scale: 1,
opacity: 1
}, { duration: 200 })
badgeEl.textContent = newCount
})
}
五、关键注意事项
- 元素定位:参与过渡的元素建议设置
position: absolute/fixed/relative,否则位置计算可能出错; - Quasar 组件 DOM 获取:Quasar 组件(如 QBtn、QCard)需通过
$el获取原生 DOM 元素,不能直接传组件 ref; - 过渡属性兼容:
scale/rotate等变换属性依赖 CSS transform,部分老旧浏览器需注意兼容性; - 性能优化:避免同时对大量元素执行形态过渡,可通过
requestAnimationFrame分批处理; - 样式冲突:过渡过程中会临时覆盖元素的样式,过渡完成后会自动还原,若有自定义样式需注意优先级;
- 生命周期清理:在 Vue 组件卸载时,需中断未完成的过渡并调用
morphCleanup清理样式,避免内存泄漏。
六、核心优势
相比手动编写 CSS 过渡 / 动画,Quasar Morph Utils 的核心优势:
- 自动计算元素状态差异,无需手动编写
from/to关键帧; - 支持 “源元素 → 目标元素”“源元素 → 自定义状态” 两种过渡模式,灵活适配不同场景;
- 与 Quasar 组件体系无缝兼容,可直接操作 Quasar 组件的原生 DOM;
- 提供过渡中断、清理等配套方法,避免样式残留和内存泄漏。
总结
- Quasar Morph Utils 专注于元素形态过渡动画,封装了位置 / 尺寸 / 样式的精准计算,无需手动编写复杂动画逻辑;
- 核心用法是
morph(source, target, options),支持源元素→目标元素、源元素→自定义状态两种过渡模式; - 在 Vue 组件中使用时,需注意获取原生 DOM 元素、组件生命周期清理,避免位置计算错误和内存泄漏。
Quasar Formatter Utils(格式化工具)核心总结
一、核心定位
Quasar Formatter Utils 是一套轻量级数据格式化工具集,专为前端常见的文本、数字、货币、字节、时间等数据格式化场景设计,无需额外依赖,适配 Quasar 跨平台项目,可快速实现标准化的数据展示,减少重复的格式化代码。
二、核心特性
- 场景全覆盖:支持数字、货币、字节、百分比、时间、电话号码、信用卡号等高频格式化需求;
- 本地化适配:支持多语言 / 地区的格式规则(如货币符号、数字千分位、日期格式);
- 可逆操作:部分格式化方法提供 “反格式化” 能力(如将带千分位的数字还原为纯数字);
- 轻量高效:纯 JS 实现,无第三方依赖,支持 Tree Shaking 按需引入;
- Vue 友好:可直接配合 Vue 响应式数据使用,适配组件模板与脚本。
三、核心功能与用法
1. 数字格式化(formatNumber /unformatNumber)
作用
处理数字的千分位、小数位数、符号等,支持反格式化还原纯数字。
import { format } from 'quasar'
const { formatNumber, unformatNumber } = format
// 基础格式化(千分位 + 2 位小数)
const num1 = formatNumber(1234567.891) // "1,234,567.89"
// 自定义配置(无千分位 + 0 位小数 + 前缀)
const num2 = formatNumber(1234.56, {
decimalPlaces: 0, // 小数位数
thousandsSeparator: '', // 千分位分隔符(空则禁用)
prefix: '¥' // 前缀
}) // "¥1235"(自动四舍五入)
// 反格式化(还原纯数字)
const rawNum = unformatNumber('1,234,567.89') // 1234567.89
核心配置项
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
decimalPlaces | Number | 2 | 保留小数位数(负数表示不限制) |
decimalSeparator | String | '.' | 小数分隔符 |
thousandsSeparator | String | ',' | 千分位分隔符 |
prefix | String | '' | 前缀(如货币符号) |
suffix | String | '' | 后缀(如单位) |
roundingMethod | String | 'round' | 舍入方式(round/ceil/floor) |
2. 货币格式化(formatCurrency)
作用
按地区规则格式化货币,支持自定义货币符号、小数位数。
const { formatCurrency } = format
// 美元格式化(默认 USD)
const usd = formatCurrency(1234.56) // "$1,234.56"
// 人民币格式化(自定义符号 + 0 位小数)
const cny = formatCurrency(1234.56, {
currency: 'CNY', // 货币类型
currencyDisplay: 'symbol', // 显示方式(symbol/name/code)
decimalPlaces: 0,
prefix: '¥'
}) // "¥1,235"
// 欧元格式化(欧洲地区规则)
const eur = formatCurrency(1234.56, {
locale: 'de-DE', // 地区(德语-德国)
currency: 'EUR'
}) // "1.234,56 €"
3. 字节格式化(formatBytes)
作用
将字节数转换为易读的单位(B/KB/MB/GB/TB),适配文件大小展示。
const { formatBytes } = format
// 基础格式化(自动适配单位)
const size1 = formatBytes(1024) // "1.00 KB"
const size2 = formatBytes(1500000) // "1.43 MB"
// 自定义配置(固定单位 + 0 位小数)
const size3 = formatBytes(2048, {
unit: 'KB', // 固定单位
decimalPlaces: 0
}) // "2 KB"
4. 百分比格式化(formatPercent)
作用
将小数转换为百分比格式,支持自定义小数位数、符号位置。
const { formatPercent } = format
// 基础格式化(0.123 → 12.30%)
const pct1 = formatPercent(0.123) // "12.30%"
// 自定义配置(1 位小数 + 前缀)
const pct2 = formatPercent(0.4567, {
decimalPlaces: 1,
suffix: '', // 移除后缀
prefix: '% ' // 前缀
}) // "% 45.7"
5. 时间 / 日期格式化(formatDate 补充)
作用
与 Date Utils 配合,提供更简洁的时间差、相对时间格式化。
const { formatDate, formatRelativeDate } = format
// 相对时间格式化(如 "2 小时前")
const relTime = formatRelativeDate(new Date(Date.now() - 2 * 3600 * 1000)) // "2 hours ago"
// 本地化相对时间(中文)
const relTimeCn = formatRelativeDate(new Date(Date.now() - 1 * 86400 * 1000), {
locale: 'zh-CN'
}) // "1 天前"
6. 文本格式化(formatPhone /formatCreditCard/formatZipCode)
(1)电话号码格式化
const { formatPhone } = format
// 美式电话号码
const phone1 = formatPhone('1234567890') // "(123) 456-7890"
// 中式电话号码(自定义格式)
const phone2 = formatPhone('13812345678', {
format: '### #### ####' // 格式模板
}) // "138 1234 5678"
(2)信用卡号格式化
const { formatCreditCard } = format
const cc = formatCreditCard('4111111111111111') // "4111 1111 1111 1111"
7. 反格式化工具(通用)
部分格式化方法提供反向操作,还原原始数据:
// 货币反格式化
const { unformatCurrency } = format
const rawCurrency = unformatCurrency('¥1,234.56') // 1234.56
// 电话号码反格式化
const { unformatPhone } = format
const rawPhone = unformatPhone('(123) 456-7890') // "1234567890"
四、Vue 组件实战示例
场景 1:商品列表价格 / 库存格式化
<template>
<q-list>
<q-item v-for="item in goodsList" :key="item.id">
<q-item-section>
<q-item-label>{{ item.name }}</q-item-label>
<q-item-label caption>
价格:{{ formatCurrency(item.price) }} |
库存:{{ formatNumber(item.stock) }} 件 |
大小:{{ formatBytes(item.size) }}
</q-item-label>
</q-item-section>
</q-item>
</q-list>
</template>
<script setup>
import { ref } from 'vue'
import { format } from 'quasar'
const { formatCurrency, formatNumber, formatBytes } = format
// 模拟商品数据
const goodsList = ref([
{ id: 1, name: '手机', price: 2999.99, stock: 1234, size: 1024 * 1024 * 64 },
{ id: 2, name: '耳机', price: 199.5, stock: 56789, size: 1024 * 1024 * 8 }
])
</script>
场景 2:表单输入格式化(实时格式化)
<template>
<q-input
v-model="phone"
label="手机号"
@input="formatPhoneInput"
/>
</template>
<script setup>
import { ref } from 'vue'
import { format } from 'quasar'
const { formatPhone, unformatPhone } = format
const phone = ref('')
// 实时格式化手机号
const formatPhoneInput = () => {
// 先反格式化(移除所有分隔符),再重新格式化
const raw = unformatPhone(phone.value)
phone.value = formatPhone(raw, { format: '### #### ####' })
}
</script>
五、关键注意事项
- 本地化配置:使用
locale配置项时,需确保浏览器支持对应地区的 Intl API(现代浏览器均支持,老旧浏览器可引入@formatjs/intl-numberformatpolyfill); - 舍入规则:
formatNumber的roundingMethod默认为round(四舍五入),按需选择ceil(向上取整)/floor(向下取整); - 空值处理:格式化
null/undefined/NaN时,方法会返回空字符串或原始值,建议提前做非空校验; - Tree Shaking:按需导入单个方法(如
import { formatCurrency } from 'quasar/dist/quasar.esm.js'),减少打包体积; - 反格式化限制:部分格式化方法(如
formatBytes)无反格式化能力,需自行处理; - Vue 响应式:在模板中直接使用格式化方法时,建议封装为计算属性,避免重复执行(如
computed(() => formatCurrency(price.value)))。
六、核心优势
相比手动编写格式化函数或引入 numeral.js/accounting.js 等第三方库,Quasar Formatter Utils 的核心优势:
- 轻量无依赖,与 Quasar 生态深度融合,无需额外适配;
- 覆盖前端绝大部分格式化场景,一套工具满足所有需求;
- 支持本地化和自定义配置,适配不同地区 / 业务的格式规则;
- 提供反格式化能力,适配 “展示格式化 + 提交原始值” 的常见业务流程。
总结
- Quasar Formatter Utils 封装了数字、货币、字节、电话号码等高频数据的格式化逻辑,是前端数据展示标准化的轻量解决方案;
- 核心高频功能包括:数字千分位、货币符号适配、文件大小单位转换、电话号码格式化,可满足商品展示、表单输入、数据统计等主流场景;
- 在 Vue 组件中使用时,建议封装为计算属性或方法,结合反格式化实现 “展示格式化、提交原始值” 的业务需求。
Quasar Scrolling Utils 核心讲解
Quasar 的滚动工具(scroll)提供了一系列便捷的 API 来操作页面 / 元素滚动,在形态过渡场景中,常用来解决滚动位置干扰过渡动画、元素定位偏移等问题。
1. 核心 API 介绍(v2.10.1 版本)
先明确 v2.10.1 中常用的滚动工具方法(避免使用新版本 API):
| 方法 | 作用 | 示例 |
|---|---|---|
scroll.getScrollTarget(el) | 获取元素的实际滚动容器 | const container = scroll.getScrollTarget(targetEl) |
scroll.getScrollPosition(target) | 获取滚动容器的滚动坐标 { left, top } | const { top, left } = scroll.getScrollPosition(window) |
scroll.offset(el) | 获取元素相对于文档的偏移量 | const { top, left } = scroll.offset(sourceEl) |
scroll.scrollTo(target, offset, duration) | 平滑滚动到指定位置 | scroll.scrollTo(window, { top: 100 }, 500) |
2. 滚动工具的关键应用场景
- 精准定位:
scroll.offset(el)能获取元素相对于文档的绝对偏移,解决因页面滚动导致getBoundingClientRect定位不准的问题(这是形态过渡错位的常见原因)。 - 滚动容器识别:
scroll.getScrollTarget(el)能自动识别元素的实际滚动容器(比如嵌套滚动的 div 而非 window),避免定位计算错误。 - 过渡后滚动校准:如果卡片展开后需要滚动到可视区域,可添加:
// 动画完成后滚动到卡片位置 scroll.scrollTo(container, { top: targetOffset.top - 20 }, 300)
总结
- 核心作用:Quasar v2.10.1 的滚动工具主要解决形态过渡中「元素定位受滚动影响」的问题,
scroll.offset()是最常用的精准定位方法。 - 版本适配:v2.10.1 中滚动工具的核心 API(
offset/getScrollPosition/getScrollTarget)均稳定可用,无需兼容新特性。 - 实战要点:形态过渡前用
scroll.offset()计算元素绝对位置,替代getBoundingClientRect,可彻底解决滚动导致的过渡错位问题。
Type Checking Utils(类型检查工具)-核心用法
1. 官方定义(官网原文核心)
Quasar 的 Type Checking Utils 提供了一套简洁、直观的方法来替代原生 JavaScript 中不直观、易出错的类型检查方式(如 typeof、instanceof),让类型判断代码更易读、更可靠。
2. 安装 / 引入(官网规范)
// 方式1:全局引入(Quasar 项目中已全局注册)
import { is } from 'quasar'
// 方式2:单独引入(官网推荐的按需引入)
import { is } from 'quasar/dist/utils/type-checking'
3. 核心 API 列表(严格按官网文档)
| 方法 | 官网描述 | 基础示例 |
|---|---|---|
is.rawObject(val) | 检查值是否为「纯对象」(Object 构造函数创建,非数组 / 函数 / 正则等) | is.rawObject({ name: 'test' }) // trueis.rawObject([]) // false |
is.object(val) | 检查值是否为「对象类型」(包括数组、函数、正则等) | is.object([1,2,3]) // trueis.object(null) // false |
is.def(val) | 检查值是否「已定义」(非 undefined /null) | is.def(0) // trueis.def(null) // false |
is.undef(val) | 检查值是否「未定义」(undefined /null) | is.undef(undefined) // trueis.undef('') // false |
is.function(val) | 检查值是否为函数 | is.function(() => {}) // trueis.function({}) // false |
is.element(val) | 检查值是否为原生 DOM 元素 | is.element(document.querySelector('div')) // trueis.element({ $el: div }) // false |
is.number(val) | 检查值是否为「有效数字」(排除 NaN/Infinity) | is.number(123) // trueis.number(NaN) // false |
is.string(val) | 检查值是否为字符串 | is.string('test') // trueis.string(123) // false |
is.array(val) | 检查值是否为数组 | is.array([1,2]) // trueis.array({}) // false |
is.boolean(val) | 检查值是否为布尔值 | is.boolean(true) // trueis.boolean(1) // false |
is.date(val) | 检查值是否为 Date 对象 | is.date(new Date()) // trueis.date('2025-01-01') // false |
is.regExp(val) | 检查值是否为正则表达式 | is.regExp(/test/) // trueis.regExp('test') // false |
is.promise(val) | 检查值是否为 Promise 对象 | is.promise(new Promise(() => {})) // trueis.promise({ then: () => {} }) // false |
Quasar Event Bus Util(事件总线工具)核心总结
一、核心定位
Quasar Event Bus 是一套轻量级的全局事件通信工具,用于 Quasar 应用中跨组件 / 跨页面的无耦合通信,替代 Vue 2 废弃的 $on/$emit/$off 全局事件 API,适配 Vue 3 组合式语法,支持事件订阅、发布、取消订阅、一次性订阅等核心能力,轻量无依赖且适配跨平台场景。
二、核心特性
- 全局通信:无需组件层级关系,任意组件 / 模块均可订阅 / 发布事件;
- Vue 3 适配:专为 Vue 3 设计,兼容
setup语法和组合式 API; - 灵活控制:支持一次性事件(触发后自动取消订阅)、批量取消订阅、事件命名空间;
- 轻量高效:纯 JS 实现,无额外依赖,支持 Tree Shaking 按需引入;
- 跨平台兼容:适配浏览器、Cordova、Electron 等 Quasar 支持的运行环境。
三、核心功能与用法
1. 基础使用流程(订阅 → 发布 → 取消订阅)
步骤 1:导入 Event Bus
import { EventBus } from 'quasar'
// 创建全局事件总线实例(建议全局唯一)
const bus = EventBus.create()
步骤 2:订阅事件(组件 A)
<template>
<div>组件 A:{{ message }}</div>
</template>
<script setup>
import { ref, onUnmounted } from 'vue'
import { EventBus } from 'quasar'
// 创建/复用全局总线实例
const bus = EventBus.create()
const message = ref('')
// 1. 基础订阅事件
const eventHandler = (data) => {
message.value = `收到消息:${data}`
}
// 订阅 "msg-update" 事件
bus.on('msg-update', eventHandler)
// 2. 一次性订阅(触发后自动取消)
bus.once('once-msg', (data) => {
console.log('一次性事件:', data) // 仅触发一次
})
// 组件卸载时取消订阅(避免内存泄漏)
onUnmounted(() => {
bus.off('msg-update', eventHandler)
})
</script>
步骤 3:发布事件(组件 B)
<template>
<q-btn label="发送消息" @click="sendMsg" />
</template>
<script setup>
import { EventBus } from 'quasar'
// 复用全局总线实例(需与订阅端一致)
const bus = EventBus.create()
// 发布事件(可传递任意数据)
const sendMsg = () => {
// 发布 "msg-update" 事件,传递字符串数据
bus.emit('msg-update', 'Hello Event Bus!')
// 发布一次性事件
bus.emit('once-msg', '仅触发一次')
// 发布事件并传递多参数
bus.emit('multi-params', 123, { name: 'Quasar' }, [1,2,3])
}
</script>
2. 核心 API 解析
| 方法 | 作用 | 示例 |
|---|---|---|
EventBus.create() | 创建事件总线实例(全局唯一) | const bus = EventBus.create() |
bus.on(eventName, handler) | 订阅事件,持续监听 | bus.on('update', (data) => {}) |
bus.once(eventName, handler) | 一次性订阅,触发后自动取消 | bus.once('init', () => {}) |
bus.emit(eventName, ...args) | 发布事件,可传多个参数 | bus.emit('msg', 1, 2, 3) |
bus.off(eventName, handler) | 取消指定事件的指定处理器 | bus.off('update', handler) |
bus.off(eventName) | 取消指定事件的所有处理器 | bus.off('update') |
bus.off() | 取消当前实例的所有事件订阅 | bus.off() |
bus.has(eventName, handler) | 检查是否订阅了指定事件 / 处理器 | bus.has('update', handler) // true/false |
bus.events() | 获取当前实例的所有订阅事件 | bus.events() // 返回事件名称数组 |
3. 高级用法:事件命名空间
通过命名空间区分同类事件,避免命名冲突:
// 订阅用户相关事件(命名空间:user)
bus.on('user:login', (userInfo) => {
console.log('用户登录:', userInfo)
})
bus.on('user:logout', () => {
console.log('用户登出')
})
// 发布命名空间事件
bus.emit('user:login', { id: 1, name: '张三' })
4. 全局总线实例封装(推荐)
为避免重复创建实例,建议封装为全局工具:
// src/utils/event-bus.js
import { EventBus } from 'quasar'
// 创建全局唯一的事件总线实例
const globalBus = EventBus.create()
// 导出实例,全局复用
export default globalBus
组件中复用:
// 任意组件
import globalBus from 'src/utils/event-bus'
// 订阅
globalBus.on('msg', () => {})
// 发布
globalBus.emit('msg', '全局消息')
四、典型业务场景
场景 1:跨页面通知(如登录状态更新)
// 登录组件(发布事件)
import globalBus from 'src/utils/event-bus'
const login = async () => {
const userInfo = await api.login()
// 发布登录成功事件
globalBus.emit('user:login', userInfo)
}
// 头部组件(订阅事件,更新用户信息)
import { ref, onMounted, onUnmounted } from 'vue'
import globalBus from 'src/utils/event-bus'
const userInfo = ref(null)
const handleLogin = (data) => {
userInfo.value = data
}
onMounted(() => {
globalBus.on('user:login', handleLogin)
})
onUnmounted(() => {
globalBus.off('user:login', handleLogin)
})
场景 2:弹窗关闭后刷新列表
// 弹窗组件(关闭时发布事件)
import globalBus from 'src/utils/event-bus'
const closeDialog = () => {
// 发布列表刷新事件
globalBus.emit('list:refresh')
dialogRef.value.hide()
}
// 列表组件(订阅事件刷新数据)
import globalBus from 'src/utils/event-bus'
const fetchList = () => {
// 重新请求列表数据
list.value = await api.getList()
}
onMounted(() => {
globalBus.on('list:refresh', fetchList)
})
onUnmounted(() => {
globalBus.off('list:refresh', fetchList)
})
五、关键注意事项
- 内存泄漏防范:订阅事件的组件卸载时,必须通过
bus.off()取消订阅(尤其是单页应用),否则事件处理器会一直存在,导致内存泄漏; - 实例唯一性:跨组件通信时,必须使用同一个 Event Bus 实例(建议封装为全局工具),否则无法通信;
- 事件命名规范:建议使用「模块:事件」的命名空间格式(如
user:login),避免不同业务的事件名称冲突; - 参数传递限制:事件参数建议传递简单数据(字符串 / 数字 / 对象),避免传递复杂实例(如 DOM 元素、组件实例);
- 替代方案说明:Event Bus 适用于简单跨组件通信,复杂场景(如全局状态管理)建议使用 Pinia/Vuex,而非 Event Bus;
- Tree Shaking:按需导入(
import { EventBus } from 'quasar/dist/quasar.esm.js')可减少打包体积; - Vue 3 兼容性:替代 Vue 2 的
Vue.prototype.$bus,完全适配 Vue 3 的setup语法和组合式 API。
六、核心优势与局限
优势
- 轻量无依赖,无需引入额外状态管理库;
- 无组件层级限制,任意组件 / 模块均可通信;
- API 简洁,学习成本低,快速实现跨组件通信;
- 适配 Quasar 跨平台场景,无环境兼容问题。
局限
- 不支持事件溯源,难以追踪事件发布 / 订阅的调用链路;
- 复杂业务场景下易导致 “事件泛滥”,维护成本升高;
- 无法持久化事件状态,页面刷新后订阅关系丢失。
总结
- Quasar Event Bus 是轻量级全局事件通信工具,适配 Vue 3 组合式 API,用于跨组件 / 跨页面的简单通信;
- 核心用法是「创建实例 → 订阅事件 → 发布事件 → 取消订阅」,需注意组件卸载时清理订阅,避免内存泄漏;
- 适合简单通信场景(如登录状态更新、弹窗刷新列表),复杂全局状态管理建议使用 Pinia。
Quasar Other Utils(其他工具)核心总结
一、核心定位
Quasar Other Utils 是一套覆盖多场景的轻量级辅助工具集,包含 URL 打开、剪贴板操作、文件导出、Promise 调度、防抖节流、对象处理等高频通用功能,无需额外依赖,适配 Quasar 跨平台(Web / 移动端 / 桌面端)场景,可直接替代第三方工具库(如 Lodash 的部分方法),提升开发效率。
二、核心特性
- 功能全面:覆盖前端开发中 “通用操作” 类高频需求,无需零散引入多个工具库;
- 跨平台适配:自动兼容浏览器、Cordova、Electron 等运行环境,处理平台差异(如 Cordova 中打开链接);
- 轻量高效:纯 JS 实现,支持 Tree Shaking 按需引入,不增加额外包体积;
- API 简洁:方法调用简单直观,无需复杂配置,部分方法返回 Promise 适配异步流程;
- Vue 友好:可直接配合 Vue 组件生命周期使用,避免内存泄漏(如防抖函数在组件内的正确声明)。
三、核心功能与用法
1. 链接与外部操作(openURL)
作用
跨平台打开 URL,自动处理不同环境的兼容性(如浏览器弹窗、Cordova InAppBrowser、iOS SafariViewController)。
基础用法
import { openURL } from 'quasar'
// 基础打开URL
openURL('https://quasar.dev')
// 带失败回调与窗口配置
openURL(
'https://quasar.dev',
(err) => console.error('打开失败:', err), // 打开失败回调
{ menubar: false, toolbar: false } // 新窗口特性(仅浏览器生效)
)
注意事项
- Cordova 中打开电话拨号器,需用
<a href="tel:123456">而非openURL; - 窗口特性(如
noopener)默认开启,保障安全,可通过布尔值关闭。
2. 剪贴板操作(copyToClipboard)
作用
将文本复制到系统剪贴板,返回 Promise 告知操作结果。
基础用法
import { copyToClipboard } from 'quasar'
// 复制文本
copyToClipboard('需要复制的内容')
.then(() => {
console.log('复制成功')
})
.catch(() => {
console.error('复制失败(可能是浏览器权限限制)')
})
3. 文件导出(exportFile)
作用
触发浏览器下载文件,支持文本、二进制数据、Blob 等多种数据类型,可指定 MIME 类型、编码。
基础用法
import { exportFile } from 'quasar'
// 导出文本文件(默认 MIME 类型:application/octect-stream)
const status = exportFile('笔记.txt', '文件内容')
if (status === true) console.log('下载触发成功')
// 导出 CSV 文件(指定编码和 MIME 类型)
exportFile(
'数据.csv',
'姓名,年龄\n张三,25\n李四,30',
{ encoding: 'windows-1252', mimeType: 'text/csv;charset=windows-1252' }
)
// 导出二进制文件(如图片 Blob)
fetch('https://example.com/image.png')
.then(res => res.blob())
.then(blob => {
exportFile('下载图片.png', blob, 'image/png')
})
4. Promise 调度(runSequentialPromises)
作用
按顺序或多线程运行多个 Promise,支持依赖前一个 Promise 结果、失败中断 / 继续等逻辑,适配批量请求、任务队列场景。
核心用法
import { runSequentialPromises } from 'quasar'
import axios from 'axios'
// 1. 按顺序运行(数组形式)
runSequentialPromises([
() => axios.get('/api/users'),
(results) => axios.get(`/api/orders?uid=${results[0].value.data.id}`) // 依赖前一个结果
])
.then(results => {
console.log('用户数据:', results[0].value)
console.log('订单数据:', results[1].value)
})
.catch(err => {
console.error(`第 ${err.key} 个任务失败:`, err.reason)
})
// 2. 多线程运行(3 个线程并行)
runSequentialPromises(
[() => axios.get('/api/a'), () => axios.get('/api/b'), () => axios.get('/api/c')],
{ threadsNumber: 3, abortOnFail: false } // 失败不中断
)
.then(results => {
results.forEach(res => {
if (res.status === 'fulfilled') console.log('成功:', res.value)
else console.error('失败:', res.reason)
})
})
关键配置
threadsNumber:线程数(浏览器默认最大 5,超出无收益);abortOnFail:是否失败中断(默认 true,失败即停止后续任务)。
5. 防抖与节流(debounce /throttle/frameDebounce)
作用
限制函数触发频率,优化高频事件(如 resize、input、scroll)的性能。
基础用法
import { debounce, throttle, frameDebounce } from 'quasar'
// 1. 防抖:停止触发 300ms 后执行
const debouncedFn = debounce(() => {
console.log('窗口调整完成')
}, 300)
window.addEventListener('resize', debouncedFn)
// 2. 节流:每 500ms 最多执行一次
const throttledFn = throttle(() => {
console.log('滚动中(每 500ms 一次)')
}, 500)
window.addEventListener('scroll', throttledFn)
// 3. 帧防抖:下一个浏览器帧执行(适配动画、DOM 操作)
const frameDebouncedFn = frameDebounce(() => {
console.log('DOM 操作(避免重绘抖动)')
})
window.addEventListener('resize', frameDebouncedFn)
组件内使用注意
- 避免在
methods中直接声明(如myFn: debounce(...)),否则组件实例共享防抖状态; - 正确用法:在
created或setup中赋值给实例属性。
6. 对象与标识工具(extend /uid)
(1)对象深拷贝(extend)
类似 jQuery.extend,支持深浅拷贝,合并多个对象。
import { extend } from 'quasar'
// 浅拷贝
const obj1 = extend({}, { a: 1 }, { b: 2 })
// 深拷贝
const obj2 = extend(true, { a: { c: 3 } }, { a: { d: 4 } })
// obj2 结果:{ a: { c: 3, d: 4 } }
(2)生成唯一标识(uid)
生成 UUID 格式的唯一字符串,用于组件 key、临时标识等。
import { uid } from 'quasar'
const uniqueId = uid() // 示例:501e7ae1-7e6f-b923-3e84-4e946bff31a8
7. DOM 事件辅助(event)
作用
跨浏览器处理 DOM 事件,提供鼠标按键判断、坐标获取、事件阻止等便捷方法。
基础用法
import { event } from 'quasar'
document.querySelector('#btn').addEventListener('click', (evt) => {
// 判断鼠标按键
if (event.leftClick(evt)) console.log('左键点击')
if (event.rightClick(evt)) console.log('右键点击')
// 获取事件位置(视口内坐标)
const pos = event.position(evt) // { top: 100, left: 200 }
// 阻止事件冒泡与默认行为
event.stopAndPrevent(evt)
})
// 处理鼠标滚轮事件
window.addEventListener('wheel', (evt) => {
const distance = event.getMouseWheelDistance(evt) // { x: 0, y: 10 }(滚动距离)
})
四、典型业务场景
场景 1:搜索框防抖请求
<template>
<q-input v-model="searchVal" @input="handleSearch" label="搜索" />
</template>
<script setup>
import { ref } from 'vue'
import { debounce } from 'quasar'
const searchVal = ref('')
// 防抖搜索(300ms 后触发请求)
const handleSearch = debounce(async (val) => {
const res = await fetch(`/api/search?key=${val}`)
const data = await res.json()
console.log('搜索结果:', data)
}, 300)
</script>
场景 2:批量导出数据
import { exportFile, runSequentialPromises } from 'quasar'
// 批量请求数据后导出 CSV
const fetchTasks = [
() => axios.get('/api/data1'),
() => axios.get('/api/data2'),
() => axios.get('/api/data3')
]
runSequentialPromises(fetchTasks)
.then(results => {
// 合并数据为 CSV 格式
const csvContent = results.map(res => res.value.data).join('\n')
exportFile('批量数据.csv', csvContent, 'text/csv')
})
五、关键注意事项
- 跨平台兼容性:Cordova/Electron 环境下,部分方法(如
openURL)会自动适配,无需额外处理; - 权限与限制:
copyToClipboard可能受浏览器权限限制,需通过 Promise 捕获失败; - 防抖节流声明:组件内使用时,需在生命周期内赋值,避免实例共享状态;
- Tree Shaking:按需导入单个方法(如
import { uid } from 'quasar/dist/quasar.esm.js'),减少打包体积; - 数据类型:
exportFile支持多种数据类型,二进制数据需传入 Blob 或 ArrayBuffer。
六、核心优势
相比零散引入第三方工具(如 Lodash、clipboard.js),Quasar Other Utils 的核心优势:
- 无额外依赖,与 Quasar 生态深度融合,无需适配;
- 跨平台自动兼容,避免手动编写环境判断代码;
- 覆盖高频通用场景,API 简洁统一,学习成本低;
- 支持 Tree Shaking,仅打包使用的功能,优化体积。

1289

被折叠的 条评论
为什么被折叠?



