You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MERN栈实现类似ProductHunt按天加载帖子的路由与触发时机问题

解决MERN栈按日期滚动加载帖子的两个核心问题

嘿,这两个问题在实现类似ProductHunt的滚动加载时很典型,我给你拆解一下实用的解决方案:

问题1:判断滚动加载的时机(无需纠结每日帖子数量)

不用死磕单天帖子的数量,Intersection Observer API是目前最优的方案——它能自动监听某个元素是否进入(或即将进入)视口。你只需要给列表里的最后一个帖子元素加个监听,当它出现在视口中时,就触发加载前一天数据的请求。这样不管单天有10条还是100条帖子,都能精准触发,比监听scroll事件高效且省心。

React端实现示例

在你的帖子列表组件里,给最后一个帖子绑定ref,再用IntersectionObserver监听它:

import { useEffect, useRef, useState } from 'react';
import axios from 'axios';

const PostList = () => {
  const [posts, setPosts] = useState([]);
  const [currentDate, setCurrentDate] = useState(new Date()); // 初始加载当天内容
  const [isLoading, setIsLoading] = useState(false);
  const [hasMore, setHasMore] = useState(true);
  const lastPostRef = useRef(null);

  // 格式化日期为YYYY-MM-DD,方便传给后端
  const formatDate = (date) => date.toISOString().split('T')[0];

  // 获取指定日期的帖子
  const fetchPostsForDate = async (date) => {
    if (isLoading || !hasMore) return;
    setIsLoading(true);
    
    try {
      const formattedDate = formatDate(date);
      const res = await axios.get(`/api/posts?date=${formattedDate}`);
      
      // 把新帖子追加到列表末尾(如果要新内容在上,就用unshift)
      setPosts(prev => [...prev, ...res.data]);
      
      // 如果返回空数组,说明这天没有帖子,停止后续请求
      if (res.data.length === 0) {
        setHasMore(false);
      } else {
        // 更新当前日期为前一天
        const prevDate = new Date(date);
        prevDate.setDate(prevDate.getDate() - 1);
        setCurrentDate(prevDate);
      }
    } catch (err) {
      console.error('加载帖子失败:', err);
    } finally {
      setIsLoading(false);
    }
  };

  // 初始化加载当日帖子
  useEffect(() => {
    fetchPostsForDate(currentDate);
  }, []);

  // 设置Intersection Observer监听最后一个帖子
  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        const [entry] = entries;
        if (entry.isIntersecting && hasMore) {
          fetchPostsForDate(currentDate);
        }
      },
      { threshold: 0.1 } // 当元素10%进入视口时触发
    );

    if (lastPostRef.current) {
      observer.observe(lastPostRef.current);
    }

    // 组件卸载时取消监听
    return () => {
      if (lastPostRef.current) {
        observer.unobserve(lastPostRef.current);
      }
    };
  }, [currentDate, hasMore]);

  return (
    <div className="post-list">
      {posts.map((post, index) => (
        <div
          key={post._id}
          className="post"
          // 给最后一个帖子绑定ref
          ref={index === posts.length - 1 ? lastPostRef : null}
        >
          <h3>{post.title}</h3>
          <p>{post.content}</p>
          <small>发布于: {new Date(post.createdAt).toLocaleDateString()}</small>
        </div>
      ))}
      {isLoading && <div className="loading">加载中...</div>}
      {!hasMore && <div className="no-more">没有更多帖子啦</div>}
    </div>
  );
};

export default PostList;

问题2:路由设计(绝对不需要每天一个路由)

完全没必要为每一天单独创建路由,只需要在现有路由基础上加个日期查询参数即可。后端根据前端传入的日期,返回该日期当天创建的所有帖子,一个路由就能搞定所有日期的请求。

后端路由改造示例

修改你现有的/api/posts路由,添加对date参数的处理(还帮你修正了原代码里的res.send(500)错误,应该是res.status(500)):

router.get('/', async (req, res) => {
  try {
    const { date } = req.query;
    let posts;

    if (date) {
      // 计算目标日期的开始和结束时间(确保只查询当天的帖子)
      const targetDate = new Date(date);
      const startOfDay = new Date(targetDate.setHours(0, 0, 0, 0));
      const endOfDay = new Date(targetDate.setHours(23, 59, 59, 999));

      // 查询该时间段内的帖子,按创建时间降序排列
      posts = await Post.find({
        createdAt: {
          $gte: startOfDay,
          $lte: endOfDay
        }
      }).sort({ createdAt: -1 });
    } else {
      // 没有传date参数时,默认返回当日帖子
      const todayStart = new Date();
      todayStart.setHours(0, 0, 0, 0);
      const todayEnd = new Date();
      todayEnd.setHours(23, 59, 59, 999);
      posts = await Post.find({
        createdAt: {
          $gte: todayStart,
          $lte: todayEnd
        }
      }).sort({ createdAt: -1 });
    }

    res.json(posts);
  } catch (err) {
    console.error(err.message);
    res.status(500).send('Server Error');
  }
});

额外优化建议

  • 给createdAt字段加索引,能大幅提升日期查询的速度
  • 如果某天帖子数量特别多,可以结合分页参数(比如?date=YYYY-MM-DD&limit=20),避免一次加载过多数据
  • 前端可以缓存已加载的日期数据,防止重复请求

内容的提问来源于stack exchange,提问作者Kittichote Chain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:08:04