基于NextJS.13、14下App
模式,将按照官方文档,以最小示例去实现文档知识点。可直接本地运行比对下列清单,比口述归档更具直观。
以下清单基本按照英文官方文档章节顺序,以文档章节最小方式去复现出来。可以运行和直接打开文件查看,不用再去逐字阅读文档。
包含章节:
运行环境:
- 路径:
/routing-file
(查看) - NodeJS:
v18.13.0
- NextJS:
13.5.6
示例:
- App模式下路由和目录结构
- 基础路由 (查看)
- 包含:
layout
、page
, - 1级目录:
/routing-file/src/app/features
- 2级目录:
/routing-file/src/app/features/metadata
_
前缀目录:/routing-file/src/app/features/%5Fuser_post-info
- ---- 分割线 ----
- 包含:
- 链接和导航 (查看)
- 目录:
/routing-file/src/app/dashboard
- 包含:link组件,route跳转,usePathname监听路由变化
- ---- 分割线 ----
- 目录:
- 路由组 (查看)
- 路由分组:
/routing-file/src/app/group
- 嵌套布局:
/routing-file/src/app/group/(marketing)
/routing-file/src/app/group/(shop)
- ---- 分割线 ----
- 路由分组:
- 动态路由 (查看)
- 目录:
/routing-file/src/app/posts
- 监听动态路由变化:
/routing-file/src/app/posts/layout.tsx
/routing-file/src/app/posts/components/NavigationEvent.ts
- 注意:
usePathname
所在的组件需要通过Suspense
包裹,以便拿到叶子节点的pathname
- 动态路由:
/routing-file/src/app/posts/[pid]
- 嵌套路由:
/routing-file/src/app/posts/user/[...uid]
- 可选嵌套路由:
routing-file/src/app/posts/shop/[[...slug]]
- ---- 分割线 ----
- 目录:
- loading和自定义数据流 (查看)
- error组件 (查看)
- 捕获子级layout错误:
/routing-file/src/app/error/global
- 捕获同级page,目录:
/routing-file/src/app/error/path
- 向上抛出异常,目录:
/routing-file/src/app/error/up
- ----
app
根目录 ---- - 跟目录捕获子级异常,目录:
/routing-file/src/app/error.tsx
- 捕获跟目录异常,目录:
/routing-file/src/app/global-error.tsx
- ---- 分割线 ----
- 捕获子级layout错误:
- 插槽和平行路由 (查看)
- 目录:
/routing-file/src/app/dashboard
- 平行路由下的
page.tsx
会自动注入到dashboard
下的layout.tsx
- ---- 分割线 ----
- 目录:
- 路由拦截 (查看)
- 拦截目录:
/routing-file/src/app/photo/@modal/(.)photos/[id]
- 拦截目标:
/routing-file/src/app/photo/photos/[id]
,(noscript
和直接访问) - 通过
/routing-file/src/app/photo/@modal/default.tsx
返回null
,阻止插槽自动注入 - 总结 (查看示例)
- ---- 分割线 ----
- 拦截目录:
- Api路由 (查看)
- 静态Api:
/routing-file/src/app/api/items/route.ts
- 模拟错误:
/routing-file/src/app/api/error
- 包含:不同请求处理(
POST
、GET
)、获取URI
、获取COOKIES
、获取header
- ---- 分割线 ----
- 动态Api、获取请求、跨域:
/routing-file/src/app/api/items/[slug]
- 跳转Api:
/routing-file/src/app/api/redirect
- 数据流:
/routing-file/src/app/api/stream
- 静态资源响应:
/routing-file/src/app/api/rss.xml
- 总结:Api Route在安全设计上的理解 (查看)
- ---- 分割线 ----
- 静态Api:
- 中间件 (查看)
- 目录:
/routing-file/src/middleware.ts
- 包含:路由重定向、重写url、获取header、设置header、设置cookies
- ---- 分割线 ----
- 目录:
- 本地化,只做了本地化词典本分 (查看)
- 目录:
/routing-file/src/app/lang/[slug]
- 还剩余两个方法
middleware
和generateStaticParams
,由于需要调整目录结构会和当前实例冲突,目前不做演示 - ---- 分割线 ----
- 目录:
- default.js (查看)
- 案例,路由拦截:
/routing-file/src/app/photo/@modal
- 这里说一个场景,在路由拦截中(
@
开头的目录),为了防止路由段下page默认渲染,添加一个default.tsx
,返回null
来阻塞 - ---- 分割线 ----
- 案例,路由拦截:
- not-found.js (查看)
- 捕获当前路段:
/routing-file/src/app/file
- 发起捕获:
/routing-file/src/app/file/[not]
- 权限场景示范:
/routing-file/src/app/file/power
- ---
app
根目录 --- - 捕获全局:
/routing-file/src/app/not-found.tsx
- 捕获全局404:
/routing-file/src/app/[...slug]
- 区别:全局捕获用于接受所有路由段下抛出的
not-found
,而全局404用于捕获未知路由段提供统一展示页面 - ---- 分割线 ----
- 总结 (查看)
- ---- 分割线 ----
- 捕获当前路段:
- 路由段配置 (查看)
dynamic
- 默认情况:
/routing-file/src/app/file/dynamic/(auto)
- 强制不缓存、强制请求不缓存:
/routing-file/src/app/file/dynamic/(force-dynamic)
error
静态模式、请求静态模式、SSG
静态模式下允许动态参数(默认error
模式下为静态):/routing-file/src/app/file/dynamic/(error)
- 强制静态模式(
cookie
、header
、searchParams
为空):/routing-file/src/app/file/dynamic/(force-static)
- 默认情况:
dynamicParams
:SSG
静态模式下允许动态参数:/routing-file/src/app/file/dynamic/(dynamic-params)/in-dynmic-params/[slug]
SSG
静态模式下超出参数范围404:/routing-file/src/app/file/dynamic/(dynamic-params)/not-in-dynmic-params/[slug]
revalidate
- 目录:
/routing-file/src/app/file/dynamic/(revalidate)
- 包含:默认强制缓存、每次请求重新验证、ISR定时重新验证
- 目录:
fetchCache
高级设置覆盖默认缓存行为- 以缓存设置为准:
/routing-file/src/app/file/dynamic/(fetch-cache)/fetch-cache
- 默认缓存行为:
/routing-file/src/app/file/dynamic/(fetch-cache)/fetch-cache-default-cache
- 使用缓存行为:
/routing-file/src/app/file/dynamic/(fetch-cache)/fetch-cache-only-cache
- 强制缓存行为:
/routing-file/src/app/file/dynamic/(fetch-cache)/fetch-cache-force-cache
- 默认不缓存行为:
/routing-file/src/app/file/dynamic/(fetch-cache)/fetch-cache-default-no-store
- 使用不缓存行为:
/routing-file/src/app/file/dynamic/(fetch-cache)/fetch-cache-only-no-store
- 强制不缓存行为:
/routing-file/src/app/file/dynamic/(fetch-cache)/fetch-cache-force-no-store
- 以缓存设置为准:
runtime
(见元数据优化) (查看)- 目录:
/optimizing/src/app/optimizing/metadata/opengraph/[id]/opengraph-image.tsx
- 目录:
generateStaticParams
(查看)/rendering/src/app/blog/time/[id]
- 详细见“数据获取、渲染、缓存”中4种模式
- 其他,因为是单一例子所以放在
dynamic
的auto
下preferredRegion
:/routing-file/src/app/file/dynamic/(auto)/auto-preferred-region/page.tsx
maxDuration
:/routing-file/src/app/file/dynamic/(auto)/auto-max-duration/page.tsx
- ---- 分割线 ----
- 基础路由 (查看)
包含章节:
运行环境:
- 路径:
/rendering
(查看) - NodeJS:
v18.13.0
- NextJS:
13.5.6
示例:
- 数据获取
- 数据获取和缓存 (查看)
- server components中获取数据:
/rendering/src/app/fetch/page.tsx
- 缓存配置
- 路由段配置:
/rendering/src/app/fetch/revalidate/[id]
- ---- routing-file ----
- fetch配置强制缓存:
/routing-file/src/app/file/dynamic/(error)/error-fetch/page.tsx
- fetch配置不缓存:
/routing-file/src/app/file/dynamic/(force-dynamic)/force-dynamic-revalidate
- 更多见路由段配置: (查看)
- 路由段配置:
- ---- 分割线 ----
- server components中获取数据:
- 重新验证
- 数据获取模式 (查看)
- 顺序请求+预加载数据:
/rendering/src/app/fetch/sequential/[id]
- 顺序请求+
suspense
优先渲染:/rendering/src/app/fetch/suspense/[id]
- 并行请求:
/rendering/src/app/fetch/parallel/[id]
- 数据缓存详细见:
/rendering/src/app/fetch/cache
- ---- 附加案例 ----
server-only
仅在服务端:/routing-file/src/app/lang/[slug]
(查看)- ---- 分割线 ----
- 顺序请求+预加载数据:
- 服务端操作 (查看)
- 服务端操作(
Server-only Forms
):/rendering/src/app/fetch/server-action/server-cart
- 服务端操作提交后重新渲染视图:
/rendering/src/app/fetch/server-action/revalidation
- 客户端操作:
/rendering/src/app/fetch/server-action/client-cart
- 除表单外通过
startTransition
进行操作:/rendering/src/app/fetch/server-action/client-cart/transition
- 除
startTransition
外,非表单操作:/rendering/src/app/fetch/server-action/custom/[id]
- 服务端校验表单 + 设置cookies:
/rendering/src/app/fetch/server-action/validation
- 通过实验性Api (experimental_useFormStatus) 处理loading + Error处理 + 重定向:
/rendering/src/app/fetch/server-action/test/form-submit
- Zod数据校验,提供3个例子:
- client validate + route validate:
/rendering/src/app/fetch/server-action/test/zod/user-info
- server validate + route validate:
/rendering/src/app/fetch/server-action/test/zod/user-info-server
- todolist (含错误处理):
/rendering/src/app/fetch/server-action/test/zod/todolist
- client validate + route validate:
- 通过
useOptimistic
乐观更新:/rendering/src/app/fetch/server-action/optimistic
- 总结 (查看)
服务端非表单进行操作:(查看:路由导航总结)/rendering/src/app/fetch/server-action/server-cart/noform
- ---- 附赠应用场景 ----
- 客户端轮训:
/rendering/src/app/fetch/server-action/client-cart/noform
- 通过
useTransition
实现的实时搜索预览:/rendering/src/app/fetch/server-action/client-cart/transition/[...slug]
(预览) - 标签筛选内容+加载提示+错误fallback+断网fallback (包含cookies设置):
/rendering/src/app/fetch/server-action/post
(预览) - 迭代更新,
Server Action
函数式和文件式有什么不同 (预览) - ---- 分割线 ----
- 服务端操作(
- 附加案例
- 数据获取和缓存 (查看)
- 渲染
- 整理内容过长,单独总结一章 (查看)
- 其中包括的案例有:仅供服务器、客户端操作、上下文配置主题、esbuild配置、服务器组件和客户端组件交叉嵌套、运行时
- 不同的渲染模式 (查看)
- 总结和关系图:
- SSR模式:
/rendering/src/app/blog/time/page.tsx
- CSR模式:
/rendering/src/app/blog/time/client
- SSG模式:
/rendering/src/app/blog/time/[id]
- 访问时
[id] = 1
,则page
和fetch
均为SSG
- 访问时
[id] > 1
,则page
为SSR
,fetch
缓存为SSG
- 访问时
- ISR模式:
/rendering/src/app/blog/time/isr/[id]
- 访问时
[id] = 1
,则page
和fetch
均为SSG
- 访问时
[id] > 1
,则通过revalidate
缓存为ISR
- 访问时
- 补充:SSG超出范围404 (查看)
- 目录:
/routing-file/src/app/file/dynamic/(dynamic-params)/not-in-dynmic-params/[slug]
- 目录:
- PPR模式: (查看)
- 新增模式,目前是实验性功能,单独开一个项目展示
- 附加案例
- 缓存 (查看)
- 请求树:
/rendering/src/app/fetch/cache/layout.tsx
/rendering/src/app/fetch/cache/page.tsx
- 服务组件到客服组件:
/rendering/src/app/fetch/cache/client
- 不缓存:
/rendering/src/app/fetch/cache/nostore
- POST缓存:
/rendering/src/app/fetch/cache/post
- 预缓存:
/rendering/src/app/fetch/cache/preload
- 通过react缓存:
/rendering/src/app/fetch/cache/react-cache
- ISR:
/rendering/src/app/blog/time/isr/[id]
(查看) - 总结 (查看)
- 附加案例
- 请求树:
包含章节:
- [Styling]
- [Optimizing]
- [Components]
- [Functions]
- 其中函数这部分作为本篇章和以上所有内容的概述,更适合Optimizing(优化)所以归纳在一起;
- 而样式和组件关联系更大,组件仍旧和优化紧密关联,因此也放在一起总结;
- 而优化中元数据(Metadata)在文件约定也有相关内容,为了便于理解也归纳在优化分类中
- 由于在渲染中展示了antd,这一章将引入semi作为附加案例
运行环境:
- 路径:
/optimizing
(查看) - NodeJS:
v20.9.0
- NextJS:
14.0.0
示例:
- 组件和优化
- 图片 (查看)
- 图片相关配置信息:
/optimizing/next.config.js
- 见
nextConfig.images
- 见
- 基础操作:
/optimizing/src/app/optimizing/images/page.tsx
- 包含:内部图片、外部图片、图片加载器、图片事件
- 背景图片:
/optimizing/src/app/optimizing/images/background
- 图片占位符图片:
/optimizing/src/app/optimizing/images/color
- 设置断点处理图片自适应:
/optimizing/src/app/optimizing/images/fill-container
- 根据父容器填充图片:
/optimizing/src/app/optimizing/images/fill-container/list
- 默认模糊占位符图片:
/optimizing/src/app/optimizing/images/placeholder
- 自适应图片:
/optimizing/src/app/optimizing/images/responsive
- svg占位符图片:
/optimizing/src/app/optimizing/images/shimmer
- 备注:nextjs推荐在生产环境和独立模式下通过
sharp
进行优化 (查看) - ---- 分割线 ----
- 图片相关配置信息:
- 字体 (查看)
- 多页共享字体库:
/optimizing/src/utils/fonts.ts
(查看) - 字体包含:可变字体、不可变字体
- 组件修改字体:字体子集加载替换、通过样式名修改字体(含子组件字体),通过样式修改字体
- 通过styled-jsx修改字体:
:root
托管全局字体变量,:global
设置子组件字体 - 通过字体别名设置字体、使用本地字体
- 通过
Tailwind CSS
设置字体 - ---- 分割线 ----
- 多页共享字体库:
- 链接 (查看)
- 包含:链接、对象链接、链接替换、阻止预加载、中间件
- 目录:
/optimizing/src/app/optimizing/link
- 导航监听和跳转:
/optimizing/src/app/optimizing/link/demo
- ---- 分割线 ----
- 动态脚本 (查看)
- 引入脚本,包含策略2个策略、2个内联模式:
/optimizing/src/app/optimizing/script/page.tsx
- 加载成功事件:
/optimizing/src/app/optimizing/script/group/chart
- 加载失败事件:
/optimizing/src/app/optimizing/script/group/onerror
- CSP组件:
/optimizing/src/app/optimizing/script/cspe/page.tsx
- CSP配置:
/optimizing/src/middleware.ts
- 动态脚本总结 (查看)
CSP
总结 (查看)- ---- 分割线 ----
- 引入脚本,包含策略2个策略、2个内联模式:
- 元数据 (查看)
- 元数据重写、继承,提供
JSON-LD
,open graph
共享,静态单个icon
和apple-icon
- 目录:
/optimizing/src/app/optimizing/metadata
- 目录:
- 动态生成描述信息,
open graph
继承、共享、覆盖,动态单个icon
- 目录:
/optimizing/src/app/optimizing/metadata/[id]
- 目录:
- 静态多个
icon
,静态单个OG image
和twitter image
- 目录:
/optimizing/src/app/optimizing/metadata/multiple
- 目录:
- 动态多个
icon
,动态多个OG image
- 目录:
/optimizing/src/app/optimizing/metadata/multiple/[id]
- 目录:
- 动态单个
OG image
,引入特殊字体- 目录:
/optimizing/src/app/optimizing/metadata/multiple/[id]
- 目录:
robot
,动态sitemap
,目录 (查看)/optimizing/src/app/robots.ts
/optimizing/src/app/sitemap.ts
- 静态
robot
,静态sitemap
(查看)- 目录:
/optimizing/src/app/@metadata
- 使用移动到
app
根目录,和动态资源不能并存
- 目录:
manifest
资源路由Api Router
(查看)- 目录:
/optimizing/src/app/api/manifest
- 目录:
- ---- 分割线 ----
- 元数据重写、继承,提供
- 懒加载 (查看)
- 及时懒加载、按需懒加载、客户端懒加载、加载loading、按命名懒加载
- 目录:
/optimizing/src/app/optimizing/lazy/page.tsx
- 目录:
- 服务端懒加载:
/optimizing/src/app/optimizing/lazy/server
- 懒加载外部库:
/optimizing/src/app/optimizing/lazy/external
- ---- 分割线 ----
- 及时懒加载、按需懒加载、客户端懒加载、加载loading、按命名懒加载
- 图片 (查看)
- 函数
- cookies (查看)
- 目录:
/optimizing/src/app/func/cookies
- 包含:获取单个、获取所有、判断存在、设置、删除、设置生命周期、设置有效期
- 备注:用
get
代替has
判断cookies,否则在server action
下报错 - ---- 分割线 ----
- 目录:
- draft,草稿模式见当前清单
- fetch (查看)
- 目录:
/optimizing/src/app/func/fetch
- 包含:缓存设置、重新校验
- 视图标签和刷新,见当前清单:服务端操作 (查看)
- 目录:
/rendering/src/app/fetch/server-action/revalidation
- 目录:
- ---- 分割线 ----
- 目录:
- 动态生成icon,见当前清单:组件和优化 - 元数据 (查看)
- 目录:
/optimizing/src/app/optimizing/metadata
- ---- 分割线 ----
- 目录:
metadata
元数据 (查看)- 目录:
/optimizing/src/app/func/metadata
- 包含:
title
、description
、baseic fields
、metadataBase
、openGraph
、robots
、icons
、manifest
、twitter
、verification
、appleWebApp
、alternates
、appLinks
、archives、assets
、bookmarks
、category
、other custom
、resource hints
- 动态生成函数(含metadata的TS类型)
- 目录:
/optimizing/src/app/optimizing/metadata
- 见当前清单:组件和优化 - 元数据 (查看)
- 目录:
- 废弃的
metadata
见14.0.0
更新部分 (查看) - ---- 分割线 ----
- 目录:
- SSG,见当前清单:4个不同的模式 (查看)
- 动态路段生成静态路由(一级路段):
/rendering/src/app/blog/[slug]
- 从下至上生成(多级路段):
/rendering/src/app/blog/list/[category]/[product]
- 捕获全路段:
/rendering/src/app/blog/list/info/[...slug]
- 从上至下生成路段:
/rendering/src/app/blog/products/[category]
- 限定路段的静态路由 (查看)
- 目录:
/routing-file/src/app/file/dynamic/(dynamic-params)/not-in-dynmic-params/[slug]
- 目录:
- ---- 分割线 ----
- 动态路段生成静态路由(一级路段):
- headers (查看)
- 目录:
/optimizing/src/app/func/headers
- 目录:
- ImageResponse,见当前清单:组件和优化 - 元数据 (查看)
- 目录:
/optimizing/src/app/optimizing/metadata
- 目录:
- NextRequest
- NextReponse
- notFound,见清单:路由和文件约定,not-found (查看)
- 目录:
/routing-file/src/app/file
- 从官方文档提供的案例中,这个模式更青睐于找不到时的UI渲染,比如说:用户找不到,文章找不到,没有权限等,而404只是这个特性附带的一个功能
- 总结 (查看)
- ---- 分割线 ----
- 目录:
- redirect (查看)
- 目录:
/routing-file/src/app/api/redirect
- 如果定向到404,官方建议用
not-found
代替 - ---- 分割线 ----
- 目录:
- revalidatePath、revalidateTag (查看)
- 目录:
/rendering/src/app/link/fetch
- 目录:
- 客户端组件hooks (查看)
useParams
,包含:静态路由、动态路由、多级路由、路由全局捕获- 目录:
/optimizing/src/app/func/client
- 目录:
useRouter
,包含:监听路由变化、路由预取、路由跳转(不滚动页面)、替换页面、路由刷新、回退、前进- 目录:
/optimizing/src/app/func/client/router
- 目录:
useSearchParams
,包含:获取和判断参数,客户端静态获取、客户端静动态获取、服务端获取、更新查询参数- 目录::
/optimizing/src/app/func/client/search-params
- 目录::
- 一级路由、二级路由
useSelectedLayoutSegment
:/optimizing/src/app/func/client/components/ActiveSegment.tsx
useSelectedLayoutSegments
:/optimizing/src/app/func/client/components/Breadcrumbs.tsx
- ---- 分割线 ----
- 网页指标:
useReportWebVitals
(查看)- 目录:
/optimizing/src/app/func/web-vitals
- 借助
google analytics
:监听路由web vitals
作为非交互事件、发送交互事件、监听路由发送监听事件 - 案例中
NEXT_PUBLIC_GA_ID
,请将自己的google analytics
代码添加到env.local
中,仓库没有提供 - ---- 分割线 ----
- 目录:
- cookies (查看)
- 案例
- UI库:semi + next-theme主题切换 (查看)
- 包含:
- 配置
transpilePackages
:/optimizing/next.config.js
- 服务端主题配置,目的在CSP中获取动态nonce,不需要启动CSP无需使用:
/optimizing/src/lib/Providers.tsx
- 客户端主题配置+next-theme主题切换:
/optimizing/src/lib/ProvidersClient.tsx
- 包裹主题:
/optimizing/src/app/layout.tsx
- 引入主题包:
/optimizing/src/app/globals.css
- 将主题切换挂载到
<html>
上避免闪缩:/optimizing/src/app/semi.css
- 配置
- 为了避免主题切换闪烁,采用了
HomeDash
(查看)的解决方案,将主题挂载到HTML下,而非官方目前推荐的body下 - 主题切换原理:通过
script
阻塞渲染,直到<html>
挂载主题为止,要验证防闪烁,请拷贝演示代码到本地product (npm run start
)下运行,因为dev
在script
阻塞之前会阻塞所有渲染 - ---- 分割线 ----
- 包含:
- UI库:semi + next-theme主题切换 (查看)
包含章节:
运行环境:
- 路径:
/configuring
(查看) - NodeJS:
v20.9.0
- NextJS:
14.0.0
示例:
- 配置文件
- 环境变量 (查看)
- 目录:
/configuring/src/app/env
- 配置文件:
/configuring/.env*
(缺少.env.local
,包含安全信息,自行配置) - Node环境变量:开发环境、生产环境、默认环境、覆盖环境变量、环境变量分组
- 浏览器环境变量:服务端组件下调用,客户端组件下调用
- ---- 分割线 ----
- 目录:
- 模块重命名见:
/configuring/tsconfig.json
- src目录,正如你看到当前示例,目前通过
npx
脚手架创建项目时默认会提示选择src目录- 如果初始项目没有选择src目录,后期直接移动目录即可
- 环境变量 (查看)
.vscode
配置 (查看)- 目录:
/configuring/.vscode
typescript.tsdk
:用于统一TS版本管理- 其他,因为我把全局默认的TS类型检查关闭了,所以在项目里单独开启
- 目录:
next.config.js
配置 (查看)- 目录:
/configuring/next.config.mjs
(使用mdx,使用为esbuild配置) - 配置文件按照属性名先后顺序排列,函数属性放置在属性名后
- 包括:
appDir
、assetPrefix
、basePath
、compress
、devIndicators
、distDir
、env
、eslint
、generateEtags
、keepAlive
、mdxRs
、onDemandEntries
、poweredByHeader
、productionBrowserSourceMaps
、reactStrictMode
、trailingSlash
generateBuildId
,生成build-id
方法 (查看)- 目录:
/configuring/src/utils/build-id.js
- 目录:
header
(查看)- 目录:
/rendering/next.config.js
- 包含:标头覆盖行为、路径匹配、通配符匹配、正则匹配、
header
匹配、cookies
匹配、query
匹配、basePath
支持、i18n
支持、可选属性(见source: '/blog/:post(\\d{1,})'
)
- 目录:
images
:remotePatterns
、unoptimized
、domains
、deviceSizes
、imageSizes
、图片输出格式、ttl
、文件导入、外部svg安全策略(csp)- 包含本地图片加载器(
loader
、loaderFile
):/optimizing/src/utils/myImageLoader.ts
(查看)
- 包含本地图片加载器(
incrementalCacheHandlerPath
:增量缓存处理器,实验功能,默认采用文件缓存 (查看)- 目录:
/configuring/src/utils/cache-handler.js
- 目录:
output
:(查看总结)pageExtensions
:文件扩展redirects
(查看)- 目录:
/rendering/next.config.js
- 包含:基础匹配、路径匹配、通配符匹配、正则匹配、特殊字符匹配、
header
匹配、cookies
匹配、query
匹配、basePath
支持、i18n
支持
- 目录:
rewrites
(查看)- 目录:
/rendering/next.config.js
- 匹配周期:
beforeFiles
、afterFiles
、fallback
(注意配置文件中missing案例) - 匹配参数:自动匹配路径、路径转换至
query
、手动匹配 - 外部重写:路径匹配、尾斜线匹配、增量匹配
- 其他:基础重写、路径重写、通配符重写、正则重写、特殊字符重写、
header
匹配、cookies
匹配、query
匹配、basePath
支持、i18n
支持 - 坑点总结 (查看)
- ---- 分割线 ----
- 目录:
experimental
实验性功能:- 文件注释包含详细说明:
appDir
、serverActions
、serverComponentsExternalPackages
、trailingSlash
、typedRoutes
、typescript
mdxRs
详细见下方mdx
说明 (查看)- output相关:
outputFileTracingExcludes
、outputFileTracingIncludes
、outputFileTracingRoot
(查看总结) urlImports
webpack
配置函数transpilePackages
,见semi配置:/optimizing/next.config.js
(查看)turbo
没有实现,目前和server action
冲突webVitalsAttribution
留个坑- ---- 分割线 ----
- 文件注释包含详细说明:
- 目录:
eslintrc.json
配置 (参考)- 目录:
/configuring/.eslintrc.json
- NextJS项目安装之后,在编辑js文件头部的时候会报错:
Cannot find module 'next/babel'
- 解决办法是找到
eslintrc.json
中的extends
属性,修改为:"extends": ["next/babel","next/core-web-vitals"]
- 目录:
- 其他
- mdx (查看)
- 目录:
/configuring/src/app/mdx
- 配置文件:
/configuring/next.config.mjs
(查看) - 额外用到的库:
@next/mdx
、@mdx-js/loader
、@mdx-js/react
、@types/mdx
、next-mdx-remote
- 本地解析(包含layout布局):
/configuring/src/app/mdx
- 远程解析:
/configuring/src/app/mdx/remote
- 自定义组件元素
- 全局配置:
/configuring/src/mdx-components.tsx
- 局部配置:
/configuring/src/app/mdx/custom
- 不能并存,通过
next.config.js
配置中withMDX
的providerImportSource
开启局部配置
- 全局配置:
- 备注1:
mdx
的图片资源支持内部和外部 - 坑点1:
mdx
和nextjs
的TS体操标准不一样,见上方mdx的“自定义组件元素” - 坑点2:本地
mdx
必须client component
否则报错,远程mdx
基于next-mdx-remote
,需要server component
否则报错(或者至少把数据获取fetch
和MDXRemote
分开) - ---- 分割线 ----
- 目录:
- mdx (查看)
NextJS 14提供的PPR功能预览,来自[vercel-labs/next-partial-prerendering]
运行环境:
- 路径:
/canary
(查看) - NodeJS:
v20.9.0
- NextJS:
Next.js 14.0.3-canary.5
示例:
划重点:
打开购物车组件/canary/src/components/product/pricing/index.tsx
[查看]
<Suspense
fallback={<AddToCart initialCartCount={0} />}
>
<AddToCartFromCookies />
</Suspense>
在Suspense
中包含了2个组件AddToCart
,AddToCartFromCookies
,这里分别用到了:
useTransition
:监听加购过程useRouter
:写入cookies后refresh
本地视图- 回到
Suspense
边界,在服务端通过AddToCartFromCookies
更新视图
cf6e2e9c42a4f29b1dacadffb58c9a1f_723639401790_v_1701946601828596.mp4
- 通过
Api Route
搭建起BFF
,通过容器内网IP和微服务进行交互,宿主机和外部都不可直接访问微服务容器 - 外网用户只能通过网关和NextJS的
Api Route
,获取微服务数据,不能直接访问微服务,也猜不到接口真实地址和具体入参信息 - 关于NextJS的3种模式
- SSG:CI\CD下,通过build直接从微服务获取数据
- SSR和CSR:通过
Api Route
包装的接口获取微服务数据
目录:/routing-file/src/app/photo
(查看)
点开列表图片将会被拦截器阻拦,当打开照片刷新页面,将跳过阻拦进入详情页
cf6e2e9c42a4f29b1dacadffb58c9a1f_711447425084_v_1689754625098525.mp4
路由拦截的坑点:
只接受当前路由段拦截,其他URL均不可以。举个例子目录如下,只有/list
下才可以拦截/list/photo
,其他路由段无论是子级的/list/catgory
还是相邻的/blog
,跳转到/list/photo
均报错,是名副其实的单页应用。
./list/
├── @modal
│ └── (.)photo
│ └── default.tsx
├── catgory
│ └── page.tsx
├── photo
│ └── page.tsx
├── default.tsx
├── layout.tsx
└── page.tsx
./blog/
└── page.tsx
静态路由:
- 在路由段中先去查
page.tsx
,找到并进行渲染 - 如果路由段中
page.tsx
抛出notFound()
,将向app
根目录查找not-found.tsx
,如果整个路由段都没找到则采用默认404
页面 - 如果抛出
notFound()
找到app
根目录not-found.tsx
,先执行默认函数不渲染,然后根据路由段一级一级查找not-found.tsx
,最后将在最接近叶子节点查找not-found.tsx
捕获并渲染 - 若只有根目录存在
not-found.tsx
,将渲染根目录下的not-found.tsx
,并不断循环这个查找过程。这意味着所有找不到路由段的404都将按照这个步骤进行
动态路由:
- 在路由段中先去查
page.tsx
,如果不存在将去查动态路由([slug]
)下的page.tsx
- 如果当前路由段存在
page.tsx
,且抛出notFound()
,则按照上面静态路由部分规则执行 - 如果想要在动态路由段中抛出404,只要在动态路由段的
page.tsx
中抛出notFound()
,抛出后渲染规则和静态路由段规则一致
SSG路由(generateStaticParams):
- 通过
notFound()
抛出的404,将按照上述规则,一层一层捕获 - 通过
generateStaticParams
+dynamicParams = false
限定路由抛出的404,将视作not exist
通过下方[...slug]
解决办法进行捕获
坑点:
由于静态路由中第三步和第四步,当访问一个不存在的路由段时,将不断循环查找过程,然而这种404的情况是很普遍的,而官方文档并没有提供任何解释和解决办法。
我的解决办法:
- 在
app
根目录下创建一个动态路由[...slug]
,并且在按照上面的规则创建page.tsx
和not-found.tsx
,这样所有找不到路由段的404,都默认向下在[...slug]
中进行捕获并渲染 - UML绘图使用了revenote
备注:
- 抛出
notFound()
时,无论如何都会去执行app
根目录not-found.tsx
除非这个文件也不存在。 - 只要存在
not-found
文件,那么路由段下无论是否发生错误,这段静态资源就必须输出,如果文件里有fetch
请求,无论是否错误也会执行 - 同理,结合这里提到的
cache
缓存部分,当前路段所有的fetch
请求都会记录缓存,无论是从layout
到not-found
,还是从not-found
到page
not-found场景复现: 复现了需登录登录查看文章的流程
cf6e2e9c42a4f29b1dacadffb58c9a1f_716898661038_v_1695205861049687.mp4
目录:/routing-file/src/app/file/power
(查看)
步骤:
- 访问
/file/power/[slug]
先检查权限,不够权限由layout.tsx
抛出notFound
,被/file/power/not-found.tsx
捕获,并指引登录 - 权限够正常访问
- 权限够访问
[slug]
为4,由page.tsx
抛出notFound
,经由/file/power/not-found.tsx
后被/file/power/(list)/not-found.tsx
捕获,并提示找不到内容
原理:
- 同级目录下
layout
包裹了not-found
,再包裹了子集的page.tsx
,当权限不够由layout
抛出的notFound
只能被上一级的not-found
捕获 - 当叶子节点
page
抛出notFound
,将会一层一层进行捕获,最终会在最接近page
层级下的notFound
捕获进行渲染
备注:复现时分别发现两个坑点,请分别查看server action
(查看)和NextJS 4个模式的关系(查看)
not-found埋下的问题:
如果使用not-found
去匹配全站404,那么会导致在next.config.js
中使用rewrites
的fallback
失效。因为在根目录设置全局not-found
匹配本身就是一种fallback
,它属于filesystem,按照文档说法优先级高于rewrites
的fallback
。如果要两者并存,建议将全局not-found
在rewrites
外部的网站进行匹配,这样就相当于location
- external site
- not-found
只能动态匹配服务器组件:
在source动态匹配中,只会将动态的pathnae传递给服务器组件作为searchParams,不会传递到客户端组件
- 验证示例:
/rendering/src/app/antd/client
(查看) - 但是作为重写路由段,URL本身是接受searchParams的,这样就会造成实际和预期不符
为了避免这个问题请注意一下几点:
- 检查重写的路由段下的
components tree
中是否用到了useSearchParams
- 如果有,就不要通过pathname作为动态参数传递给searchParams
- 尽量不要在服务器组件的
props
去捕获searchParams
同时,又使用客户端组件去捕获useSearchParams
,统一获取规范
- 缓存是树状结构,如上图官方所示,缓存只加载请求1次,多次相同链接的请求将按照第一次的结果从缓存中获取数据
- 如果要刷新缓存,通过
fetch
设置no-store
或export const revalidate = 0
- 在请求树中相同链接的请求,只要有1个请求设置了重新验证,整个树所有请求都将重新向服务端发起请求(注1)
- 关于
POST
请求缓存的一处错误(注2) POST
和GET
缓存不同在于,POST
如果链接相同,请求参数不同,将视作新的请求而不从缓存中获取,而GET
没有- 目录:
/rendering/src/app/fetch/cache/post
(查看)
- 目录:
- 对于预存取的用法,在官方文档有提到(注3)
- 对于react的
cache
组件缓存(注3) - 对于客户端组件,它本身和服务端组件不是一套组件,所以在客服端组件中,无论怎么刷新时间,都不重新渲染服务端,也不会使用服务端的缓存
注1: /rendering/src/app/fetch/cache/post/revalidate
(查看)
- 包含两个Api:
worldtimeapi
、timeapi
- 请求树链路:
cache/layout.tsx
-cache/post/layout.tsx
-cache/post/revalidate/page.tsx
- 在
page.tsx
刷新缓存,整个树全部刷新,无论链接、请求方式,参数、层级,整个请求树缓存都重新请求 - 在重新请求过程中遵循缓存规则,后一条请求获取第一条请求结果的缓存
注2: /rendering/src/app/fetch/cache/post
(查看)
文档是这么说的
Dynamic methods (next/headers, export const POST, or similar) are used and the fetch is a POST request (or uses Authorization or cookie headers)
实测:
- post请求也缓存,请求缓存不缓存取决于
no-store
和revalidate
- 除非这个post请求位于
server action
中,这样即便是get请求一样会重新请求
注3:
官方不建议通过props跨层级传递数据,而建议重复使用fetch获取数据,因为NextJS中fetch
的结果是默认获取上一条相同请求的缓存
In this new model, we recommend fetching data directly in the component that needs it, even if you're requesting the same data in multiple components, rather than passing the data between components as props.
关于react的cache
组件,官方备注一条:即便你不这样做,NextJS在fetch
时默认也会缓存,并且推荐默认的做法
fetch()
caches requests automatically, so you don't need to wrap functions that usefetch()
withcache()
. See automatic request deduping for more information.
use server
:
- 大部分情况是用于表单验证,非表单验证的情况下可做后台静默更新(例如埋点检测并更新用户信息)
- 在官方文档里提到这个功能目前是实验性的,对于用在表单场景的交互更多可能出于本地JS被禁用的情况
- 在服务端非表单情况下,可以不使用'use server'交互,但这样就意味着只能只能在构建时生效
- 反之,所有'use server'都脱离了缓存
服务端提交后交互:
- 文档里写到通过
redirect
、revalidatePath
、revalidateTag
(示范中提供了revalidateTag
) - 以示范文件说原理:页面首次fetch,得到未认证的结果,提交表单
revalidateTag
重新验证,视图中的fetch再次请求得到认证状态,重新渲染视图
use client
:
- 可用于表单,也可以是非表单的操作(诸如:轮训,见示例文件)
- 对于表单验证,本地验证更符合当下交互习惯
轮训场景:
- 例如微信扫码登录,先从
server action
提交OAUTH
认证,拿到token
后交给客户端轮训登录状态 - 以示范文件说原理:先发起
server action
,通过后本地使用swr
轮训状态
坑点1:(13.5已修复,见末尾更新说明 - 查看)Server Action
+ redirect
展开此前的记录
- 不要在
Server Action
中直接调用redirect
,否则会警告failed to get redirect response TypeError: fetch failed
- 正确做法,
Server Action
后通过revalidateTag
或revalidatePath
刷新视图,在视图中根据情况redirect
坑点2:Server Action
+ cookies().has()
- 不要在
Server Action
后去判断cookies().has()
,会提示has
这个方法补存在,也不要用get
后去转换成boolean
,因为删除cookies
后对象依旧存在,只是值为空了 - 解决办法:
/routing-file/src/app/file/power/lib.ts
(查看)
示例采用cdn.jsdelivr.net
作为外部脚本公共库,查看演示前先确保能够正常访问仓库资源。
坑点1:两个策略
beforeInteractive
:在App dir
模式下,按照官方文档添加到根目录下的layout
,会提示错误“No Before Interactive Script Outside Document” (查看),要求你放到page
目录下,然而app
和page
两个模式并不可以同时存在worker
:在App dir
模式下,文档已明确目前不可用:The worker strategy is not yet stable and does not yet work with the app directory. Use with caution.
坑点2:编译检测
假定你远程调用echart
,完成后有个全局的echat
对象,编译时会异常提醒echat
这个对象不存在,于是我将代码用字符的形式包裹起来,用event
调用,编译通过,这样就埋下第3个坑
坑点3:CSP(并入CSP总结)
展开此前的记录
- 对于
script-src
和style-src
,默认要求配置self
,而对于有在组件上写CSS的情况,style-src
还需要设置unsafe-inline
- 对于
script-src
,如果有客户端组件用到JS的情况(例如:useEffect
),必须设置unsafe-inline
才能执行 - 对应坑点2,有使用到
eval
、setTimeout
、setInterval
和Function
等函数,必须设置unsafe-eval
才能执行
坑点4:CSP-nonce模式(并入CSP总结)
展开此前的记录
NextJS加载动态脚本分两部分:①通过
link
元素将远程的script
下载下来;②按照组件情况插入script
接着说坑点:
- 对于使用
nonce
模式,那么一定会造成unsafe-inline
失效,因为NextJS,不会根据你设置nonce
的Hash值,动态去匹配每一个内联脚本,也就不会去执行 - 设置了
nonce
模式,如果是外部脚本,同样需要配置domain
,它关系到外部的脚本在link
标签下会不会下载,而nonce
的Hash值决定下载后会不会动态插入到document
- 由于第一点问题,
nonce
模式下,onLoad
和onReady
,同样不会执行
总结:如果需要使用csp
的nonce
模式需要自行考虑了。如果不考虑引入外部脚本,那么csp
完全可以解决XSS
的问题。对于图片组件安全,配置安全作用域,对于脚本安全,可以采用csp
作为安全解决方案
规范标准(驳回:动态加载脚本坑点3)
- 使用上没有特定的规范标准,按实际情况来,通过调试工具查看错误信息进行调整
- 难点肯定不在NextJS,而是在于项目中引入的第三方库
- --- 仓库案例中遇到的问题 ---
- next-theme
- 支持
unsafe-inline
或nonce
,需要开启unsafe-event
,无第三方CSS
- 支持
- echart
- 支持
unsafe-inline
或nonce
,需要开启unsafe-event
,不能启用严格模式strict-dynamic
,否则url报错,目前暂无解 - 动态引入的CSS不支持
nonce
,所以style-src
不支持nonce
- 支持
- google analytics
- 适配度最好,会根据当前页面自动匹配
nonce
,需要开启unsafe-event
- 由于会引入第三方资源,需要在
img-src
和default-src
启用安全域名
- 适配度最好,会根据当前页面自动匹配
坑点1:nonce(对应:动态加载脚本坑点4)
目前不用纠结是否要使用nonce
,因为NextJS目前为止(14.0.0),在构建后start
环境下会丢失nonce
的值。详细见:vercel/next.js#55638
- 在启用
urlImport
导入外部的script
后,请不要同时在next.config.js
中启用transpilePackages
,否则会提示一段:undefinde export {default} ...
之类的错误。 - 假定你有使用semi这样的UI库,可能
urlImport
就无法正常使用,好在urlImport
目前是一个实验性功能,并非必要功能。
留一个坑待日后再观察
问题: 在server components
下3个模式:(SSR
、SSG
、ISR
)的缓存和重新验证,在官方文档所有说明中,只针对新开、刷新当前路由,而不包括路由导航之间的跳转。这就意味着,所有非单一用户产生的状态,需要在路由跳转后实时返回状态信息的页面,不能及时同步状态。
示例: /rendering/src/app/link
(查看)
解决办法:
- 服务端渲染,用原生
<a>
标签代替<Link>
组件,缺点:浏览器会有明显的刷新感,路由中layout
页会被刷新 - 客户端渲染,异步给链接添加
hash
值,缺点:导航链接会多出一串随机的hash
值,路由中layout
页不会被刷新 server action
,缺点:只接受post
请求,路由中layout
页不会被刷新- 本地异步获取信息,缺点:不是服务端渲染,服务端不会输出任何静态资源,浏览器必须允许JS执行下才能运行
- 继续往下看高级用法
相关链接: https://segmentfault.com/q/1010000044106831/a-1020000044112750
附加情况:页面小部件
- 例如:个人中心出票状况、最新订单发货进度等等,这种可能在个人中心某一处位置的小部件,它不是整个页面主导部分,但又需要实时同步状态;
- 那么建议通过本地异步请求状态,而不要使用服务端加载(这就意味着在并行路由中,获取状态需要在本地进行异步
fetch
请求) - 或者继续往下看高级用法
当然,如果存在非单一用户产生的状态(出票状况,快递进度、订单状态等),而又不需要实时同步信息的页面。无需考虑以上情况。 为何强调非单一用户,因为单一用户产生的状态可以通过
server action
提交信息的同时无感刷新路由视图
源码解析:
趴了源码看到这段,会阻止所有Link
标签的点击事件:
function linkClicked(e, router, href, as, replace, shallow, scroll, locale, isAppRouter, prefetchEnabled) {
const { nodeName } = e.currentTarget;
// anchors inside an svg have a lowercase nodeName
const isAnchorNodeName = nodeName.toUpperCase() === "A";
if (isAnchorNodeName && (isModifiedEvent(e) || // app-router supports external urls out of the box so it shouldn't short-circuit here as support for e.g. `replace` is added in the app-router.
!isAppRouter && !(0, _islocalurl.isLocalURL)(href))) {
// ignore click for browser’s default behavior
return;
}
e.preventDefault();
const navigate = ()=>{
// If the router is an NextRouter instance it will have `beforePopState`
const routerScroll = scroll != null ? scroll : true;
if ("beforePopState" in router) {
router[replace ? "replace" : "push"](href, as, {
shallow,
locale,
scroll: routerScroll
});
} else {
router[replace ? "replace" : "push"](as || href, {
forceOptimisticNavigation: !prefetchEnabled,
scroll: routerScroll
});
}
};
if (isAppRouter) {
_react.default.startTransition(navigate);
} else {
navigate();
}
}
高级用法:
- 目录:
/rendering/src/app/link/server-action
(查看) - 原理:服务端通过
server action
刷新,客户端通过异步发起请求,并通过React Cache
或zustand
这类状态机记录请求步骤
解决的问题:
- 能够每次导航后更新当前数据和
page
视图,不刷新整个layout
,能够做到无感更新数据; - 不需要通过给url添加随机
hash
后缀,也不用手动刷新页面;
再补充一个群里提到的一个坑点:
App Dir模式下不支持waitUntil
当时给出了3个方案:
server action
非表单默认提交(无效),从上面例子中证实,server action
并不根据<Link>
组件跳转而执行;Api Route
异步fetch
,有效但设置很繁琐;middleware
发起异步fetch
(推荐),因为一个页面内容可以no data
,但是绝对不会没有header
;
构建导出包含3个模式:default
、export
、standalone
export:纯静态模式,或采用客户端单页应用模式
方法:在next.config.js
中添加output: 'export'
说明:纯静态化,将根据路由导出静态文件到out
目录,需要借助web server运行 (示例)
缺点:不能在服务端进行动态路由处理,动态函数处理 (查看)
default:默认模式
方法:在next.config.js
去掉output
属性(默认没有启用)
说明:支持服务端静态和动态导出,导出将存放至.next
目录,部署时需要按照开发环境目录连同.next
一起运行
缺点:项目占用大;不支持monorepo
模式;依赖next cli
和node_modules
;
standalone:独立模式
方法:在next.config.js
中添加output: 'standalone'
说明:
- 支持服务端静态和动态导出,导出将存放至
.next/standalone
目录,部署时需仅需提供standalone
目录,运行node server.js
- 其中
public
或.next/static
需要手动添加到standalone
目录,官方推荐存放至CDN,这样就需要在next.config.js
中用到第二个属性:assetPrefix
优点:项目占用小,仅根据所需文件追踪;支持monorepo
模式;不依赖next cli
和node_modules
(也拷贝到standalone
目录),通过node
即可启动服务
对于monorepo
模式,standalone
还提供几个实验性的功能:outputFileTracingExcludes
、outputFileTracingIncludes
、outputFileTracingRoot
,说明见next.config.js
(查看)
对于
default
模式和standalone
模式,如果项目中有使用next\image
加载器的情况,构建时官方建议安装sharp
依赖 (查看)
当前默认认为看的用户能力均能本地安装和运行,所以这里直接采用官方提供的内容,不做额外说明,对于我运行的环境补充一下。
- 系统:macOS, Windows (including WSL), and Linux(我是OSX)
- node版本:v18.12.1(官方推荐^16.8,16.0运行过会报错)
- React:18.2.0
- 由于仓库已安装过了,如果需要本地运行,请clone项目后直接
npm install
- 在官方文档中提到过,如果要在包含
TypeScript
的服务端组件中使用async
/await
,请确保你的TypeScript
的版本为5.1.3
或更高,@types/react
版本为18.2.8
或更高 (查看)
以下是官方文档内容:
This is a Next.js project bootstrapped with create-next-app
.
First, run the development server:
npm run dev
# or
yarn dev
# or
pnpm dev
Open http://localhost:3000 with your browser to see the result.
You can start editing the page by modifying app/page.tsx
. The page auto-updates as you edit the file.
This project uses next/font
to automatically optimize and load Inter, a custom Google Font.
To learn more about Next.js, take a look at the following resources:
- Next.js Documentation - learn about Next.js features and API.
- Learn Next.js - an interactive Next.js tutorial.
You can check out the Next.js GitHub repository - your feedback and contributions are welcome!
The easiest way to deploy your Next.js app is to use the Vercel Platform from the creators of Next.js.
Check out our Next.js deployment documentation for more details.
尝试了一次从13.4.9
更新到13.5.2
,做一个初步的记录,更新方法如下:
npm i next@latest react@latest react-dom@latest eslint-config-next@latest
一些差异:
next.config.js
中experimental
不再需要设置appDir
,也支持并行渲染路由段下添加子目录,升级后将其去掉next dev
环境下,不再展示fetch
的情况,包括缓存激活,丢失的情况,渲染树的情况- 修复了上面提到的在
server action
中重定向redirect
报错 - 离谱的来了,
Api
路由中必须返回Response
,这就意味着,请不要试图通过redirect
或者NextResponse.redirect
去进行重定向,而是手写一个带有30*
头部的Response
(对于这个问题,官方文档到目前还未改动)
这次更新更多内容见官方发布:https://nextjs.org/blog/next-13-5
废弃的Metadata属性: colorScheme
、themeColor
、viewport