1. 使用 getStaticProps
和 getStaticPaths
实现静态生成
getStaticPaths
设计思路
- 对于产品分类页面,由于分类可能动态更新,我们可以从数据库或 API 获取所有分类数据。例如,假设我们有一个数据库存储分类信息,我们可以编写如下代码获取分类路径:
export async function getStaticPaths() {
const res = await fetch('https://your-api.com/categories');
const categories = await res.json();
const paths = categories.map(category => ({
params: { categoryId: category.id.toString() }
}));
return { paths, fallback: false };
}
- 对于产品详情页面,我们需要考虑不同分类下产品数量庞大的情况。可以先获取每个分类下的产品列表,然后生成对应的路径。例如:
export async function getStaticPaths() {
const categoryRes = await fetch('https://your-api.com/categories');
const categories = await categoryRes.json();
const paths = [];
for (const category of categories) {
const productRes = await fetch(`https://your-api.com/products?categoryId=${category.id}`);
const products = await productRes.json();
for (const product of products) {
paths.push({
params: { productId: product.id.toString() }
});
}
}
return { paths, fallback: false };
}
- 对于产品变体,假设变体信息存储在产品对象中,我们可以在生成产品路径时,同时生成变体路径。例如:
export async function getStaticPaths() {
const categoryRes = await fetch('https://your-api.com/categories');
const categories = await categoryRes.json();
const paths = [];
for (const category of categories) {
const productRes = await fetch(`https://your-api.com/products?categoryId=${category.id}`);
const products = await productRes.json();
for (const product of products) {
paths.push({
params: { productId: product.id.toString() }
});
if (product.variants) {
for (const variant of product.variants) {
paths.push({
params: { productId: product.id.toString(), variantId: variant.id.toString() }
});
}
}
}
}
return { paths, fallback: false };
}
可能遇到的问题及解决方案
- 性能问题:如果分类和产品数量过多,生成路径的过程可能会很耗时。解决方案是可以在服务器端进行缓存,例如使用 Redis 缓存分类和产品数据,减少数据库或 API 的请求次数。同时,可以分页获取数据,分批次生成路径。
- 动态更新问题:如果分类或产品数据更新,已生成的路径可能不准确。可以通过设置较短的缓存时间或者使用增量静态再生来解决。
getStaticProps
设计思路
- 对于产品分类页面,
getStaticProps
可以根据 params.categoryId
获取该分类下的产品列表。例如:
export async function getStaticProps(context) {
const { categoryId } = context.params;
const res = await fetch(`https://your-api.com/products?categoryId=${categoryId}`);
const products = await res.json();
return {
props: {
products
},
revalidate: 60 * 5 // 5分钟后重新验证
};
}
- 对于产品详情页面,
getStaticProps
根据 params.productId
获取产品详细信息。如果有变体,根据 params.variantId
获取变体信息。例如:
export async function getStaticProps(context) {
const { productId, variantId } = context.params;
const res = await fetch(`https://your-api.com/products/${productId}`);
const product = await res.json();
let variant;
if (variantId) {
variant = product.variants.find(v => v.id.toString() === variantId);
}
return {
props: {
product,
variant
},
revalidate: 60 * 5 // 5分钟后重新验证
};
}
可能遇到的问题及解决方案
- 数据一致性问题:由于设置了
revalidate
时间,在重新验证前,页面显示的数据可能不是最新的。可以通过在管理后台更新数据时,主动触发页面重新生成(如果支持的话)。
- API 调用失败:如果 API 调用失败,
getStaticProps
会返回错误。可以设置重试机制,例如使用 axios - retry
库,对失败的 API 调用进行重试。
2. 数据缓存策略
- 服务器端缓存:使用 Redis 等缓存工具,缓存分类数据、产品列表数据和产品详情数据。在
getStaticPaths
和 getStaticProps
中优先从缓存获取数据,如果缓存不存在,再从数据库或 API 获取,并将获取到的数据存入缓存。例如,使用 ioredis
库:
const Redis = require('ioredis');
const redis = new Redis();
export async function getStaticPaths() {
let categories = await redis.get('categories');
if (!categories) {
const res = await fetch('https://your-api.com/categories');
categories = await res.json();
await redis.set('categories', JSON.stringify(categories));
} else {
categories = JSON.parse(categories);
}
const paths = categories.map(category => ({
params: { categoryId: category.id.toString() }
}));
return { paths, fallback: false };
}
- 客户端缓存:可以使用浏览器的
localStorage
或 sessionStorage
缓存一些不敏感且变动不大的数据,如分类列表。在页面加载时,先从本地缓存获取数据,减少服务器请求。但要注意设置合适的缓存过期时间。
3. 增量静态再生设计
- 原理:Next.js 的增量静态再生允许在页面已经生成静态 HTML 后,在后台重新生成该页面。我们在
getStaticProps
中设置 revalidate
字段,如上面代码示例中的 revalidate: 60 * 5
,表示 5 分钟后重新验证数据。
- 设计思路:对于产品分类页面和产品详情页面,根据数据变化的频率设置不同的
revalidate
时间。例如,对于变化频繁的分类,设置较短的 revalidate
时间;对于相对稳定的产品详情,设置较长的 revalidate
时间。
- 可能遇到的问题及解决方案:在重新验证期间,如果数据发生多次变化,可能导致页面数据不是最新的。可以通过结合管理后台,在数据更新时主动触发页面重新生成,或者缩短
revalidate
时间,但这可能会增加服务器负载。
4. 应对高并发访问的优化措施
- 负载均衡:使用 Nginx 等负载均衡器,将高并发请求均匀分配到多个服务器实例上,避免单个服务器过载。
- CDN:启用 CDN(如 Cloudflare、阿里云 OSS 等),缓存静态资源(如 HTML、CSS、JS 文件),将资源缓存到离用户更近的节点,加快用户访问速度,减轻服务器压力。
- 数据库连接池:在获取数据时,使用数据库连接池(如
mysql -2
的连接池),避免在高并发时频繁创建和销毁数据库连接,提高数据库访问效率。
- 优化静态文件:压缩和优化 CSS、JS 文件,减少文件大小,加快页面加载速度。可以使用工具如
terser
压缩 JS,css - minimizer - webpack - plugin
压缩 CSS。