最新Quasar 指南8:35790字带你了解Quasar 实用程序 核心总结 - Quasar 完全教程:从基础到实战 --Ryan

Quasar Date Utils(日期工具)核心总结

一、核心定位

Quasar Date Utils 是一套轻量级日期处理工具集,无需依赖 Moment.js 等第三方库,封装了日期格式化、创建、验证、加减、比较等常用操作,支持本地时间与 UTC 时间切换,适配 Quasar 跨平台项目,且支持 Tree Shaking 按需引入,减少包体积。

二、核心特性

  1. 输入灵活:支持 Unix 时间戳、日期字符串(原生 JS Date 可解析格式)、Date 对象作为输入参数;
  2. 输出统一:所有操作返回原生 JS Date 对象(格式化方法返回字符串),兼容性强;
  3. 按需引入:支持 ES6 解构导入单个方法,实现 Tree Shaking,仅打包使用的功能;
  4. 国际化支持:格式化日期时可自定义星期、月份的多语言文本,适配 i18n 场景;
  5. 轻量高效:无需额外依赖,比 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')
关键格式标记(常用)
标记说明示例
YYYY4 位年份2025
MM2 位月份(补零)03
DD2 位日期(补零)15
HH24 小时制(补零)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)。

四、关键注意事项

  1. 月份索引:JS 原生 Date 月份为 0-11(0=1 月,11=12 月),Quasar 工具遵循此规则;
  2. UTC 支持:buildDate、adjustDate 等方法的第二个参数为 true 时,按 UTC 时间处理;
  3. 格式转义:格式化时需保留 [ ] 等字符,需用 [] 包裹(如 [今天] YYYY-MM-DD);
  4. Tree Shaking:按需导入单个方法(如 import { addToDate } from 'quasar/dist/quasar.esm.js'),减少打包体积;
  5. 兼容性:支持所有现代浏览器,无需额外 polyfill。

五、优势对比

相比 Moment.js,Quasar Date Utils 更轻量(无额外依赖)、支持 Tree Shaking,且与 Quasar 生态无缝集成,适合 Quasar 项目中替代第三方日期库,满足大部分日常日期处理需求。

Quasar Color Utils(颜色工具)核心总结

一、核心定位

Quasar Color Utils 是一套轻量级颜色处理工具集,专为前端颜色操作设计,无需额外依赖,支持颜色格式转换、色值计算、对比度校验、主题色生成等核心功能,完美适配 Quasar 内置的调色板体系,可快速实现动态换肤、颜色适配等场景。

二、核心特性

  1. 格式兼容:支持 Hex(#fff/#ffffff)、RGB/RGBA(rgb(255,255,255))、HSL/HSLA、Quasar 调色板名称(如 primary/purple-6)等多种颜色输入格式;
  2. 功能全面:覆盖颜色解析、转换、明暗调整、透明度修改、对比度计算、主题色生成等高频需求;
  3. 适配 Quasar 生态:可直接解析 Quasar 内置调色板颜色,与 Quasar 组件样式无缝联动;
  4. 轻量高效:纯 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)

五、关键注意事项

  1. Quasar 调色板解析:解析 primary/purple-6 等 Quasar 调色板颜色时,需确保项目已在 quasar.config.js 中配置对应的主题色,否则会解析失败;
  2. 格式规范:Hex 颜色支持 3 位(#fff)和 6 位(#ffffff),带透明度的 Hex 需 8 位(#ffffff80);
  3. Tree Shaking:按需导入单个方法(如 import { lighten } from 'quasar/dist/quasar.esm.js')可减少打包体积;
  4. 浏览器兼容性:所有方法兼容现代浏览器,无需额外 polyfill;
  5. 透明度处理:RGBA/HSLA 格式的透明度值范围为 0-1(如 0.5 表示 50% 透明度)。

六、核心优势

相比 chroma-jstinycolor2 等第三方颜色库,Quasar Color Utils 更轻量(无依赖)、与 Quasar 生态深度融合,能直接复用 Quasar 内置调色板,无需额外适配,适合 Quasar 项目中快速实现颜色相关需求。

总结

  1. Quasar Color Utils 支持 Hex/RGB/HSL/Quasar 调色板等多格式颜色的解析与转换,是前端颜色操作的轻量解决方案;
  2. 核心高频功能包括:颜色明暗调整、透明度修改、对比度计算、自动生成可读文字色,可满足动态换肤、样式适配等主流场景;
  3. 与 Quasar 内置调色板无缝兼容,无需额外配置即可解析 primary/purple-6 等主题色,适配性极强。

Quasar DOM Utils(DOM 工具)核心总结

一、核心定位

       Quasar DOM Utils 是一套轻量级 DOM 操作工具集,封装了前端高频的 DOM 查找、样式修改、事件处理、元素位置计算等操作,无需依赖 jQuery 等第三方库,适配 Quasar 跨平台(Web / 移动端 / 桌面端)场景,且与 Vue 组件体系无缝兼容。

二、核心特性

  1. 轻量无依赖:纯原生 JS 实现,替代 jQuery 常用 DOM 操作,减少包体积;
  2. 跨平台适配:兼容浏览器、Cordova、Electron 等 Quasar 支持的运行环境;
  3. Vue 友好:可直接配合 Vue 组件的 ref 使用,避免手动操作 DOM 引发的响应式问题;
  4. 安全可靠:封装了边界判断、兼容性处理(如不同浏览器的样式前缀);
  5. 按需引入:支持 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>

五、关键注意事项

  1. Vue ref 配合使用:操作 Vue 组件内的 DOM 时,务必通过 ref 获取,且确保在 onMounted 钩子中执行(组件已挂载,DOM 存在);
  2. 事件清理:通过 addNativeEventListener 绑定的事件,需在组件卸载(onUnmounted)或元素销毁时调用 removeNativeEventListener 移除,避免内存泄漏;
  3. 跨平台兼容:在 Electron/Cordova 环境中,部分方法(如 preventBodyScroll)已做适配,无需额外处理;
  4. 样式单位:设置样式时需显式指定单位(如 '200px',而非 200),避免解析异常;
  5. 边界判断:使用 findDomElement 后建议先判断是否为 null,再执行后续操作,避免报错;
  6. Tree Shaking:按需导入方法(如 import { preventBodyScroll } from 'quasar/dist/quasar.esm.js'),减少打包体积。

六、核心优势

相比 jQuery 或原生 DOM 操作,Quasar DOM Utils 的核心优势:

  • 无需引入 jQuery,减少第三方依赖;
  • 封装了跨平台 / 跨浏览器的兼容性处理,无需手动写兼容代码;
  • 与 Vue 组件体系适配,避免直接操作 DOM 引发的响应式问题;
  • 提供了前端高频的 DOM 操作封装(如滚动控制、可视区域判断),提升开发效率。

总结

  1. Quasar DOM Utils 封装了 DOM 查找、样式 / 类名操作、尺寸 / 位置计算、事件处理等高频操作,是 Vue + Quasar 项目中替代 jQuery 的轻量方案;
  2. 核心高频功能包括:元素查找、滚动控制、可视区域判断、页面滚动禁止 / 恢复,可满足弹窗、懒加载、滚动监听等主流业务场景;
  3. 在 Vue 组件中使用时,需配合 ref 和生命周期钩子,确保 DOM 存在且避免内存泄漏。

Quasar Morph Utils(形态过渡工具)核心总结

一、核心定位

Quasar Morph Utils 是一套专为元素形态过渡动画设计的工具集,封装了元素位置 / 尺寸 / 样式的精准计算与过渡适配逻辑,无需手动编写复杂的 CSS 动画或 JS 计算,可快速实现元素在不同状态(如展开 / 收起、移动、变形)间的平滑过渡,适配 Quasar 组件体系与跨平台场景。

二、核心特性

  1. 精准计算:自动计算元素在不同状态下的位置、尺寸、偏移量,消除手动计算的误差;
  2. 无缝过渡:基于原生 CSS 过渡 / 动画,实现元素从一个形态到另一个形态的平滑切换;
  3. Vue 友好:可直接配合 Vue 组件的 ref 和响应式状态使用,适配组件生命周期;
  4. 轻量无依赖:纯 JS + CSS 实现,无需额外动画库(如 GSAP);
  5. 跨平台兼容:适配浏览器、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('形态过渡完成')
})
核心配置项
配置项类型默认值说明
durationNumber300过渡时长(ms)
easingString'ease-out'缓动函数(支持 CSS 所有合法值)
propsArray['x','y','width','height','opacity','scale']需要过渡的属性
delayNumber0过渡延迟(ms)
keepSourceBooleantrue过渡后是否保留源元素(false 则移除)
keepTargetBooleantrue过渡后是否保留目标元素(false 则移除)
offsetObject{ 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
  })
}

五、关键注意事项

  1. 元素定位:参与过渡的元素建议设置 position: absolute/fixed/relative,否则位置计算可能出错;
  2. Quasar 组件 DOM 获取:Quasar 组件(如 QBtn、QCard)需通过 $el 获取原生 DOM 元素,不能直接传组件 ref;
  3. 过渡属性兼容scale/rotate 等变换属性依赖 CSS transform,部分老旧浏览器需注意兼容性;
  4. 性能优化:避免同时对大量元素执行形态过渡,可通过 requestAnimationFrame 分批处理;
  5. 样式冲突:过渡过程中会临时覆盖元素的样式,过渡完成后会自动还原,若有自定义样式需注意优先级;
  6. 生命周期清理:在 Vue 组件卸载时,需中断未完成的过渡并调用 morphCleanup 清理样式,避免内存泄漏。

六、核心优势

相比手动编写 CSS 过渡 / 动画,Quasar Morph Utils 的核心优势:

  • 自动计算元素状态差异,无需手动编写 from/to 关键帧;
  • 支持 “源元素 → 目标元素”“源元素 → 自定义状态” 两种过渡模式,灵活适配不同场景;
  • 与 Quasar 组件体系无缝兼容,可直接操作 Quasar 组件的原生 DOM;
  • 提供过渡中断、清理等配套方法,避免样式残留和内存泄漏。

总结

  1. Quasar Morph Utils 专注于元素形态过渡动画,封装了位置 / 尺寸 / 样式的精准计算,无需手动编写复杂动画逻辑;
  2. 核心用法是 morph(source, target, options),支持源元素→目标元素、源元素→自定义状态两种过渡模式;
  3. 在 Vue 组件中使用时,需注意获取原生 DOM 元素、组件生命周期清理,避免位置计算错误和内存泄漏。

Quasar Formatter Utils(格式化工具)核心总结

一、核心定位

Quasar Formatter Utils 是一套轻量级数据格式化工具集,专为前端常见的文本、数字、货币、字节、时间等数据格式化场景设计,无需额外依赖,适配 Quasar 跨平台项目,可快速实现标准化的数据展示,减少重复的格式化代码。

二、核心特性

  1. 场景全覆盖:支持数字、货币、字节、百分比、时间、电话号码、信用卡号等高频格式化需求;
  2. 本地化适配:支持多语言 / 地区的格式规则(如货币符号、数字千分位、日期格式);
  3. 可逆操作:部分格式化方法提供 “反格式化” 能力(如将带千分位的数字还原为纯数字);
  4. 轻量高效:纯 JS 实现,无第三方依赖,支持 Tree Shaking 按需引入;
  5. 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
核心配置项
配置项类型默认值说明
decimalPlacesNumber2保留小数位数(负数表示不限制)
decimalSeparatorString'.'小数分隔符
thousandsSeparatorString','千分位分隔符
prefixString''前缀(如货币符号)
suffixString''后缀(如单位)
roundingMethodString'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>

五、关键注意事项

  1. 本地化配置:使用 locale 配置项时,需确保浏览器支持对应地区的 Intl API(现代浏览器均支持,老旧浏览器可引入 @formatjs/intl-numberformat polyfill);
  2. 舍入规则formatNumber 的 roundingMethod 默认为 round(四舍五入),按需选择 ceil(向上取整)/floor(向下取整);
  3. 空值处理:格式化 null/undefined/NaN 时,方法会返回空字符串或原始值,建议提前做非空校验;
  4. Tree Shaking:按需导入单个方法(如 import { formatCurrency } from 'quasar/dist/quasar.esm.js'),减少打包体积;
  5. 反格式化限制:部分格式化方法(如 formatBytes)无反格式化能力,需自行处理;
  6. Vue 响应式:在模板中直接使用格式化方法时,建议封装为计算属性,避免重复执行(如 computed(() => formatCurrency(price.value)))。

六、核心优势

相比手动编写格式化函数或引入 numeral.js/accounting.js 等第三方库,Quasar Formatter Utils 的核心优势:

  • 轻量无依赖,与 Quasar 生态深度融合,无需额外适配;
  • 覆盖前端绝大部分格式化场景,一套工具满足所有需求;
  • 支持本地化和自定义配置,适配不同地区 / 业务的格式规则;
  • 提供反格式化能力,适配 “展示格式化 + 提交原始值” 的常见业务流程。

总结

  1. Quasar Formatter Utils 封装了数字、货币、字节、电话号码等高频数据的格式化逻辑,是前端数据展示标准化的轻量解决方案;
  2. 核心高频功能包括:数字千分位、货币符号适配、文件大小单位转换、电话号码格式化,可满足商品展示、表单输入、数据统计等主流场景;
  3. 在 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)
    

总结

  1. 核心作用:Quasar v2.10.1 的滚动工具主要解决形态过渡中「元素定位受滚动影响」的问题,scroll.offset() 是最常用的精准定位方法。
  2. 版本适配:v2.10.1 中滚动工具的核心 API(offset/getScrollPosition/getScrollTarget)均稳定可用,无需兼容新特性。
  3. 实战要点:形态过渡前用 scroll.offset() 计算元素绝对位置,替代 getBoundingClientRect,可彻底解决滚动导致的过渡错位问题。

Type Checking Utils(类型检查工具)-核心用法

1. 官方定义(官网原文核心)

Quasar 的 Type Checking Utils 提供了一套简洁、直观的方法来替代原生 JavaScript 中不直观、易出错的类型检查方式(如 typeofinstanceof),让类型判断代码更易读、更可靠。

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 组合式语法,支持事件订阅、发布、取消订阅、一次性订阅等核心能力,轻量无依赖且适配跨平台场景。

二、核心特性

  1. 全局通信:无需组件层级关系,任意组件 / 模块均可订阅 / 发布事件;
  2. Vue 3 适配:专为 Vue 3 设计,兼容 setup 语法和组合式 API;
  3. 灵活控制:支持一次性事件(触发后自动取消订阅)、批量取消订阅、事件命名空间;
  4. 轻量高效:纯 JS 实现,无额外依赖,支持 Tree Shaking 按需引入;
  5. 跨平台兼容:适配浏览器、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)
})

五、关键注意事项

  1. 内存泄漏防范:订阅事件的组件卸载时,必须通过 bus.off() 取消订阅(尤其是单页应用),否则事件处理器会一直存在,导致内存泄漏;
  2. 实例唯一性:跨组件通信时,必须使用同一个 Event Bus 实例(建议封装为全局工具),否则无法通信;
  3. 事件命名规范:建议使用「模块:事件」的命名空间格式(如 user:login),避免不同业务的事件名称冲突;
  4. 参数传递限制:事件参数建议传递简单数据(字符串 / 数字 / 对象),避免传递复杂实例(如 DOM 元素、组件实例);
  5. 替代方案说明:Event Bus 适用于简单跨组件通信,复杂场景(如全局状态管理)建议使用 Pinia/Vuex,而非 Event Bus;
  6. Tree Shaking:按需导入(import { EventBus } from 'quasar/dist/quasar.esm.js')可减少打包体积;
  7. Vue 3 兼容性:替代 Vue 2 的 Vue.prototype.$bus,完全适配 Vue 3 的 setup 语法和组合式 API。

六、核心优势与局限

优势

  • 轻量无依赖,无需引入额外状态管理库;
  • 无组件层级限制,任意组件 / 模块均可通信;
  • API 简洁,学习成本低,快速实现跨组件通信;
  • 适配 Quasar 跨平台场景,无环境兼容问题。

局限

  • 不支持事件溯源,难以追踪事件发布 / 订阅的调用链路;
  • 复杂业务场景下易导致 “事件泛滥”,维护成本升高;
  • 无法持久化事件状态,页面刷新后订阅关系丢失。

总结

  1. Quasar Event Bus 是轻量级全局事件通信工具,适配 Vue 3 组合式 API,用于跨组件 / 跨页面的简单通信;
  2. 核心用法是「创建实例 → 订阅事件 → 发布事件 → 取消订阅」,需注意组件卸载时清理订阅,避免内存泄漏;
  3. 适合简单通信场景(如登录状态更新、弹窗刷新列表),复杂全局状态管理建议使用 Pinia。

Quasar Other Utils(其他工具)核心总结

一、核心定位

Quasar Other Utils 是一套覆盖多场景的轻量级辅助工具集,包含 URL 打开、剪贴板操作、文件导出、Promise 调度、防抖节流、对象处理等高频通用功能,无需额外依赖,适配 Quasar 跨平台(Web / 移动端 / 桌面端)场景,可直接替代第三方工具库(如 Lodash 的部分方法),提升开发效率。

二、核心特性

  1. 功能全面:覆盖前端开发中 “通用操作” 类高频需求,无需零散引入多个工具库;
  2. 跨平台适配:自动兼容浏览器、Cordova、Electron 等运行环境,处理平台差异(如 Cordova 中打开链接);
  3. 轻量高效:纯 JS 实现,支持 Tree Shaking 按需引入,不增加额外包体积;
  4. API 简洁:方法调用简单直观,无需复杂配置,部分方法返回 Promise 适配异步流程;
  5. 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')
  })

五、关键注意事项

  1. 跨平台兼容性:Cordova/Electron 环境下,部分方法(如 openURL)会自动适配,无需额外处理;
  2. 权限与限制copyToClipboard 可能受浏览器权限限制,需通过 Promise 捕获失败;
  3. 防抖节流声明:组件内使用时,需在生命周期内赋值,避免实例共享状态;
  4. Tree Shaking:按需导入单个方法(如 import { uid } from 'quasar/dist/quasar.esm.js'),减少打包体积;
  5. 数据类型exportFile 支持多种数据类型,二进制数据需传入 Blob 或 ArrayBuffer。

六、核心优势

相比零散引入第三方工具(如 Lodash、clipboard.js),Quasar Other Utils 的核心优势:

  • 无额外依赖,与 Quasar 生态深度融合,无需适配;
  • 跨平台自动兼容,避免手动编写环境判断代码;
  • 覆盖高频通用场景,API 简洁统一,学习成本低;
  • 支持 Tree Shaking,仅打包使用的功能,优化体积。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值