# 进入生产阶段
在将 Next.js 应用程序投入生产之前,以下是一些确保最佳用户体验的建议。
- 尽可能使用缓存
- 确保您的数据库和后端部署在同一区域。
- 目标是尽可能减少 JavaScript 的数量。
- 推迟加载繁重的 JavaScript 包,直到需要时才加载。
- 确保日志记录已设置。
- 确保错误处理已设置。
- 配置 404(未找到)和 500(错误)页面。
- 使用以下方法提高性能:
- 第一章提到的 next/image 和自动图像优化
- 第一章提到的自动字体优化
- 第一章提到的脚本优化
# 缓存
缓存可缩短响应时间并减少对外部服务的请求数量。 /_next/static Next.js 自动将缓存标头添加到由 JavaScript、CSS、静态图像和其他媒体提供的不可变资产中。
Cache-Control: public, max-age=31536000, immutable
next.config.js 中设置的 Cache-Control 将在生产中被覆盖,以确保静态资产可以有效缓存。如果需要重新验证静态生成的页面的缓存,可以通过在页面的 getStaticProps 函数中进行设置 revalidate 来实现。如果您使用 next/image ,则默认的 image Optimization 也有特定的缓存规则。
注意 :当使用 本地运行应用程序时 next dev ,您的标头将被覆盖,以防止在本地缓存。
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
您还可以使用 getServerSideProps 和 API 路由中的缓存头来进行动态响应。例如,使用 stale-while-revalidate
// 该值在10秒内被认为是新鲜的(s-maxage=10)
// 如果在接下来的10秒内重复请求,缓存的值仍然是新的。如果在59秒之前重复请求,缓存的值将是过时的,但仍然呈现(stale-while-revalidate=59)
// 在后台,将发出重新验证请求以填充缓存具有新值。如果刷新页面,您将看到新值。
// with a fresh value. If you refresh the page, you will see the new value.
export async function getServerSideProps({ req, res }) {
res.setHeader(
"Cache-Control",
"public, s-maxage=10, stale-while-revalidate=59"
);
return {
props: {},
};
}
# 减少 JavaScript 大小
为了减少发送到浏览器的 JavaScript 数量,您可以使用以下工具来了解每个 JavaScript 包中包含的内容:
Import Cost– 在 VSCode 中显示导入包的大小。Package Phobia– 查找在项目中添加新的开发依赖项的成本。Bundle Phobia- 分析依赖项可以在多大程度上增加 bundle 大小。Webpack Bundle Analyzer– 使用交互式、可缩放的树图可视化 webpack 输出文件的大小。
在 next build 中, pages/ 目录中的每个文件都将自动被代码拆分为自己的 JavaScript 捆绑包。您还可以使用 “动态导入” 来延迟加载组件和库。例如,您可能希望推迟加载模块代码,直到用户单击打开按钮。
# 记录 (logging)
由于 Next.js 在客户端和服务器上运行,因此支持多种形式的日志记录:
console.log在浏览器中stdout在服务器上
# 部署
# Node.js 服务器
Next.js 可以部署到任何支持 Node.js 的托管提供商处。确保你的 package.json 文件中设置了 package.json 文件中存在 "build" 和 "start" 两个构建命令。
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
}
}
执行 next build 命令将在 .next 文件夹中构建出用于生产环境的应用程序。构建之后,执行 next start 命令启动一个支持 混合页面(hybrid pages) 的 Node.js 服务程序,该服务程序将同时服务于 静态生成的页面和服务器端渲染的页面 。
If you are using next/image, consider adding sharp for more performant Image Optimization in your production environment by running npm install sharp in your project directory. On Linux platforms, sharp may require additional configuration to prevent excessive memory usage.
如果你正在使用 next/image ,请考虑通过在项目目录中运行 npm install sharp ,在生产环境中添加 sharp 以获得更高性能的图像优化。在 Linux 平台上, sharp 可能需要额外的配置,以防止过度使用内存。
# Docker 镜像
具体查看官网
# 身份验证
身份验证去验证用户是谁,而授权控制用户可以访问的内容。
# 身份验证模式
确定您需要哪种身份验证模式的第一步是 了解您想要的数据获取策略 。然后我们可以确定哪些身份验证提供商支持此策略。主要有 两种模式 :
-
使用静态生成来服务器渲染加载状态,然后在客户端获取用户数据。
如果没有阻塞数据要求,Next.js 会自动确定页面是静态的。这意味着页面中不存在
getInitialProps和getServerSideProps。 相反,你的页面可以从服务器呈现加载状态,然后从客户端获取用户。这种模式的一个优点是,它允许从全局 CDN 提供页面,并使用 next/link 进行预加载。在实践中,这会导致更快的 TTI(交互时间)。
示例:这将首先渲染加载骨架。一旦对用户的请求完成,它将显示用户的姓名:
// pages/profile.js import useUser from "../lib/useUser"; import Layout from "../components/Layout"; const Profile = () => { // Fetch the user client-side const { user } = useUser({ redirectTo: "/login" }); // Server-render loading state if (!user || user.isLoggedIn === false) { return <Layout>Loading...</Layout>; } // Once the user request finishes, show the user return ( <Layout> <h1>Your Profile</h1> <pre>{JSON.stringify(user, null, 2)}</pre> </Layout> ); }; export default Profile; -
在服务器端获取用户数据,以消除未经身份验证的内容的闪现。
如果从页面导出一个名为getServerSideProps的异步函数,Next.js 将使用getServerSideProps返回的数据在每个请求中预呈现该页面。export async function getServerSideProps(context) { return { props: {}, // 将作为props传递给页面组件 }; }// pages/profile.js import withSession from "../lib/session"; import Layout from "../components/Layout"; export const getServerSideProps = withSession(async function ({ req, res }) { if (!req.session.user) { return { redirect: { destination: "/login", permanent: false, }, }; } return { props: { user }, }; }); const Profile = ({ user }) => { // Show the user. No loading state is required return ( <Layout> <h1>Your Profile</h1> <pre>{JSON.stringify(user, null, 2)}</pre> </Layout> ); }; export default Profile;这种模式的一个优点是在重定向之前防止未经身份验证的内容展示。需要注意的是,在 getServerSideProps 中获取用户数据将阻止渲染,直到对身份验证提供程序的请求得到解决。为了防止造成瓶颈并增加 TTFB(第一个字节的时间),您应该确保身份验证查找很快。否则,请考虑静态生成。
# 高级特性
# Next.js 编译器
Next.js 编译器是使用 SWC 在 Rust 中编写的,它允许 Next.js 转换和缩小你的 JavaScript 代码以用于生产。这取代了用于单个文件的 Babel 和用于缩小输出打包的 Terser。
使用 Next.js 编译器编译比 Babel 快 17 倍,并且从 Next12 版本开始默认启用。如果您有现有的 Babel 配置或正在使用不受支持的功能,您的应用程序将选择退出 Next.js 编译器,继续使用 Babel。
# 为什么选择 SWC
SWC 是一个基于 Rust 的可扩展平台,适用于下一代快速开发工具。
SWC 可用于编译、缩小、打包等,并且被设计为可扩展。您可以调用它来执行代码转换(内置或自定义)。运行这些转换是通过 Next.js 等更高级别的工具进行的。
选择基于 SWC 进行构建有以下几个原因:
可扩展性:SWC 可以用作 Next.js 中的 Crate,而无需分叉库或解决设计约束。性能:通过切换到 SWC,我们能够在 Next.js 中实现约 3 倍的快速刷新速度和约 5 倍的构建速度,并且仍有更多优化空间。WebAssembly:Rust 对 WASM 的支持对于支持所有可能的平台以及在任何地方进行 Next.js 开发至关重要。社区:Rust 社区和生态系统非常出色,并且仍在不断发展。
# 实验特点
-
缩小化
// next.config.js module.exports = { swcMinify: true, };您可以选择使用 Next.js 编译器进行缩小。这比 Terser 快 7 倍。
-
样式化组件 (看文档)
-
Jest (看文档)
-
遗留装饰器 (看文档)
-
导入源 (看文档)
-
不支持的功能
当您的应用程序有一个.babelrc文件时,Next.js 将自动回退到使用 Babel 来转换单个文件。这确保了与利用自定义 Babel 插件的现有应用程序的向后兼容性。
# 动态导入
# 基本用法
import dynamic from "next/dynamic";
const DynamicComponent = dynamic(() => import("../components/hello"));
function Home() {
return (
<div>
<Header />
<DynamicComponent />
<p>HOME PAGE is here!</p>
</div>
);
}
export default Home;
DynamicComponent 将是返回的默认组件 ../components/hello 。它的工作方式类似于 常规的 React 组件 ,您可以像平常一样向它传递 props 。
注意:在 import('path/to/component') 中,必须明确写入路径。它不能是 模板字符串 ,也不能是 变量 。此外, import()必须位于 dynamic()Next.js 的调用中 ,才能将 webpack 包 / 模块 id 与特定 dynamic () 调用相匹配,并在渲染之前预加载它们。 dynamic()不能在 React 渲染内部使用 ,因为它需要在模块的顶层进行标记才能预加载工作,类似于 React.lazy .
# 自动静态优化
如果页面没有阻塞数据要求,Next.js 会自动确定页面是静态的(可以预先呈现)。该确定是通过页面中缺少 getServerSideProps 和 getInitialProps 来做出的。
此功能允许 Next.js 发出包含 服务器渲染页面和静态生成页面 的混合应用程序。
此功能的主要好处之一是,优化的页面不需要服务器端计算,并且可以从多个 CDN 位置立即传输给最终用户。其结果是为您的用户提供超快速的加载体验。
# 怎么运行的
如果页面中存在 getServerSideProps 或 getInitialProps ,Next.js 将切换为按需、按请求渲染页面(即服务器端渲染)。
如果不是上述情况,Next.js 将通过将页面预呈现为静态 HTML 来自动 静态优化您的页面 。
在 预渲染期间 ,路由器的对象将为空,因为我们在此阶段 query 没有可提供的信息。水化之后,Next.js 将触发应用程序更新以提供对象中的路由参数 query。
注意:使用 动态路由 添加到正在使用的页面的参数 getStaticProps 将始终在对象内可用 query 。
next build 将为静态优化的页面发出 .html 文件。例如, pages/about.js 页面的结果是:
.next/server/pages/about.html
如果您将 getServerSideProps 添加到页面中,那么它将是 JavaScript,如下所示:
.next/server/pages/about.js
# src 目录
页面(pages)也可以添加到 src/pages 目录下,作为根目录下的 pages 目录的替代品。
该 src 目录在许多应用程序中非常常见,并且 Next.js 默认支持该目录。
# 注意事项
- 如果根目录下的
pages目录存在的话,src/pages目录将被忽略 - 类似
next.config.js和tsconfig.json的配置文件应放在根目录中,将他们移至src下的话将无法使用。这同样适用于public目录
# 绝对导入
使用 paths 允许您配置模块别名。例如 @/components/* 到 components/* .
此配置的示例:
// tsconfig.json or jsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/components/*": ["components/*"]
}
}
}
// pages/index.js
import Button from "@/components/button";
export default function HomePage() {
return (
<>
<h1>Hello World</h1>
<Button />
</>
);
}
# Source Map
开发过程中默认启用源映射。在生产构建过程中,它们被禁用,因为生成源映射会显着增加生成时间和内存使用量。
Next.js 提供了一个配置标志,您可以使用它在生产构建期间启用浏览器源映射生成:
// next.config.js
module.exports = {
productionBrowserSourceMaps: true,
};
启用该选项后 productionBrowserSourceMaps ,源映射将输出到与 JavaScript 文件相同的目录中。Next.js 将根据请求自动提供这些文件。
注意:
- 添加源映射会增加
next build时间 - 在
next build期间增加内存使用量
# API
# next/router
import { useRouter } from "next/router";
function ActiveLink({ children, href }) {
const router = useRouter();
const style = {
marginRight: 10,
color: router.asPath === href ? "red" : "black",
};
const handleClick = (e) => {
e.preventDefault();
router.push(href);
};
return (
<a href={href} onClick={handleClick} style={style}>
{children}
</a>
);
}
export default ActiveLink;
以下是 userRouter 返回的一些参数:
pathname:当前路由。这个路径是/pages中的路径,不包括配置的basePath或者locale。query:查询字符串asPath:浏览器中显示的路径(包括 query),但没有配置basePath或locale。isFallback: 当前页面是否处于回退模式basePathlocalelocalesdefaultLocaledomainLocalesisReadyisPreview
# router.push
router.push(url, as, options);
进入某个路由;第二个参数 as,是为了装饰 URL ,如果你在服务器端设置了自定义路由将会起作用。
router.push("/login");
router.push({
pathname: "/post/[pid]",
query: { pid: post.id },
});
# router.replace
router.replace 将阻止在历史堆栈中添加新的 URL 条目。
router.replace(’/home’)
重定向路由;
# router.prefetch
预读取某个路由的组件内容;仅在生产环境下有效;
router.prefetch(’/dashboard’)
// 假设您有一个登录页面,登录后,您将用户重定向到仪表板。
// 对于这种情况,我们可以预取仪表板以进行更快的转换,如以下示例所示:
import { useCallback, useEffect } from "react";
import { useRouter } from "next/router";
export default function Login() {
const router = useRouter();
const handleSubmit = useCallback((e) => {
e.preventDefault();
fetch("/api/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
/* Form data */
}),
}).then((res) => {
// Do a fast client-side transition to the already prefetched dashboard page
if (res.ok) router.push("/dashboard");
});
}, []);
useEffect(() => {
// Prefetch the dashboard page
router.prefetch("/dashboard");
}, []);
return (
<form onSubmit={handleSubmit}>
{/* Form fields */}
<button type="submit">Login</button>
</form>
);
}
- 介绍:Next.js 一个非常实用的功能就是你在浏览当前页面的时候,Next.js 会自动 prefetch 页面上所有的站内链接页面。这样一来,当你真正点击某个链接时,就可以迅速加载该页面,快得简直就像是在读取本地文件。可能这就是为什么同样是 client-side navigation (客户端导航),Next.js 要比常规的 React SPA 的页面跳转快这么多吧!
- 这个功能在
<Link />中是默认开启的。除非像下面这样把<Link>的 prefetch 置为 false,Next.js 就不会在当前页面,再去 prefetch 该路由:
<Link href="/home" prefetch={false}>
<a>主页</a>
</Link>
-
prefetch 属性会在后台预读取目标路由页面,默认值为 true。它具有以下特性:
- 处于视口(viewport)中(初始或滚动后)的任何
<Link />都将被预加载。通过设置 prefetch={false} 可以禁止页面预取。 - 当 prefetch 被设置为 false 时,鼠标悬停在
<Link />上时仍然会触发预取。 - 使用 静态生成 的页面(pages)将预加载存有数据的 JSON 文件,以实现更快的页面切换。
- 预取功能只在生产环境中开启。
- 处于视口(viewport)中(初始或滚动后)的任何
-
prefetch 的内容是如何存储的?
通过<link rel="prefetch">的方式,浏览器会请求相应的 .js 和 .css 文件,但不会执行它们,而是作为缓存把它们储存在浏览器中。等到用户真正跳转到该 route,需要加载这些文件时,就可以直接从本地加载,不需要请求服务器了,其实也是一种前端提高性能的常见方法。
# router.beforePopState
在路由器处理事件之前拦截
# router.back
返回上一个路由;
# router.reload
重载当前路由;
# next/link
通过 Link 组件可以启用客户端的路由切换。
import Link from "next/link";
function Home() {
return (
<Link href="/about" passHref>
<MyButton />
</Link>
// /about?name=test
<Link
href={{
pathname: '/about',
query: { name: 'test' },
}}
>
<a>About us</a>
</Link>
// /blog/my-post
<Link
href={{
pathname: '/blog/[slug]',
query: { slug: 'my-post' },
}}
>
<a>Blog Post</a>
</Link>
);
}
# next/image
# 图片优化方案
- 使用 nextjs 内置的 Image 组件,该组件可以自动优化图片以提升网站性能。
- 使用 nextjs 内置的图片处理功能,自动调整大小、压缩和优化图片。
# Image 组件优势
-
性能提升:使用现代图片格式,为每台设备提供合适尺寸的图片
webp 是一种现代图像格式,可为 web 上的图像提供卓越的无损和有损压缩。使用 webp,网站管理员和 web 开发人员可以创建更小、更丰富的图像,从而使 web 更快,性能更好。 -
视觉稳定性:自动阻止非预期内容偏移,简称
CLS。- 网站中的资源是异步下载的,页面内容也是动态添加到页面上,所以内容发生非预期偏移场景比较常见。比如:未设置宽高的
<image>、<video>元素、实际使用的字体与备用大小不一致、可以动态变更大小的第三方广告或者组件都有可能是导致页面发生非预期偏移。 - 可以给
image、video元素设置size属性,或者预留image需要的布局空间。这个做法确保让浏览器在图片正在加载时就分配好正确的布局空间,避免非预期内容偏移。
- 网站中的资源是异步下载的,页面内容也是动态添加到页面上,所以内容发生非预期偏移场景比较常见。比如:未设置宽高的
-
更快的页面加载速度:图像只有在进入网页视口时才会被加载,并带有可选的模糊占位符,用于图片未加载完成时的临时占位图片,类似于图片懒加载技术。
# 用法
- Image 组件属性介绍
width和height属性:设置图片宽度和高度,可以只设置其中一个属性,另一个属性自适应调整大小。src属性:设置图片资源地址,可以是远程图片、本地图片。alt属性:图片加载失败时的文本提示。layout属性:设置图片在容器中如何展示。
# layout 属性
在不知道图像的宽度和高度的前提下,仍希望图片在保持纵横比的同时填满整个父盒子空间。在这种情况下,您可以省略 Image 组件上的 width 和 height 属性。相反,添加一个 layout="fill" 的 prop 属性。这会将图像拉伸到父盒子的宽度和高度。使用 layout="fill" 属性时,最好将其与 objectFit="cover" 配对。
import Image from "next/legacy/image";
import test from "@/assets/imgs/test.jpg";
export default function ImageCom() {
return (
// 给父元素设置容器大小,并设定position: "relative"。
<div className="ImageCom">
<div
className="img"
style={{ position: "relative", height: "300px", width: "500px" }}
>
<Image src={test} layout="fill" alt="加载失败"></Image>
</div>
<h5>图片优化</h5>
</div>
);
}
在保证图片纵横比不失真,不变形的同时,不适合父元素的图像部分将被剪裁。
-
layout属性取值fill:在x轴和y轴同比缩放,以保证图片能够充满整个父容器空间。responsive:在x轴缩放,以保证图片的宽度充满父容器空间,高度则不保证,高度会自适应。fixed:按图片自身宽度和高度精确尺寸。图像尺寸不会随着视口的变化而变化(无响应),类似于本地 img 元素了。
-
quality属性
优化图像的质量,是 1 和 100 之间的整数,其中 100 是最好的质量,默认为 75。<Image src={test} layout="responsive" quality={60} alt="加载失败"></Image> -
blurDataURL属性
blurDataURL属性是占位符图片,在src图片成功加载之前加载,必须是base64编码的图片,只有与placeholder="blur"结合使用时才有效。<Image src={test} layout="responsive" quality={60} alt="加载失败" placeholder="blur" blurDataURL="data:image/png;base64,..." ></Image>
# 远程图片
<Image
src="https://img0.baidu.com/it/u=726620871,4203188840&fm=253&fmt=auto&app=120&f=JPEG?w=1280&h=800"
width="300"
height="200"
></Image>
需要配置 next.config.js 文件,配置远程图片域名
const nextConfig = {
experimental: {
appDir: true,
},
images: {
domains: ["img0.baidu.com"],
},
};
module.exports = nextConfig;
# next/script
Next.js 提供了 next/script 组件来更加方便的管理和加载外部脚本。
它包含了;
- 异步加载外部脚本的能力,从而避免阻塞页面渲染。
- 脚本的优化,包括 preloading 和 prebrowsing。
- 支持内联脚本和外联脚本。
- 支持多种异步加载方式,例如 async, defer 等。
import Script from "next/script";
function MyComponent() {
return (
<>
<h1>Hello World</h1>
<Script
src="https://www.google-analytics.com/analytics.js"
strategy="afterInteractive"
/>
</>
);
}
export default MyComponent;
可以看到, src 属性指定了要加载的脚本的 URL,并且我们使用 strategy 属性指定了在何时加载脚本。
Next.js 提供了四种策略:
- beforeInteractive:在页面的 DOMContentLoaded 事件之前加载。
- afterInteractive:在页面的 DOMContentLoaded 事件之后,和其他 async 或 defer 的脚本之前加载。
- lazyOnload:在页面的 onload 事件之前加载。
- lazy:在页面的 onload 事件之后加载。
# next/head
Next.js 暴露了一个内置组件,用于将 HTML 标签添加到页面的 head 中:
import Head from "next/head";
function IndexPage() {
return (
<div>
<Head>
<title>My page title</title>
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
</Head>
<p>Hello world!</p>
</div>
);
}
// 为了避免在 head 中出现重复的 HTML 标签,你可以设置 key 属性,该属性将确保添加的标签仅渲染一次,如以下示例所示:
function IndexPage() {
return (
<div>
<Head>
<title>My page title</title>
<meta property="og:title" content="My page title" key="title" />
</Head>
<Head>
<meta property="og:title" content="My new title" key="title" />
</Head>
<p>Hello world!</p>
</div>
);
}
// 在这种情况下,仅渲染第二个 <meta property="og:title" />。具有相同 key 属性的 meta 标签将会被自动处理。
// 在 unmount 组件时,head 中的内容将会被清理,因此请确保每个页面都定义了其所需的所有内容,不要假设其它页面帮你添加任何内容。
// title、meta 或任何其它 HTML 标签(例如 script)必须作为 Head 元素的 直接 子元素存在, 或者被包裹在最多一层 <React.Fragment> 或数组中,否则在客户端导航时这些标签无法被正确识别并使用。
export default IndexPage;
# getInitialProps
getInitialProps 能够在页面中进行服务器端渲染,并允许你进行初始数据填充,这意味着在发送页面时已经在服务端中填充了数据。这对于 SEO 特别有用。
在页面组件中使用了 getInitialProps ,也就说明当前的页面是开启的 SSR 的渲染方式。
从 getInitialProps 返回的数据在服务端渲染时会被序列化,类似于 JSON.stringify 的做法。确保从 getInitialProps 返回的对象是一个纯粹的 Object,而不是使用 Date 、 Map 或 Set 。
执行时机:
getInitialProps 的执行时机分为两种情况,都是在运行时执行的:
- 直接访问页面时:
getInitialProps会在服务端执行 - 通过
next/link或next/router跳转到某个页面时,会在客户端执行
getInitialProps 只能在 页面组件 中使用,在其他子组件中是 不能使用 的。
getInitialProps 会根据情况在服务端或者客户端执行,如果我在 getInitialProps 进行了一些 node.js 或者操作了数据库这种纯服务端的操作,当 getInitialProps 在客户端执行时就会报错,除非自己去做兼容处理。由于执行环境不统一, 使用成本较高,因此在 getServerSideProps 引入后, getInitialProps 就不再被推荐使用了。
# next.config.js
# Base Path
module.exports = {
basePath: "/docs",
};
// 设置了basePath,这里会自动去到/docs/about
<Link href="/about">
<a>About Page</a>
</Link>
# 案例
const configs = {
// 编译文件的输出目录
distDir: "dest",
// 是否给每个路由生成Etag
generateEtags: true,
// 页面内容缓存配置
onDemandEntries: {
// 内容在内存中缓存的时长(ms)
maxInactiveAge: 25 * 1000,
// 同时缓存多少个页面
pagesBufferLength: 2,
},
// 在pages目录下那种后缀的文件会被认为是页面
pageExtensions: ["jsx", "js"],
// 配置buildId
generateBuildId: async () => {
if (process.env.YOUR_BUILD_ID) {
return process.env.YOUR_BUILD_ID;
}
// 返回null使用默认的unique id
return null;
},
// 手动修改webpack config
webpack(config, options) {
return config;
},
// 修改webpackDevMiddleware配置
webpackDevMiddleware: (config) => {
return config;
},
// 可以在页面上通过 procsess.env.customKey 获取 value
env: {
customKey: "value",
},
// 下面两个要通过 'next/config' 来读取
// 只有在服务端渲染时才会获取的配置
serverRuntimeConfig: {
mySecret: "secret",
secondSecret: process.env.SECOND_SECRET,
},
// 在服务端渲染和客户端渲染都可获取的配置
publicRuntimeConfig: {
staticFolder: "/static",
},
};