You need to enable JavaScript to run this app. 如何用NodeJS、MongoDB、Pug实现无刷新多Ajax更新.todos?
优惠活动
大模型
产品
解决方案
定价
更多

如何用NodeJS、MongoDB、Pug实现无刷新多Ajax更新.todos?

实现目标拆解

你当前已经完成了「点击复选框发送PUT请求更新数据库done状态」的功能,剩下的只需要打通两个环节:

  • 让更新数据的PUT接口返回渲染好的局部todo列表HTML
  • 前端在请求成功后,用返回的HTML替换掉原来的.todos容器内容,全程无需刷新页面
后端改造步骤

1. 抽离可复用的todo列表查询逻辑

原来的代码中todo接口的聚合查询没有做用户过滤,会把全库所有用户的todo都拉取排序,属于潜在逻辑bug,抽成公共函数后顺便修复:

const mongoose = require('mongoose');
const User = mongoose.model('User');

// 公共方法:获取当前用户按到期时间排序后的todo列表
async function getSortedUserTodos(userId) {
    return User.aggregate([
        { $match: { _id: new mongoose.Types.ObjectId(userId) } }, // 匹配当前登录用户,避免拉取其他用户数据
        { $unwind: '$todos' },
        { $sort: { 'todos.dueDate': 1 } },
        { $group: { _id: '$_id', todos: { $push: '$todos' } } },
    ]);
}

exports.todo = async (req, res) => {
    const user = await User.findById(req.user._id);
    if (user.todos.length) {
        const updatedUser = await getSortedUserTodos(req.user._id);
        res.render('todos', { updatedUser: updatedUser[0].todos });
    } else {
        res.render('layout');
    }
};

exports.addTodo = async (req, res) => {
    // 原有新增todo的逻辑保持不变
    const add = {
        todos: {
            task: req.body.task,
            dueDate: req.body.dueDate,
            dueTime: req.body.dueTime,
        },
    };
    await User.findOneAndUpdate(
        { _id: req.user._id },
        { $push: add },
        { new: true }
    ).exec();
    req.flash('success', 'saved');
    res.redirect('/');
};

exports.checkStatus = async (req, res) => {
    // 原有数据库更新逻辑
    await User.findOneAndUpdate(
        { todos: { $elemMatch: { _id: req.params.id } } },
        { $set: { 'todos.$.done': req.body.done } }
    );
    // 新增:拉取更新后的最新todo列表
    const updatedUser = await getSortedUserTodos(req.user._id);
    // 新增:返回纯局部HTML片段,不套公共页面布局
    res.render('todos', { updatedUser: updatedUser[0].todos, layout: false });
};

注意:res.render加layout: false参数,是为了避免Pug默认套入全局公共布局(导航、页脚等),我们只需要返回.todos区域对应的纯HTML结构。如果你的项目没有配置公共布局引擎,加这个参数也不会有副作用。

前端改造步骤

修改客户端的main.js,给已有的ajax请求补上成功回调逻辑,拿到返回的HTML后直接替换容器内容,不需要手动操作DOM移动todo项:

$('.todo--checkbox').change(function () {
    const todoId = $(this).data('value');
    const isChecked = this.checked;
    const $checkbox = $(this);
    $.ajax({
        url: `/todo/${todoId}`,
        type: 'PUT',
        data: { done: isChecked },
        // 请求成功后替换容器内容
        success: function (renderedHtml) {
            $('.todos').html(renderedHtml);
        },
        // 可选错误兜底:请求失败时把复选框状态改回去,避免和数据库状态不一致
        error: function () {
            $checkbox.prop('checked', !isChecked);
            alert('更新失败,请稍后重试');
        }
    });
});
实现优势
  • 完全复用原有Pug模板逻辑,不需要在前端硬编码拼接todo卡片的DOM结构,不会出现样式、渲染逻辑不一致的问题
  • 每次替换整个.todos容器时,后端会根据最新的done状态自动把待办项分到「未完成」「已完成」两个区域,不需要手动写DOM移动的逻辑
  • 原有Pug模板不需要做任何修改,GET全页面访问、AJAX局部更新用的是同一套渲染逻辑,行为完全一致

内容的提问来源于stack exchange,提问作者AmAn KumAr

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.01 19:06:00