++写在最前:好难啊我走召🤯🤯🤯++
目录&待解决的问题
next版本:13.5.1
node版本:22.15.0
🔗 Next中文文档
目录
[1️⃣ 页面](#1️⃣ 页面)
[2️⃣ 应用级路由](#2️⃣ 应用级路由)
[3️⃣ CSS相关](#3️⃣ CSS相关)
待解决的问题
[1️⃣ 如何做嵌套路由](#1️⃣ 如何做嵌套路由)
[2️⃣ 如何做动态路由表](#2️⃣ 如何做动态路由表)
[3️⃣ 如何集成mobx](#3️⃣ 如何集成mobx)
[4️⃣ 如何做客户端渲染](#4️⃣ 如何做客户端渲染)
页面
假设在 /next_project/pages/home.tsx下我们export default
了这样一个组件:
ts
const HomePage = () => {
return <div>HomePage</div>;
};
export default HomePage;
此时我们便可以在本地的http://localhost:3000/home中查看此组件,这便是使用Next脚手架搭建的应用中最基础的页面与组件的映射关系。这种约定方式很像微信小程序开发,也很像Egg。
此外,Next具有静态生成 和服务端渲染 这两种预渲染 模式。静态生成是Next推荐的最佳实践,即指HTML在应用构建时生成,并在每次页面请求时通过浏览器缓存被复用(CDN可以在没有额外配置的情况下缓存静态生成的页面以提高性能);服务端渲染指的是在每次请求时重新生成HTML。
静态生成
默认情况下,Next.js 使用静态生成来预渲染页面,正如上述Demo所编写的那样。此外,Next官方的文档中还提供了需要外部数据的静态生成的页面 的做法,此处只将其与客户端渲染进行对比,因为煮啵始终觉得既然走到了"需要外部数据"这个份上了,那页面就具有实时性的需求,应该用服务端渲染或者客户端渲染才合理。 见下面Demo:
js
import { useEffect, useState } from "react";
interface PageProps { posts: Array<{ title: string }> }
const UserPage = (props: PageProps) => {
const { posts: postsA } = props; // postsA在Next静态生成时注入
const [postsB, setPostsB] = useState<Array<{ title: string }>>([]);
useEffect(() => {
setPostsB([{ title: "Post 3" }, { title: "Post 4" }]);
}, []);
return (
<div className="user-page">
<h1>User Page</h1>
{postsA.map((post) => <div>{post.title}</div>)}
{postsB.map((post) => <div>{post.title}</div>)}
</div>
);
};
// 此函数会在构建的时候执行一次
export async function getStaticProps() {
const result = await new Promise((res) => {
const posts = [{ title: "Post 1" }, { title: "Post 2" }];
res(posts);
});
return { props: { posts: result } };
}
export default UserPage;
Q1:"静态生成"生成的到底是啥玩意?
A1:生成的是"可预见的DOM"。 说了跟白说一样哈哈。 对比我们熟悉的用Vite构建的单页面应用或许更好理解,这种时候我们请求的HTML文件中通常只是一个id为root的div,这种HTML通常只是一个"空壳"。但是在Next的静态构建中我们请求的HTML文件中却出现了用postsA
来填充的多个div,这部分DOM是不依赖于"水合"这个过程,就可以事先渲染出来的;但是用postsB
来填充的多个div并没有出现在HTML文件中,其行为更像我们熟悉的"客户端渲染"。
区别 | Next静态生成 | Vite构建的SPA应用 |
---|---|---|
HTML中是否含有页面实际内容 | ✅ 有完整的UI内容 | ❌ 通常只是一个id为root的div |
首屏是否无需JS即可看到UI | ✅ 可以,即使可能不完整 | ❌ 看不到 |
是否能用SEO抓取 | ✅ 很适合 | ❌ 几乎抓不到 |
是否支持预渲染多页面 | ✅ 多页面静态生成 | ❌ 本质只有一个HTML + JS |
服务端渲染
在Next中我们可以通过实现getServerSideProps
函数来实现服务端渲染,详见下面的Demo:
ts
const HomePage = ({ data }: any) => {
return (
<div>
<div>name: {data.name}</div>
<div>age: {data.age}</div>
</div>
);
}
export async function getServerSideProps() {
// 每次请求页面时,都会执行此函数
console.log("getServerSideProps");
const result = await new Promise((res) => {
const data = { name: "Pro", age: 25 };
res(data);
});
console.log("result", result);
return { props: { data: result } };
}
export default HomePage;
需要重申的是,不管是静态生成的HTML还是服务端渲染后返回的HTML都可以依赖外部数据来填充HTML页面,具体做法可参考Next文档中此章的内容。
应用级路由
路由嵌套 & 重定向
需求:想在Next中构建一个带左侧导航栏和右侧内容区的"后台管理布局",并通过导航切换子页面,如admin/dict和admin/notice,并将后台首页重定向到admin/notice。
直接开撸:
ts
// pages/admin/index.tsx
import Link from "next/link";
import { ReactNode, useEffect } from "react";
import { useRouter } from "next/router";
export default function AdminLayout({ children }: { children: ReactNode }) {
const router = useRouter();
useEffect(() => {
// 页面加载后立即跳转到公告管理
router.pathname === "/admin" && router.replace("/admins/notice");
}, []);
return (
<div style={{ display: "flex", height: "100vh" }}>
{/* 左侧导航栏 */}
<div style={{ width: 200, background: "#f2f2f2", padding: 20 }}>
<nav style={{ display: "flex", flexDirection: "column", gap: 10 }}>
<Link href="/admins/dict">词典管理</Link>
<Link href="/admins/notice">公告管理</Link>
</nav>
</div>
{/* 右侧内容 */}
<div style={{ flex: 1, padding: 20 }}>{children}</div>
</div>
);
}
// pages/admin/notice.tsx
import AdminLayout from ".";
const NoticePage = () => {
return <AdminLayout>Notice Page</AdminLayout>;
};
export default NoticePage;
// pages/admin/dict.tsx
import AdminLayout from ".";
const DictPage = () => {
return <AdminLayout>Dict Manege</AdminLayout>;
};
export default DictPage;
路由守卫
虽然Next的路由是文件系统驱动的,且没有像react-router那样的显式路由表,但Next仍提供了多种路由守卫方式。但这部分内容的讨论脱离了煮啵想学的"服务端渲染"的内容,更偏向于实际开发的应用,后续开发到这中需求再详细补充,此处仅对几种组件内的守卫方法做简单介绍。其实就是懒。
控制方式 | 触发时机 | 用法举例 | 适用场景 |
---|---|---|---|
页面组件内守卫 | 客户端 | useEffect + 跳转 |
判断登陆态,客户端重定向 |
getServerSideProps |
服务端 | 判断cookie/token,重定向或403 | 页面级SSR权限控制 |
Next中间件middleware.ts |
请求到达前 | 在Edge上做重定向拦截 | 全局路径守卫,高性能 |
懒加载
Next已经实现了基于路由的自动懒加载 ,以上述Demo为例,只要还未进入到admin/dict页面,那么pages/admin/dict.tsx的代码便不会被加载。此处或许还有更高级的用法,后续见到了再补充吧。
CSS相关
有空再写