MST

星途 面试题库

面试题:Next.js 复杂应用中数据流管理架构设计

假设要开发一个大型的电商平台,包含商品展示、用户订单管理、促销活动等多个复杂模块,使用 Next.js 进行前端开发。请你设计一个基于 getStaticProps、getServerSideProps 与 API Routes 结合的数据管理架构,详细说明不同模块的数据获取、更新以及缓存策略,并且要考虑高并发情况下的数据一致性和系统稳定性。
28.0万 热度难度
前端开发Next.js

知识考点

AI 面试

面试题答案

一键面试

1. 商品展示模块

  • 数据获取
    • 首页商品列表:使用 getStaticProps 进行预渲染。由于首页商品列表相对稳定,可设置 revalidate 时间(例如 60 秒),定时重新生成静态页面。这可以利用 CDN 缓存提高性能,在高并发下减少服务器压力。示例代码:
export async function getStaticProps() {
  const res = await fetch('https://api.example.com/products/top');
  const products = await res.json();
  return {
    props: {
      products
    },
    revalidate: 60
  };
}

const HomePage = ({ products }) => {
  // 渲染商品列表
};

export default HomePage;
  • 商品详情页:对于不太频繁更新的商品详情数据,使用 getStaticProps 并结合 params 获取具体商品信息。若商品信息更新频率较高,可搭配 revalidate 或切换到 getServerSideProps。示例代码(getStaticProps 结合 params):
export async function getStaticProps({ params }) {
  const res = await fetch(`https://api.example.com/products/${params.productId}`);
  const product = await res.json();
  return {
    props: {
      product
    }
  };
}

const ProductPage = ({ product }) => {
  // 渲染商品详情
};

export default ProductPage;
  • 数据更新:通过 API Routes 来处理商品数据的更新。例如,当商家修改商品信息时,发送 PUT 请求到 /api/products/:productId 接口,在服务器端更新数据库,并清除相关缓存(如果使用了缓存机制,如 Redis)。
// pages/api/products/[productId].js
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();

export default async function handler(req, res) {
  const { productId } = req.query;
  if (req.method === 'PUT') {
    const updatedProduct = await prisma.product.update({
      where: { id: productId },
      data: req.body
    });
    res.json(updatedProduct);
  } else {
    res.status(405).end();
  }
}
  • 缓存策略:在前端层面,使用浏览器缓存来减少重复请求。在服务器端,可采用 Redis 等缓存中间件。对于商品列表,缓存热门商品列表数据,设置合理的过期时间。商品详情页可根据商品 ID 缓存具体商品信息,当商品更新时,及时清除对应的缓存数据,以保证数据一致性。

2. 用户订单管理模块

  • 数据获取
    • 订单列表:使用 getServerSideProps,因为订单数据通常与用户登录状态紧密相关且需要实时获取最新数据。在请求订单列表时,通过 req 对象获取用户认证信息(如 JWT 令牌),并在服务器端验证后请求订单数据。示例代码:
export async function getServerSideProps(context) {
  const token = context.req.headers.authorization;
  const res = await fetch('https://api.example.com/orders', {
    headers: {
      Authorization: token
    }
  });
  const orders = await res.json();
  return {
    props: {
      orders
    }
  };
}

const OrderListPage = ({ orders }) => {
  // 渲染订单列表
};

export default OrderListPage;
  • 订单详情页:同样使用 getServerSideProps,根据订单 ID 获取具体订单详情。同时,验证用户是否有权限访问该订单(例如,订单是否属于当前用户)。
export async function getServerSideProps({ req, params }) {
  const token = req.headers.authorization;
  const res = await fetch(`https://api.example.com/orders/${params.orderId}`, {
    headers: {
      Authorization: token
    }
  });
  const order = await res.json();
  return {
    props: {
      order
    }
  };
}

const OrderDetailPage = ({ order }) => {
  // 渲染订单详情
};

export default OrderDetailPage;
  • 数据更新:订单状态更新等操作通过 API Routes 处理。例如,当订单状态从 “待支付” 变为 “已支付” 时,发送 PATCH 请求到 /api/orders/:orderId 接口,服务器端更新数据库并处理相关业务逻辑(如库存更新等)。同时,通过 WebSocket 等实时通信技术通知相关用户和系统模块订单状态的变化,以保证数据一致性。
// pages/api/orders/[orderId].js
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();

export default async function handler(req, res) {
  const { orderId } = req.query;
  if (req.method === 'PATCH') {
    const updatedOrder = await prisma.order.update({
      where: { id: orderId },
      data: req.body
    });
    res.json(updatedOrder);
  } else {
    res.status(405).end();
  }
}
  • 缓存策略:由于订单数据的实时性要求较高,缓存策略相对简单。可在短时间内(如几分钟)缓存已获取的订单列表和详情数据,以减少重复数据库查询。在更新订单数据时,及时清除相关缓存,确保后续请求获取到最新数据。

3. 促销活动模块

  • 数据获取
    • 促销活动列表页:对于促销活动列表,使用 getStaticProps 并设置 revalidate。促销活动通常有一定的时效性且不会频繁变动,可设置较长的 revalidate 时间(如 1 小时)。示例代码:
export async function getStaticProps() {
  const res = await fetch('https://api.example.com/promotions');
  const promotions = await res.json();
  return {
    props: {
      promotions
    },
    revalidate: 3600
  };
}

const PromotionListPage = ({ promotions }) => {
  // 渲染促销活动列表
};

export default PromotionListPage;
  • 促销活动详情页:若促销活动详情相对稳定,使用 getStaticProps;若活动实时性较强(如限时秒杀活动实时剩余库存等),使用 getServerSideProps。以限时秒杀活动为例,示例代码(getServerSideProps):
export async function getServerSideProps({ params }) {
  const res = await fetch(`https://api.example.com/promotions/${params.promotionId}`);
  const promotion = await res.json();
  return {
    props: {
      promotion
    }
  };
}

const PromotionDetailPage = ({ promotion }) => {
  // 渲染促销活动详情
};

export default PromotionDetailPage;
  • 数据更新:通过 API Routes 处理促销活动的创建、修改和删除操作。例如,创建新的促销活动时,发送 POST 请求到 /api/promotions 接口,服务器端验证数据并插入数据库。同时,更新相关缓存数据。
// pages/api/promotions.js
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();

export default async function handler(req, res) {
  if (req.method === 'POST') {
    const newPromotion = await prisma.promotion.create({
      data: req.body
    });
    res.json(newPromotion);
  } else {
    res.status(405).end();
  }
}
  • 缓存策略:对于促销活动列表和详情数据,使用缓存提高性能。在服务器端,采用 Redis 缓存促销活动数据,设置合理的过期时间。当促销活动数据更新时,及时清除对应的缓存数据,确保用户获取到最新的活动信息。同时,在高并发场景下,如限时秒杀活动,可使用分布式锁(如 Redis 分布式锁)来保证库存等关键数据的一致性。

高并发情况下的数据一致性和系统稳定性

  • 数据库层面:使用支持事务的数据库(如 PostgreSQL、MySQL 等),确保在高并发操作时数据的一致性。例如,在处理订单支付和库存更新时,将两个操作放在一个事务中,要么都成功,要么都失败。
  • 缓存层面:采用缓存更新策略,如写后失效、读写锁等。在高并发写操作时,通过分布式锁(如 Redis 分布式锁)保证缓存数据的一致性。同时,设置合理的缓存过期时间,避免缓存雪崩和缓存穿透问题。
  • 负载均衡:使用负载均衡器(如 Nginx、HAProxy 等)将高并发请求均匀分配到多个服务器实例上,减轻单个服务器的压力,提高系统的稳定性和可用性。
  • 监控与报警:搭建监控系统(如 Prometheus + Grafana)实时监控系统的各项指标(如 CPU 使用率、内存使用率、数据库连接数等),设置报警规则,当系统出现异常时及时通知运维人员进行处理,保障系统的稳定性。