Next框架学习篇 ✅
写在最前:好难啊我走召🤯🤯🤯
目录&待解决的问题
next版本:13.5.1
node版本:22.15.0
🔗 Next中文文档
目录
1️⃣ 页面
2️⃣ 应用级路由
3️⃣ CSS相关
待解决的问题
1️⃣ 如何做嵌套路由
2️⃣ 如何做动态路由表
3️⃣ 如何集成mobx
4️⃣ 如何做客户端渲染
页面
假设在 /next_project/pages/home.tsx下我们export default
了这样一个组件:
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:
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:
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;
后续我们每次请求http://localhost:3000/home的时候,两次console.log()
都是在服务端执行的(开发模式下即为VSCode),且每次对http://localhost:3000/home的请求返回的响应状态码都是200(说明是在请求时实时返回的,走不了缓存;如果用的是静态生成,从第二次请求开始返回的便是304)
需要重申的是,不管是静态生成的HTML还是服务端渲染后返回的HTML都可以依赖外部数据来填充HTML页面,具体做法可参考Next文档中此章的内容。
应用级路由
路由嵌套 & 重定向
需求:想在Next中构建一个带左侧导航栏和右侧内容区的“后台管理布局”,并通过导航切换子页面,如admin/dict和admin/notice,并将后台首页重定向到admin/notice。
直接开撸:
// 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相关
有空再写