Next.js 与 Node.js 全栈应用开发:API设计、数据库连接、身份验证 | 2024版
<p data-source-line="3">书接上回,到目前为止,您的应用程序只有一个主页。让我们学习如何使用布局和页面创建更多路线。</p><p data-source-line="5">在本章之中我们需要讨论:</p>
<ul data-source-line="6">
<li>dashboard使用文件系统路由创建路由。</li>
<li>了解创建新路线段时文件夹和文件的作用。</li>
<li>创建可以在多个仪表板页面之间共享的嵌套布局。</li>
<li>了解什么是共置、部分渲染和根布局。</li>
</ul>
<h2 id="嵌套路由" data-source-line="11">嵌套路由</h2>
<p data-source-line="12">Next.js 使用文件系统路由,其中文件夹用于创建嵌套路由。每个文件夹代表一个映射到URL 段的路由段。<br><img src="https://pic.dataeast.cn/picture/20241011102737.png?ynotemdtimestamp=1728887240937" data-processed="https%3A%2F%2Fpic.dataeast.cn%3A443%2Fpicture%2F20241011102737.png"><br>layout.tsx您可以使用和文件为每条路线创建单独的 UI page.tsx。<br>page.tsx是一个特殊的 Next.js 文件,它导出一个 React 组件,它是路由可访问所必需的。在您的应用程序中,您已经有一个页面文件:<code>/app/page.tsx</code>- 这是与路由关联的主页<code>/</code>。<br>要创建嵌套路由,您可以将文件夹嵌套在一起并<code>page.tsx</code>在其中添加文件。例如:<br><img src="https://pic.dataeast.cn/picture/20241011102935.png?ynotemdtimestamp=1728887240937" data-processed="https%3A%2F%2Fpic.dataeast.cn%3A443%2Fpicture%2F20241011102935.png"><br>/app/dashboard/page.tsx与路径相关联/dashboard。<br>让我们创建页面来看看它是如何工作的!</p>
<h2 id="创建仪表板页面" data-source-line="21">创建仪表板页面</h2>
<p data-source-line="22">在 /app 内创建一个名为仪表板的新文件夹。然后,在dashboard文件夹中创建一个新的 page.tsx 文件,其中包含以下内容:<br><img src="https://pic.dataeast.cn/picture/20241011103742.png?ynotemdtimestamp=1728887240937" data-processed="https%3A%2F%2Fpic.dataeast.cn%3A443%2Fpicture%2F20241011103742.png"><br>然后启动我们的网站 <code>pnpm run dev</code><br><img src="https://pic.dataeast.cn/picture/20241011103840.png?ynotemdtimestamp=1728887240937" data-processed="https%3A%2F%2Fpic.dataeast.cn%3A443%2Fpicture%2F20241011103840.png"><br>访问网站地址,就可以看到该目录下的页面啦 <code>http://172.16.100.104/dashboard</code><br><img src="https://pic.dataeast.cn/picture/20241011103958.png?ynotemdtimestamp=1728887240937" data-processed="https%3A%2F%2Fpic.dataeast.cn%3A443%2Fpicture%2F20241011103958.png"></p>
<p data-source-line="29">这是在Next.js中创建不同页面的方法:使用文件夹创建新的路由段,并在其中添加页面文件。通过为页面文件指定一个特殊的名称,Next.js允许你将UI组件、测试文件和其他相关代码与你的路由放在一起。只有页面文件中的内容才会被公开访问。例如,/ui和/lib目录与你的路由一起放在/app目录中。</p>
<h2 id="创建仪表板布局" data-source-line="31">创建仪表板布局</h2>
<p data-source-line="33">在 Next.js 中,您可以使用 layout.tsx文件来创建可在多个页面之间共享的UI组件。</p>
<p data-source-line="35">首先我们创建两个页面,分别为客户页面(customers)和发票页面 (invoices),并且创建默认页面page.tsx<br>路径:app/dashboard/customers/page.tsx</p>
<pre data-source-line="37"><code class="hljs language-tsx"><span class="hljs-keyword">export <span class="hljs-keyword">default <span class="hljs-keyword">function <span class="hljs-title function_">Page(<span class="hljs-params">) {<br> <span class="hljs-keyword">return <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">p><span class="zh-hans">客户 <span class="zh-hans">页面<span class="hljs-tag"></<span class="hljs-name">p>;<br>}<br></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p data-source-line="42">路径:app/dashboard/invoices/page.tsx</p>
<pre data-source-line="43"><code class="hljs language-tsx"><span class="hljs-keyword">export <span class="hljs-keyword">default <span class="hljs-keyword">function <span class="hljs-title function_">Page(<span class="hljs-params">) {<br> <span class="hljs-keyword">return <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">p><span class="zh-hans">发票 <span class="zh-hans">页面<span class="hljs-tag"></<span class="hljs-name">p>;<br>}<br></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p data-source-line="48"><img src="https://pic.dataeast.cn/picture/20241011104931.png?ynotemdtimestamp=1728887240937" data-processed="https%3A%2F%2Fpic.dataeast.cn%3A443%2Fpicture%2F20241011104931.png"><br>对于 dashboard 页面,您可以创建一个layout.tsx文件,并在其中定义导航栏和其他共享的 UI 组件。</p>
<p data-source-line="51">首先在ui文件夹上面也创建一个dashboard路径,并且创建导航栏组件,因为通过分路径,我们能够清晰的分别哪个组件来自于哪里,创建导航文件 sidenav.tsx和nav-links.tsx。</p>
<p data-source-line="53">sidenav.tsx</p>
<pre><code data-source-line="54">import Link from "next/link";
import NavLinks from "@/app/ui/dashboard/nav-links";
import AcmeLogo from "@/app/ui/acme-logo";
import { HomeIcon } from "@heroicons/react/24/outline";
export default function SideNav() {
return (
<div className="flex h-full flex-col px-3 py-4 md:px-2">
<Link
className="mb-2 flex h-20 items-end justify-start rounded-md bg-blue-600 p-4 md:h-40"
href="/"
>
<div className="w-32 text-white md:w-40">
<AcmeLogo />
</div>
</Link>
<div className="flex grow flex-row justify-between space-x-2 md:flex-col md:space-x-0 md:space-y-2">
<NavLinks />
<div className="hidden h-auto w-full grow rounded-md bg-gray-50 md:block"></div>
<Link
href="/"
className="flex h- w-full grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3"
>
<HomeIcon className="w-6" />
<div className="hidden md:block">返回首页</div>
</Link>
</div>
</div>
);
}
</code></pre>
<p data-source-line="88">nav-links.tsx</p>
<pre data-source-line="89"><code class="hljs language-tsx"><span class="hljs-string">'use client';<br><br><span class="hljs-keyword">import {<br> <span class="hljs-title class_">UserGroupIcon,<br> <span class="hljs-title class_">HomeIcon,<br> <span class="hljs-title class_">DocumentDuplicateIcon,<br>} <span class="hljs-keyword">from <span class="hljs-string">'@heroicons/react/24/outline';<br><span class="hljs-keyword">import <span class="hljs-title class_">Link <span class="hljs-keyword">from <span class="hljs-string">'next/link';<br><span class="hljs-keyword">import { usePathname } <span class="hljs-keyword">from <span class="hljs-string">'next/navigation';<br><span class="hljs-keyword">import clsx <span class="hljs-keyword">from <span class="hljs-string">'clsx';<br><br><span class="hljs-comment">// Map of links to display in the side navigation.<br><span class="hljs-comment">// Depending on the size of the application, this would be stored in a database.<br><span class="hljs-keyword">const links = [<br> { <span class="hljs-attr">name: <span class="hljs-string">'Home', <span class="hljs-attr">href: <span class="hljs-string">'/dashboard', <span class="hljs-attr">icon: <span class="hljs-title class_">HomeIcon },<br> {<br> <span class="hljs-attr">name: <span class="hljs-string">'Invoices',<br> <span class="hljs-attr">href: <span class="hljs-string">'/dashboard/invoices',<br> <span class="hljs-attr">icon: <span class="hljs-title class_">DocumentDuplicateIcon,<br> },<br> { <span class="hljs-attr">name: <span class="hljs-string">'Customers', <span class="hljs-attr">href: <span class="hljs-string">'/dashboard/customers', <span class="hljs-attr">icon: <span class="hljs-title class_">UserGroupIcon },<br>];<br><br><span class="hljs-keyword">export <span class="hljs-keyword">default <span class="hljs-keyword">function <span class="hljs-title function_">NavLinks(<span class="hljs-params">) {<br> <span class="hljs-keyword">const pathname = <span class="hljs-title function_">usePathname();<br><br> <span class="hljs-keyword">return (<br> <span class="language-xml"><span class="hljs-tag"><><br> {links.map((link) => {<br> const LinkIcon = link.icon;<br> return (<br> <span class="hljs-tag"><<span class="hljs-name">Link<br> <span class="hljs-attr">key=<span class="hljs-string">{link.name}<br> <span class="hljs-attr">href=<span class="hljs-string">{link.href}<br> <span class="hljs-attr">className=<span class="hljs-string">{clsx(<br> '<span class="hljs-attr">flex <span class="hljs-attr">h-[<span class="hljs-attr">48px] <span class="hljs-attr">grow <span class="hljs-attr">items-center <span class="hljs-attr">justify-center <span class="hljs-attr">gap-2 <span class="hljs-attr">rounded-md <span class="hljs-attr">bg-gray-50 <span class="hljs-attr">p-3 <span class="hljs-attr">text-sm <span class="hljs-attr">font-medium <span class="hljs-attr">hover:bg-sky-100 <span class="hljs-attr">hover:text-blue-600 <span class="hljs-attr">md:flex-none <span class="hljs-attr">md:justify-start <span class="hljs-attr">md:p-2 <span class="hljs-attr">md:px-3',<br> {<br> '<span class="hljs-attr">bg-sky-100 <span class="hljs-attr">text-blue-600'<span class="hljs-attr">: <span class="hljs-attr">pathname === <span class="hljs-string">link.href,<br> },<br> )}<br> ><br> <span class="hljs-tag"><<span class="hljs-name">LinkIcon <span class="hljs-attr">className=<span class="hljs-string">"w-6" /><br> <span class="hljs-tag"><<span class="hljs-name">p <span class="hljs-attr">className=<span class="hljs-string">"hidden md:block">{link.name}<span class="hljs-tag"></<span class="hljs-name">p><br> <span class="hljs-tag"></<span class="hljs-name">Link><br> );<br> })}<br> <span class="hljs-tag"></><br> );<br>}<br></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p data-source-line="140">按照我的方法引入之后,nav-links.tsx会出现<code>Cannot find module 'clsx' or its corresponding type declarations.ts(2307)</code><br><img src="https://pic.dataeast.cn/picture/20241011110604.png?ynotemdtimestamp=1728887240937" data-processed="https%3A%2F%2Fpic.dataeast.cn%3A443%2Fpicture%2F20241011110604.png"><br>在终端上面输入<code>pnpm install clsx</code>,然后重启ts服务器即可,如何重启可以观看我的第4章内容(Next.js 零基础教程4|2024最新更新中|曲速引擎 Warp Drive)<br><img src="https://pic.dataeast.cn/picture/20241011110656.png?ynotemdtimestamp=1728887240937" data-processed="https%3A%2F%2Fpic.dataeast.cn%3A443%2Fpicture%2F20241011110656.png"></p>
<p data-source-line="145">都创建完成之后我们来解析一下这两个文件什么意思,<code>sidenav.tsx</code>这个组件创建了一个响应式的侧边导航栏,包含一个logo、导航链接和一个返回首页的按钮。它在移动设备和桌面设备上有不同的布局和显示方式。这种设计常见于现代的Web应用程序,特别是管理面板或仪表板类型的界面。</p>
<p data-source-line="147">而<code>nav-links.tsx</code>这个组件的主要功能是:根据预定义的链接数据创建导航链接,使用适当的图标和文本标签,根据当前页面路径高亮显示活动链接,响应式设计,适应不同屏幕尺寸。这种设计常见于现代Web应用的侧边导航栏,特别是在管理面板或仪表板中。它提供了一个清晰、一致的导航结构,同时保持了良好的用户体验和视觉吸引力。</p>
<p data-source-line="149">问题:为什么页面上面要有个<code>"use client";</code> ?</p>
<ul data-source-line="150">
<li>"use client"; 指令是Next.js13及以后版本引入的一个重要概念,用于区分服务器组件(Server Components)和客户端组件(Client Components)。</li>
</ul>
<p data-source-line="152">问题:nav-links.tsx里面使用的函数比较多,都是什么意思</p>
<ul data-source-line="153">
<li><code>const pathname = usePathname();</code>这是一个Next.js提供的hook,用于获取当前页面的路径。它在组件重新渲染时更新,允许组件根据当前 URL 路径做出响应。</li>
<li><code>links.map() </code>函数:<code>links.map((link) => {// ... 渲染逻辑 })}</code> 这个函数遍历 links 数组,为每个链接项创建一个 Link 组件。</li>
<li><code>clsx</code> 函数: 它接受多个参数,可以是字符串、对象或数组。在这里,它用于根据当前路径是否匹配链接的 href 来添加额外的样式类。<br>都创建完成之后结构是这样的<br><img src="https://pic.dataeast.cn/picture/20241011111734.png?ynotemdtimestamp=1728887240937" data-processed="https%3A%2F%2Fpic.dataeast.cn%3A443%2Fpicture%2F20241011111734.png"></li>
</ul>
<p data-source-line="159">接下来我们回到app/dashboard/layout.tsx文件夹,然后写入这个代码即可引用导航栏</p>
<pre data-source-line="161"><code class="hljs language-tsx"><span class="hljs-keyword">import <span class="hljs-title class_">SideNav <span class="hljs-keyword">from <span class="hljs-string">"@/app/ui/dashboard/sidenav";<br><br><span class="hljs-keyword">export <span class="hljs-keyword">default <span class="hljs-keyword">function <span class="hljs-title function_">Layout(<span class="hljs-params">{ children }: { children: React.ReactNode }) {<br> <span class="hljs-keyword">return (<br> <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">div <span class="hljs-attr">className=<span class="hljs-string">"flex h-screen flex-col md:flex-row md:overflow-hidden"><br> <span class="hljs-tag"><<span class="hljs-name">div <span class="hljs-attr">className=<span class="hljs-string">"w-full flex-none md:w-64"><br> <span class="hljs-tag"><<span class="hljs-name">SideNav /><br> <span class="hljs-tag"></<span class="hljs-name">div><br> <span class="hljs-tag"><<span class="hljs-name">div <span class="hljs-attr">className=<span class="hljs-string">"grow p-6 md:overflow-y-auto md:p-12">{children}<span class="hljs-tag"></<span class="hljs-name">div><br> <span class="hljs-tag"></<span class="hljs-name">div><br> );<br>}<br></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p data-source-line="175"><img src="https://pic.dataeast.cn/picture/20241011112025.png?ynotemdtimestamp=1728887240937" data-processed="https%3A%2F%2Fpic.dataeast.cn%3A443%2Fpicture%2F20241011112025.png"></p>
<p data-source-line="177">在 Next.js 中使用布局的一个好处是,在导航时,只有页面组件会更新,而布局不会重新渲染。这称为部分渲染:<br><img src="https://pic.dataeast.cn/picture/20241011112310.png?ynotemdtimestamp=1728887240937" data-processed="https%3A%2F%2Fpic.dataeast.cn%3A443%2Fpicture%2F20241011112310.png"></p>
<p data-source-line="180">在前面章节中,将Inter字体导入了另一个布局:/app/layout.tsx。提醒一下:这称为根布局,是必需的。您添加到根布局的任何UI都将在应用程序的所有页面之间共享。您可以使用根布局来修改和标签,并添加元数据(您将在后面的章节<body>中了解有关元数据的更多信息)。由于刚刚创建的新布局(/app/dashboard/layout.tsx)对于仪表板页面来说是唯一的,因此您不需要向上面的根布局添加任何 UI。</p>
<p data-source-line="182">好了,第五章结束,更多详细内容正在更新请关注,我喜欢的不要忘记点赞和转发哦!</p>
<p data-source-line="184">更详细内容查看</p>
<blockquote data-source-line="185">
<p>独立博客 https://www.dataeast.cn/<br>CSDN博客 https://blog.csdn.net/siberiaWarpDrive<br>B站视频空间 https://space.bilibili.com/25871614?spm_id_from=333.1007.0.0<br>关注 “曲速引擎 Warp Drive” 微信公众号<br><img src="https://pic.dataeast.cn/picture/20241016170749.png?ynotemdtimestamp=1728887240937" data-processed="https%3A%2F%2Fpic.dataeast.cn%3A443%2Fpicture%2F20241016170749.png"></p>
</blockquote><br><br>
来源:https://www.cnblogs.com/XiaoH160309/p/18472554
頁:
[1]