Quasar 框架 QChatMessage 聊天气泡组件完全指南:Props、Slots、HTML 安全与无障碍实践
2026/9/20 22:50:53 网站建设 项目流程

Quasar 框架 QChatMessage 聊天气泡组件完全指南:Props、Slots、HTML 安全与无障碍实践

【免费下载链接】quasarQuasar Framework - Build high-performance VueJS user interfaces in record time项目地址: https://gitcode.com/gh_mirrors/qu/quasar

导读

QChatMessage 是 Quasar Framework 提供的一款"单条聊天消息条目"组件,它负责把作者名、头像、消息正文、时间戳和日期分隔标签等数据渲染成完整的聊天气泡 UI,开箱即用地支持"自己发送(sent)"与"对方接收(received)"两种视觉形态。本文以官方文档 chat.md 为主线,逐项讲解全部 14 个 Props、5 个 Slots 的用法,并结合仓库内的示例与源码实现,深入剖析组件的渲染原理、HTML 注入风险与无障碍(Accessibility)设计。读完本文,你将能够用 QChatMessage 快速搭建专业、安全、可访问的聊天界面。

组件定位:一条消息,而不是整个聊天窗口

QChatMessage 的定位非常聚焦——它只是一个"聊天条目(chat entry)",负责把 Props 传入的数据渲染成单条气泡消息,并不包含消息列表、输入框、滚动容器等完整 IM 能力。真实场景中你通常用v-for循环渲染多条q-chat-message,外层再包上你自选的滚动容器和输入区。

[!TIP] 当同一会话中部分消息带头像、部分不带时,为了让所有气泡对齐,建议为无头像的消息使用一张占位头像图片。

Props 全解:数据驱动的气泡渲染

依据 QChatMessage.json 中的 API 定义,组件共有 14 个 Props,可按作用分为"内容"与"样式"两类:

Prop类型默认值说明
sentBooleanfalse是否渲染为"自己发送"的消息(内容靠右、颜色区分)
labelString只渲染一个标签头部/分隔行(如日期),不渲染气泡
nameString消息作者的名字
avatarString作者头像图片 URL
textArray消息正文,为字符串数组,每项渲染为一段文本
stampString创建时间戳文本,如'13:55''Yesterday at 13:51'
bg-colorString气泡背景色,取值来自 Quasar 调色板
text-colorString气泡文字颜色,同样来自调色板
sizeString气泡占据的栅格宽度,1–12(同col-*栅格语义)
label-htmlBooleanfalse以 HTML 渲染label,存在 XSS 风险
name-htmlBooleanfalse以 HTML 渲染name,存在 XSS 风险
text-htmlBooleanfalse以 HTML 渲染text,存在 XSS 风险
stamp-htmlBooleanfalse以 HTML 渲染stamp,存在 XSS 风险

其中avatar的 URL 写法非常灵活,源码声明了transformAssetUrls: true,因此以下形式都合法:

<!-- 放在 public 目录:直接引用 --> <q-chat-message avatar="boy-avatar.png" /> <!-- 放在 assets 目录:通过模块解析 --> <q-chat-message avatar="~@/assets/boy-avatar.png" /> <!-- 相对路径格式 --> <q-chat-message :avatar="require('./my_img.jpg')" /> <!-- 远程 URL --> <q-chat-message avatar="https://picsum.photos/500/300" />

text是字符串数组,数组中的每一项会被渲染为一段独立的文字节点,天然支持"一条消息多条分段"的场景(见下文 Size 示例中的超长消息拆分)。

基础用法:从空白气泡到完整消息

以下示例均可在仓库的 docs/src/examples/QChatMessage 目录中找到完整源码。

最简形态:只有正文

只传text数组即可渲染气泡,配合sent区分发送方与接收方:

<q-chat-message :text="['hey, how are you?']" sent /> <q-chat-message :text="['doing fine, how r you?']" />

sent语义上代表"当前用户发送"的消息(渲染为右侧对齐),不加sent的消息则作为"对方接收"的消息渲染在左侧。完整示例见 Basic.vue。

作者名:name

通过name在气泡上方标注作者:

<q-chat-message name="me" :text="['hey, how are you?']" sent /> <q-chat-message name="Jane" :text="['doing fine, how r you?']" />

完整示例见 Name.vue。

头像:avatar

<q-chat-message name="me" avatar="https://cdn.quasar.dev/img/avatar1.jpg" :text="['hey, how are you?']" sent /> <q-chat-message name="Jane" avatar="https://cdn.quasar.dev/img/avatar2.jpg" :text="['doing fine, how r you?']" />

完整示例见 Avatar.vue。注意:源码中头像<img>会被渲染为q-message-avatar q-message-avatar--sent|received类,并带有aria-hidden="true"属性(无障碍部分会详述)。

时间戳:stamp

<q-chat-message name="me" avatar="https://cdn.quasar.dev/img/avatar4.jpg" :text="['hey, how are you?']" sent stamp="7 minutes ago" /> <q-chat-message name="Jane" avatar="https://cdn.quasar.dev/img/avatar3.jpg" :text="['doing fine, how r you?']" stamp="4 minutes ago" />

stamp只是一段纯文本,传入什么就显示什么,你也可以在业务层自行格式化为绝对时间或相对时间。完整示例见 Stamp.vue。

日期分隔标签:label

label用于在消息流中插入"日期/分组标题"——它只渲染一条水平居中的标签,不渲染气泡:

<q-chat-message label="Sunday, 19th" />

完整示例见 Label.vue。从源码看,label对应的 DOM 类名为q-message-label,它被渲染在组件根节点之下、消息容器之上,天然承担"会话内分组分隔符"的职责。

定制:颜色与尺寸

文本与背景色

bg-colortext-color直接接受 Quasar 调色板中的颜色名(如primaryamber-7),组件内部会拼接出text-<color>工具类:

<q-chat-message name="me" avatar="https://cdn.quasar.dev/img/avatar1.jpg" :text="['hey, how are you?']" stamp="7 minutes ago" sent bg-color="amber-7" /> <q-chat-message name="Jane" avatar="https://cdn.quasar.dev/img/avatar5.jpg" :text="['doing fine, how r you?']" stamp="4 minutes ago" text-color="white" bg-color="primary" />

完整示例见 Color.vue。

气泡宽度:size

size接受 1–12 的栅格值(与col-*语义一致),用于控制气泡在会话中的宽度占比。当消息较长时,可以把它限制在例如6(一半宽度),短消息则用8或默认:

<q-chat-message name="Jane" avatar="https://cdn.quasar.dev/img/avatar5.jpg" :text="[ 'doing fine, how r you?', 'I just feel like typing a really, really, REALLY long message to annoy you...' ]" size="6" stamp="4 minutes ago" text-color="white" bg-color="primary" /> <q-chat-message name="Jane" avatar="https://cdn.quasar.dev/img/avatar5.jpg" :text="['Did it work?']" stamp="1 minutes ago" size="8" text-color="white" bg-color="primary" />

从源码看,size会被映射为col-${size}类名(如col-6col-8),因此其栅格行为与 Quasar 栅格系统完全一致。完整示例见 Size.vue。

Slots:完全自定义气泡内容

QChatMessage 提供 5 个插槽,优先级均高于对应 Props——一旦使用插槽,同名 Prop 即被忽略。插槽定义见 QChatMessage.json 的slots段。

default 插槽:自定义消息正文

默认插槽会完全覆盖textprop,允许你在气泡内放任意内容,比如带表情图片的富文本、加载动画等:

<q-chat-message name="me" avatar="https://cdn.quasar.dev/img/avatar3.jpg" stamp="7 minutes ago" sent text-color="white" bg-color="primary" > <div> Hey there! </div> <div> Have you seen Quasar? <img alt="Surprised Quasar emoji" src="https://cdn.quasar.dev/img/discord-omq.png" class="my-emoticon" /> </div> </q-chat-message> <q-chat-message name="Jane" avatar="https://cdn.quasar.dev/img/avatar5.jpg" bg-color="amber"> <q-spinner-dots size="2rem" /> </q-chat-message>

第二个气泡用<q-spinner-dots>模拟"对方正在输入…"的等待态,这是默认插槽的典型实战用法。完整示例见 SlotDefault.vue。

avatar / name / stamp 插槽:逐块替换

这三个插槽分别覆盖头像、名字和时间戳。注意官方示例提示:使用avatar插槽时,建议自行为图片添加q-message-avatar q-message-avatar--sent|received类,以保持与 Prop 渲染一致的外观:

<q-chat-message :text="['Have you seen Quasar?']" sent text-color="white" bg-color="primary" > <template #name>me</template> <template #stamp>7 minutes ago</template> <template #avatar> <img alt="User avatar" class="q-message-avatar q-message-avatar--sent" src="https://cdn.quasar.dev/img/avatar4.jpg" /> </template> </q-chat-message>

完整示例见 SlotAvatarStampName.vue。此外 JSON 定义中还包含label插槽,可自定义日期分隔标签内容。

Sanitization:HTML 注入安全警示

组件提供label-htmlname-htmltext-htmlstamp-html四个"渲染原始 HTML"开关。源码中对应的实现是:

h('div', { class: `q-message-name q-message-name--${op}`, [props.nameHtml ? 'innerHTML' : 'textContent']: props.name })

即:默认走textContent(纯文本,安全),只有显式开启*-html后才走innerHTML(原始 HTML,危险)。官方文档对此给出明确警告:

[!WARNING] 如果你不信任值的来源(例如值来自用户输入),请务必先对内容进行消毒(sanitize)。

看下面的对比示例(完整版见 Sanitize.vue):

<!-- 默认:name 中的 HTML 标签会被当作纯文本显示 --> <q-chat-message name="<span class='text-positive'>Untrusted Source</span>" :text="['hey, how are <strong>you</strong>?']" sent /> <!-- 开启 name-html:标签被渲染,但如果内容不可信则存在 XSS 风险 --> <q-chat-message name="<span class='text-negative'>Jane (trusted name but untrusted text)</span>" name-html :text="['doing fine, how r you?']" sent /> <!-- 同时开启 name-html 与 text-html --> <q-chat-message name="<span class='text-negative'>Jao (trusted)</span>" name-html :text="['<strong>Did it work?</strong>']" text-html sent />

安全实践建议

  • 来自服务端/其他用户的消息正文,默认不要开text-html,直接传纯文本即可;
  • 确实需要富文本(如表情、链接)时,务必先用 DOMPurify 等库对字符串消毒后再传入;
  • 名字、时间戳、日期标签同理,非必要不开*-html

Accessibility:无障碍与可访问性设计(v2.25+)

从 v2.25 起,QChatMessage 强化了无障碍支持,chat.md 中对此有专门一节说明:

  • 正文是纯文本内容,屏幕阅读器可直接朗读;
  • 头像图片对辅助技术隐藏——源码中头像<img>硬编码了aria-hidden="true"(见 QChatMessage.js),避免装饰性图片干扰朗读;
  • "发送/接收"仅通过视觉(对齐方向与颜色)传达,屏幕阅读器无法感知方向差异,因此必须提供nameprop 或等价的文本内容,让"消息作者是谁"能被正确播报。只靠对齐区分作者的可访问性是不足的。

这条提醒对"无障碍合规"项目非常关键:即便视觉上靠左右对齐就能分清对话双方,也请始终为每条消息设置name(或用#name插槽提供文本)。

源码剖析:一条消息如何被渲染

QChatMessage.js 的实现非常轻量,没有模板文件,全部通过渲染函数(render function)输出 DOM,核心逻辑集中在setup内:

  1. 方向标记const op = props.sent ? 'sent' : 'received',决定所有内部类名后缀(q-message--sentq-message-avatar--sentq-message-text--sent等)与容器reverse类(实现 Flex 行反转、气泡靠右);
  2. 头像优先级#avatar插槽 >avatarprop(渲染为带aria-hidden<img>);
  3. 作者名优先级#name插槽 >nameprop(textContent/innerHTML二选一);
  4. 正文优先级#default插槽 >textprop。使用默认插槽时,若只有一个 VNode 会包一层<div>,多个 VNode 则平铺渲染;而text数组则对每个字符串生成独立的q-message-text气泡段落;
  5. 时间戳#stamp插槽 >stampprop,追加到每段正文之后;
  6. 尺寸sizeprop 映射为col-${size}
  7. 日期标签#label插槽 >labelprop,渲染为q-message-label,放在消息容器之前。

最终的 DOM 骨架为q-message(含q-message-sent/received修饰)→q-message-label+q-message-containerrow items-end no-wrapsent时加reverse)→ 头像 +col-*内容列(q-message-name、多个q-message-text,每个内含q-message-text-content与可选q-message-stamp)。所有样式类在 QChatMessage.sass 中定义,你可以在自己的样式中覆写这些类来深度定制外观。

仓库还为组件提供了完整的单元测试与 SSR 水合测试(QChatMessage.test.js、QChatMessage.hydration.test.js),可作为你理解各 Props 组合行为的参考。

小结:何时用 Props,何时用 Slots

需求推荐方式
纯文本消息流、姓名、头像、时间Props:text+name+avatar+stamp
富文本、图片、加载动画、自定义排版#default插槽
特殊头像(如 SVG、QAvatar 组件)#avatar插槽
动态作者名/时间(如本地化格式)#name/#stamp插槽
日期分组labelprop 或#label插槽
强调某条消息(颜色、宽度)bg-color/text-color/size

QChatMessage 的设计哲学是"数据驱动 + 插槽兜底":常规文本场景一行 Props 即可,复杂场景则用插槽完全接管。唯一需要你保持警惕的是*-html系列开关——它们是功能便利与安全责任的边界,务必在渲染不可信内容前做好消毒。

【免费下载链接】quasarQuasar Framework - Build high-performance VueJS user interfaces in record time项目地址: https://gitcode.com/gh_mirrors/qu/quasar

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询