@aiwanna-team/message-list v0.1.1
@aiwanna-team/message-list
一个基于 React + TypeScript + Virtuoso 的高性能消息列表组件,专为聊天应用和实时通讯场景设计。
✨ 功能特性
- 🚀 高性能虚拟滚动 - 基于 Virtuoso 引擎,支持大量消息流畅渲染
- 💬 智能消息气泡 - 自动分组显示,支持连续消息的视觉优化
- 📱 响应式设计 - 完美适配移动端和桌面端
- ⏰ 时间戳显示 - 自动格式化消息时间
- 🔄 上拉加载历史 - 支持异步加载历史消息,游标分页
- 🎨 自定义样式 - 基于 TailwindCSS,易于定制
- 📝 消息更新 - 支持实时更新消息内容(AI 流式回复场景)
- 🔧 TypeScript 支持 - 完整的类型定义
📦 安装
npm install @aiwanna-team/message-list
依赖要求
确保你的项目已安装以下依赖:
npm install react react-dom tailwindcss @tailwindcss/vite
最低版本要求:
- Node.js >= 20.0.0
- React >= 19.1.0
- TailwindCSS >= 4.1.8
🚀 快速开始
设置步骤
安装组件库
npm install @aiwanna-team/message-list
配置 TailwindCSS 源文件检测
在你的主 CSS 文件(如
src/app.css
)中添加:@import "tailwindcss"; @source "../node_modules/@aiwanna-team/message-list";
开始使用组件
基础使用
import React, { useRef } from 'react'
import MessageList, {
type MessageListMethods,
type Message
} from '@aiwanna-team/message-list'
import '@aiwanna-team/message-list/css'
function ChatApp() {
const messageListRef = useRef<MessageListMethods>(null)
const initialMessages: Message[] = [
{
key: 'msg-1',
text: '你好!欢迎使用消息列表组件',
user: 'other',
timestamp: new Date(),
},
{
key: 'msg-2',
text: '这个组件很棒!',
user: 'me',
timestamp: new Date(),
}
]
const handleSendMessage = () => {
messageListRef.current?.sendMessage('新消息内容')
}
return (
<div className="w-full max-w-2xl mx-auto">
<MessageList
ref={messageListRef}
height="500px"
initialMessages={initialMessages}
/>
<button
onClick={handleSendMessage}
className="mt-4 px-4 py-2 bg-blue-500 text-white rounded"
>
发送消息
</button>
</div>
)
}
配置 TailwindCSS
⚠️ 重要配置:由于该组件使用了 TailwindCSS 样式,你需要在项目的 CSS 文件中注册组件库的源文件,否则 TailwindCSS 无法检测到组件中使用的类名,导致样式缺失。
在你的主 CSS 文件中添加:
@import "tailwindcss";
@source "../node_modules/@aiwanna-team/message-list";
这样 TailwindCSS 就能扫描组件库中的样式类并生成对应的 CSS。
示例项目结构:
your-project/
├── src/
│ ├── app.css # 在这里添加 @source 指令
│ └── main.tsx
└── node_modules/
└── @aiwanna-team/
└── message-list/
了解更多关于源文件检测的信息,请参考 TailwindCSS 官方文档。
📚 API 文档
MessageList 组件属性
属性 | 类型 | 默认值 | 说明 |
---|---|---|---|
height | string \| number | "500px" | 消息列表容器高度 |
className | string | "" | 自定义 CSS 类名 |
style | React.CSSProperties | - | 自定义样式 |
initialMessages | Message[] | [] | 初始消息列表 |
onLoadHistory | LoadHistoryFunction | - | 历史消息加载回调 |
historyLoadLimit | number | 20 | 每次加载历史消息数量 |
loadThreshold | number | 100 | 触发加载的滚动阈值(px) |
autoHideScrollbar | boolean | true | 是否自动隐藏滚动条 |
licenseKey | string | "" | Virtuoso 许可证密钥 |
MessageListMethods 实例方法
interface MessageListMethods {
/** 发送消息,返回消息key */
sendMessage: (text: string) => string
/** 接收消息,返回消息key */
receiveMessage: (text: string) => string
/** 更新消息内容 */
updateMessage: (key: string, newText: string) => void
/** 获取所有消息 */
getAllMessages: () => Message[]
/** 清空消息列表 */
clearMessages: () => void
/** 重设消息列表 */
resetMessages: (messages: Message[]) => void
}
Message 数据结构
interface Message {
/** 消息唯一标识 */
key: string
/** 消息文本内容 */
text: string
/** 消息发送者 */
user: "me" | "other"
/** 消息时间戳 */
timestamp?: Date
}
LoadHistoryFunction 回调类型
type LoadHistoryFunction = (
beforeMessageKey?: string,
limit?: number
) => Promise<Message[]>
🔄 历史消息加载
基础配置
import { useCallback } from 'react'
function ChatApp() {
// 历史消息加载函数
const handleLoadHistory = useCallback(async (beforeMessageKey, limit = 20) => {
try {
// 调用你的 API
const response = await fetch(`/api/messages?before=${beforeMessageKey}&limit=${limit}`)
const data = await response.json()
// 返回消息数组
return data.messages.map(msg => ({
key: msg.id,
text: msg.content,
user: msg.sender === 'currentUser' ? 'me' : 'other',
timestamp: new Date(msg.createdAt),
}))
} catch (error) {
console.error('加载历史消息失败:', error)
return [] // 返回空数组表示加载完成
}
}, [])
return (
<MessageList
onLoadHistory={handleLoadHistory}
historyLoadLimit={20}
loadThreshold={100}
/>
)
}
游标分页说明
beforeMessageKey
: 游标位置,加载此消息之前的历史记录limit
: 每次加载的消息数量- 返回空数组
[]
表示没有更多历史消息
🤖 AI 聊天集成
流式回复示例
function AIChatApp() {
const messageListRef = useRef<MessageListMethods>(null)
const handleAIChat = async (question: string) => {
// 发送用户问题
messageListRef.current?.sendMessage(question)
// 创建 AI 回复占位符
const botMessageKey = messageListRef.current?.receiveMessage('正在思考...')
try {
// 模拟流式回复
const responses = [
'让我想想...',
'让我想想...这是一个很好的问题',
'让我想想...这是一个很好的问题,我来为你详细解答。'
]
for (let i = 0; i < responses.length; i++) {
await new Promise(resolve => setTimeout(resolve, 500))
messageListRef.current?.updateMessage(botMessageKey!, responses[i])
}
} catch (error) {
messageListRef.current?.updateMessage(botMessageKey!, '抱歉,出现了错误')
}
}
return (
<div>
<MessageList ref={messageListRef} />
<button onClick={() => handleAIChat('你好,你是谁?')}>
测试 AI 回复
</button>
</div>
)
}
🎨 自定义样式
基础样式定制
<MessageList
className="border-2 border-blue-200 shadow-lg"
style={{
borderRadius: '16px',
background: 'linear-gradient(to bottom, #f8fafc, #ffffff)'
}}
/>
高级样式定制(待开发)
通过 CSS 变量覆盖默认样式:
.message-list-container {
--message-bg-me: #3b82f6;
--message-bg-other: #f3f4f6;
--message-text-me: #ffffff;
--message-text-other: #1f2937;
}
📖 使用示例
聊天室应用
import { useState, useRef, useCallback } from 'react'
import MessageList, { type MessageListMethods, type Message } from '@aiwanna-team/message-list'
function ChatRoom() {
const [inputValue, setInputValue] = useState('')
const messageListRef = useRef<MessageListMethods>(null)
const handleSend = () => {
if (inputValue.trim()) {
messageListRef.current?.sendMessage(inputValue)
setInputValue('')
}
}
const handleLoadHistory = useCallback(async (beforeKey, limit) => {
// 实现你的历史消息加载逻辑
const messages = await loadHistoryFromAPI(beforeKey, limit)
return messages
}, [])
return (
<div className="flex flex-col h-screen max-w-2xl mx-auto">
<div className="flex-1 p-4">
<MessageList
ref={messageListRef}
height="100%"
onLoadHistory={handleLoadHistory}
/>
</div>
<div className="p-4 border-t flex gap-2">
<input
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && handleSend()}
className="flex-1 px-3 py-2 border rounded-lg"
placeholder="输入消息..."
/>
<button
onClick={handleSend}
className="px-4 py-2 bg-blue-500 text-white rounded-lg"
>
发送
</button>
</div>
</div>
)
}
🧪 开发和测试
运行 Storybook
# 克隆仓库
git clone <repository-url>
cd message-list
# 安装依赖
npm install
# 启动 Storybook
npm run storybook
可用的演示
- Default - 基础使用演示
- WithInitialMessages - 初始消息展示
- Interactive - 交互式功能演示
- HistoryLoading - 历史消息加载演示
- Performance - 性能压力测试
- CustomStyle - 自定义样式演示
⚡ 性能优化
最佳实践
- 合理设置历史加载数量
<MessageList
historyLoadLimit={30} // 根据网络状况调整
loadThreshold={150} // 调整触发距离
/>
- 使用 useCallback 优化回调
const handleLoadHistory = useCallback(async (beforeKey, limit) => {
// 实现逻辑
}, [/* 最小依赖 */])
- 避免频繁的消息更新
// 批量更新而不是逐条更新
const batchUpdate = (updates) => {
updates.forEach(({ key, text }) => {
messageListRef.current?.updateMessage(key, text)
})
}
🔧 故障排除
常见问题
Q: 样式不显示或错乱?
A: 最常见的原因是没有在 CSS 中添加 @source
指令。请确保:
1. 在主 CSS 文件中添加了 @source "../node_modules/@aiwanna-team/message-list";
2. 项目使用的是 TailwindCSS 4.0+
3. 检查控制台是否有 TailwindCSS 相关的错误信息
Q: 历史消息加载不触发?
A: 检查 onLoadHistory
回调是否正确设置,确保有足够的初始消息支持滚动
Q: 消息更新不生效?
A: 确保使用正确的消息 key,检查 updateMessage
调用
Q: 性能问题?
A: 调整 historyLoadLimit
和虚拟滚动配置,避免一次性加载过多消息
📄 许可证
MIT License
🤝 贡献
欢迎提交 Issue 和 Pull Request!
官方文档: 查看 Storybook 演示 GitHub: aiwanna-team/message-list