跳至主要內容

Vercel-Chat学习

小企鹅笔记前端NextjsNextjs大约 3 分钟约 860 字...

Vercel-Chat学习

Github:https://github.com/vercel/ai-chatbot/tree/mainopen in new window

目录

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,它返回两个状态变量:isSidebarOpenisLoading。这两个变量用来控制侧边栏的显示和加载状态。

    • return 语句中,我们创建了一个 div 元素,它具有以下属性:

      • data-state: 根据 isSidebarOpenisLoading 的值,将 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>
      

      在上面的例子中:

      1. <React.Suspense> 标签包裹了需要异步加载的组件 <MyAsyncComponent />
      2. 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
评论
  • 按正序
  • 按倒序
  • 按热度