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 | 类型 | 默认值 | 说明 |
|---|---|---|---|
sent | Boolean | false | 是否渲染为"自己发送"的消息(内容靠右、颜色区分) |
label | String | — | 只渲染一个标签头部/分隔行(如日期),不渲染气泡 |
name | String | — | 消息作者的名字 |
avatar | String | — | 作者头像图片 URL |
text | Array | — | 消息正文,为字符串数组,每项渲染为一段文本 |
stamp | String | — | 创建时间戳文本,如'13:55'、'Yesterday at 13:51' |
bg-color | String | — | 气泡背景色,取值来自 Quasar 调色板 |
text-color | String | — | 气泡文字颜色,同样来自调色板 |
size | String | — | 气泡占据的栅格宽度,1–12(同col-*栅格语义) |
label-html | Boolean | false | 以 HTML 渲染label,存在 XSS 风险 |
name-html | Boolean | false | 以 HTML 渲染name,存在 XSS 风险 |
text-html | Boolean | false | 以 HTML 渲染text,存在 XSS 风险 |
stamp-html | Boolean | false | 以 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-color与text-color直接接受 Quasar 调色板中的颜色名(如primary、amber-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-6、col-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-html、name-html、text-html、stamp-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内:
- 方向标记:
const op = props.sent ? 'sent' : 'received',决定所有内部类名后缀(q-message--sent、q-message-avatar--sent、q-message-text--sent等)与容器reverse类(实现 Flex 行反转、气泡靠右); - 头像优先级:
#avatar插槽 >avatarprop(渲染为带aria-hidden的<img>); - 作者名优先级:
#name插槽 >nameprop(textContent/innerHTML二选一); - 正文优先级:
#default插槽 >textprop。使用默认插槽时,若只有一个 VNode 会包一层<div>,多个 VNode 则平铺渲染;而text数组则对每个字符串生成独立的q-message-text气泡段落; - 时间戳:
#stamp插槽 >stampprop,追加到每段正文之后; - 尺寸:
sizeprop 映射为col-${size}; - 日期标签:
#label插槽 >labelprop,渲染为q-message-label,放在消息容器之前。
最终的 DOM 骨架为q-message(含q-message-sent/received修饰)→q-message-label+q-message-container(row items-end no-wrap,sent时加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),仅供参考