Vercel-Chat学习
Vercel-Chat学习
Github:https://github.com/vercel/ai-chatbot/tree/main
目录
app
(chat)
()
是为了处理组件隔离的问题,该文件夹下的组件只对当前文件夹下的页面生效
layout.tsx
常规写法,放了一个
<SidebarDesktop />
export default async function ChatLayout({ children }: ChatLayoutProps) { return ( <div className="relative flex h-[calc(100vh_-_theme(spacing.16))] overflow-hidden"> <SidebarDesktop /> {children} </div> ) }
<SidebarDesktop />
中有一个<Sidebar>
export async function SidebarDesktop() { const session = await auth() if (!session?.user?.id) { return null } return ( <Sidebar className="peer absolute inset-y-0 z-30 hidden -translate-x-full border-r bg-muted duration-300 ease-in-out data-[state=open]:translate-x-0 lg:flex lg:w-[250px] xl:w-[300px]"> {/* @ts-ignore */} <ChatHistory userId={session.user.id} /> </Sidebar> ) }
<Sidebar>
export function Sidebar({ className, children }: SidebarProps) { const { isSidebarOpen, isLoading } = useSidebar() return ( <div data-state={isSidebarOpen && !isLoading ? 'open' : 'closed'} className={cn(className, 'h-full flex-col dark:bg-zinc-950')} > {children} </div> ) }
useSidebar()
是一个自定义的 React hook,它返回两个状态变量:isSidebarOpen
和isLoading
。这两个变量用来控制侧边栏的显示和加载状态。在
return
语句中,我们创建了一个div
元素,它具有以下属性:data-state
: 根据isSidebarOpen
和isLoading
的值,将data-state
属性设置为'open'
或'closed'
。这可能用于在 CSS 中根据侧边栏的状态应用不同的样式。className
: 这个属性使用cn()
函数来结合传入的className
和一些默认的样式类名。主要包括'h-full'
(占满父容器高度)、'flex-col'
(垂直排列子元素)和'dark:bg-zinc-950'
(在暗色主题下设置背景色)。children
: 这是所有传入组件的子元素,它们将被渲染在这个div
内部。
<Sidebar>
又嵌套了一个<ChatHistory>
export async function ChatHistory({ userId }: ChatHistoryProps) { return ( <div className="flex flex-col h-full"> <div className="flex items-center justify-between p-4"> <h4 className="text-sm font-medium">Chat History</h4> </div> <div className="mb-2 px-2"> <Link href="/" className={cn( buttonVariants({ variant: 'outline' }), 'h-10 w-full justify-start bg-zinc-50 px-4 shadow-none transition-colors hover:bg-zinc-200/40 dark:bg-zinc-900 dark:hover:bg-zinc-300/10' )} > <IconPlus className="-translate-x-2 stroke-2" /> New Chat </Link> </div> <React.Suspense fallback={ <div className="flex flex-col flex-1 px-4 space-y-4 overflow-auto"> {Array.from({ length: 10 }).map((_, i) => ( <div key={i} className="w-full h-6 rounded-md shrink-0 animate-pulse bg-zinc-200 dark:bg-zinc-800" /> ))} </div> } > {/* @ts-ignore */} <SidebarList userId={userId} /> </React.Suspense> </div> ) }
</React.Suspense>
是 React 中的一个标签, 用于包裹需要异步加载的组件或资源。React.Suspense
是 React 16.6 版本引入的新特性, 它允许应用程序在等待异步资源加载完成时显示 fallback UI (加载中的状态)。这个标签的用法如下:
jsx
复制
<React.Suspense fallback={<div>Loading...</div>}> {/* 需要异步加载的组件或资源 */} <MyAsyncComponent /> </React.Suspense>
在上面的例子中:
<React.Suspense>
标签包裹了需要异步加载的组件<MyAsyncComponent />
。fallback
属性指定了在组件还在加载时显示的 UI, 这里显示了一个简单的 "Loading..." 文本。
当 React 渲染
<MyAsyncComponent />
时, 如果组件还未完成加载, React 会暂停渲染并显示fallback
指定的 UI, 直到组件准备就绪。
<SidebarList>
const loadChats = cache(async (userId?: string) => { return await getChats(userId) }) export async function SidebarList({ userId }: SidebarListProps) { const chats = await loadChats(userId) return ( <div className="flex flex-1 flex-col overflow-hidden"> <div className="flex-1 overflow-auto"> {chats?.length ? ( <div className="space-y-2 px-2"> <SidebarItems chats={chats} /> </div> ) : ( <div className="p-8 text-center"> <p className="text-sm text-muted-foreground">No chat history</p> </div> )} </div> <div className="flex items-center justify-between p-4"> <ThemeToggle /> <ClearHistory clearChats={clearChats} isEnabled={chats?.length > 0} /> </div> </div> ) }
- 其中
getChats(userId)
会从缓存中拿到历史列表 <SidebarItems chats={chats} />
则会将列表渲染出来
- 其中
- 0
- 0
- 0
- 0
- 0
- 0