type
status
date
slug
summary
tags
category
icon
password
1. 前言
Next.js 14 提供了新的 App Router 作为默认的路由方案,文件夹的嵌套结构决定了路由的渲染或请求的返回处理。太棒了这逻辑清晰,再也不必手动配置 router 了,开发一路畅通了,家人们。
这简直就是危言耸听!那文档翻起来无异于海底捞针。
本文分为上下两篇,上篇主要以路由为核心,下篇补充网络请求以及各杂项。
2. 路由页面
例如有以下路由页面:
访问路径 | 页面组件 | 名称 |
/ | app/page.js | 主页 |
/blogs | app/blogs | 文章列表 |
/blogs/1 | app/blogs/[id]/page.js | 文章详情(使用id) |
/blogs/hello-next | app/blogs/[slug]/page.js | 文章详情(使用slug) |
/login | app/(auth)/login/page.js | 登录 |
/forget-password | app/(auth)/forget-password | 忘记密码 |
登录和忘记密码使用(auth)进行逻辑分组,不影响路径访问。
在路由渲染方面,还提供了诸如 layout.js、loading.js、error.js、not-found.js 以配置布局页面、加载页面、错误反馈页面、404页面。
3. 路由处理器
除了页面渲染,还有路由处理器(Route Handler)。笔者在使用这部分特性时摔过不少跟头,它不如 Pages Router 的 API Routes 那么直观。
假设有以下 API 接口:
- GET api/v1/blogs
- GET api/v1/blogs/1
- POST api/v1/login { email, password }
- GET api/v1/blogs?q=xxx
- GET api/v1/blogs?q=xxx&&start=1&&end=2
在 route.js 中定义路由处理器:
每一个方法都有 request 参数,从中可以解析出所需的内容。请求对象的类型可以是 Request 或者 NextRequest,后者是扩展了前者功能的类型。在实际开发中,笔者使用的是后者。同理,响应对象的类型有 Response 和 NextResponse。
3.1 body
body 数据藏在
request.json()
中。3.2 query && pathname
查询字符串藏在
request.nextUrl.searchParams
中。- 如果是单个查询字符串:
- 如果是多个查询字符串:
pathname 藏在
request.nextUrl.pathname
下:3.3 params
如果想要获取路由的动态参数 [id] 或 [slug],咋办?
这些数据就藏在路由处理器中的第二个参数中:
3.4 cookies
cookie 在用户登录时在响应豹纹中返回给前端,这在《图解HTTP》书中有形象的描述:
对应的豹纹如下:
可以看到,响应豹纹中通过 Set-Cookie 设置了 sid,以后客户端请求中自动携带了 Cookie,里面就放着 sid 数据。同理,cookie 里可以放 token。
有的接口需要把 token 信息传给服务器,因此获取 cookie 就变得尤为重要。在请求头和响应头中都可以获取到 Cookie,这里以请求头为例,利用
request.cookies.get('token')
:如上,拿到 token 的值设置到请求头参数 Authorization 中。
从
next/headers
可以得到 cookie
方法, 删除的方式如下:3.5 headers
在 3.1 中可以看到,在返回 json 数据时可以设置 headers。
如果想要获取 headers 信息(只读),从
next/headers
可以得到 headers
方法:3.6 重定向
4. 在服务端组件中获取 URL 参数
在路由处理器中,可以从 request 中拿到很多东西,而到了服务端组件是没有 request 对象的。
在 page.js 中呈现的是某一路由的组件,从 props 中可以解构出 params 和 searchParams:
5. 在客户端组件中获取 URL 参数
以上是在服务端组件中获取参数的方式,而在客户端组件中利用客户端 hook 获取。
5.1 useRouter()
控制路由跳转、重定向等。
router.push(href: string, { scroll: boolean })
:对提供的路由执行客户端导航。在浏览器的历史堆栈中添加一个新条目。(可以用<Link>
组件代替。)
router.replace(href: string, { scroll: boolean })
: 执行指向所提供路由的客户端导航,但不在浏览器历史堆栈中添加新条目。
router.refresh()
:刷新当前路由。向服务器发出新请求、重新获取数据请求并重新渲染服务器组件。客户端将合并更新的 React 服务器组件有效载荷,而不会丢失未受影响的客户端 React(如useState
)或浏览器状态(如滚动位置)。
router.prefetch(href: string)
: 预取所提供的路由,以加快客户端转换。
router.back()
:返回浏览器历史堆栈中的前一个路由。
router.forward()
:向前导航至浏览器历史堆栈中的下一页。
5.2 usePathname()
获取 URL 上的 pathname,跟路径后面那一串,不包含查询字符串。
5.3 useSearchParams()
获取 URL 上的查询字符串。
5.4 useParams()
获取 URL 上的路由参数。
5.5 redirect()
使用
useRouter()
相关方法代替或者这个🌰:https://nextjs.org/docs/app/api-reference/functions/redirect#client-component6. 服务端组件和客户端组件的组合模式
服务端组件和客户端组件可以嵌套组合使用,但有所限制,它们各自的使用时机也不同。
6.1 服务端组件和客户端组件的使用时机
如果你想… | 服务端组件 | 客户端组件 |
请求数据 | ✅ | ❌ |
直接获取后端资源 | ✅ | ❌ |
保持服务器中的敏感数据(获取 token、API key等等) | ✅ | ❌ |
在服务器上保留大量依赖关系/减少客户端 JavaScript | ✅ | ❌ |
添加交互性和事件监听器(onClick()、onChange()等) | ❌ | ✅ |
使用状态和生命周期副作用(useState()、useReducer()、useEffect()等) | ❌ | ✅ |
使用浏览器专用 API | ❌ | ✅ |
使用依赖于状态、副作用或浏览器专用 API 的自定义钩子 | ❌ | ✅ |
使用 React Class 组件 | ❌ | ✅ |
6.2 不支持的模式:服务端组件作为模块引入客户端组件
下面代码中,将服务端组件作为模块嵌入客户端组件是不可行的:
6.3 支持的模式:服务端组件放入客户端插槽
作为 props,比如从插槽中将服务端组件插入客户端则是正确的:
- 作者:Eric 见嘉
- 链接:https://tangly1024.com/article/next14-app-router-1
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章