环境

  • Next.js 14
  • React 18
  • Mongodb

前言

花了两周时间学习了Next.js, 自己做了个demo,尝试了下服务器端渲染,客户端渲染,给人的感觉就是又像回到了asp.net MVC时代, 需要在页面初次加载时显示的数据可以使用ViewModel来解决,需要在页面上有交互、异步刷新的业务可以使用ajax来解决。

最主要的是整理了使用Next.js 项目结构,一些文件、目录应该怎么放。

项目结构(目录结构)

  1. 需要区分服务器端渲染和客户端渲染的页面,Next.js 14版本推荐服务器端渲染页面是放在app目录下,按照目录约定方式配置路由。 比如这里app/addTopic, app/editTopic就对应两个路由地址

http://localhost:3000/addTopic

http://localhost:300/editTopic.

如果需要带路由参数,是把文件夹名称使用中括号包装起来

  1. 需要通过客户端渲染的页面建议创建Views or Pages目录,主要是和服务器渲染组件区分开,并且使用'use client' 指令描述
  2. API Endpoint - Next.js 可以创建服务器端接口, 就像asp.net MVC里创建Controller/Action 可以在Razor 视图里异步调用。 默认放在app/api目录下,如上图,根据约定就会暴露出如下几个api endpoint

http://localhost:3000/api/topics

http://localhost:3000/api/topics/{id}

然后具体的http协议可以在代码里指定,可以是POST OR GET OR PUT. 后面会贴上参考代码。 这一点我个人发现如果是复杂一些的路由,可能会在api目录先出现嵌套很多的目录结构,比如说URL上包含很多查询参数,按照Restful URL设计的话, 此时项目结构可能就有点凌乱,体验不是很好。

API Endpoint 代码片段

app/topics/route.ts

/**
* POST
* http://localhost:3000/api/topics
* **/
export async function POST(request: any) {
const { title, description } = await request.json();
await connectMongoDb(); await Topic.create({ title, description });
return NextResponse.json({ message: "Topic Created" }, { status: 201 });
} /**
* GET
* http://localhost:3000/api/topics
* **/
export async function GET() {
await connectMongoDb();
const topics = await Topic.find();
return NextResponse.json(topics);
} /**
* DELETE
* http://localhost:3000/api/topics?id=123
* **/
export async function DELETE(request: any) {
const id = request.nextUrl.searchParams.get("id");
await connectMongoDb();
await Topic.findByIdAndDelete(id);
return NextResponse.json({ message: "Topic Deleted" }, { status: 200 });
}

app/topics/[id]/route.ts

import connectMongoDb from "@/libs/mongodb";
import Topic from "@/models/topic";
import { NextResponse } from "next/server"; /**
* http get
* http://localhost:3000/api/topics/[id]
*/
export async function GET(request: any, { params }: any) {
const { id } = params;
await connectMongoDb();
const topic = await Topic.findOne({ _id: id });
return NextResponse.json({ topic }, { status: 200 });
} /**
* http put
* http://localhost:3000/api/topics/[id]
*/
export async function PUT(request: any, { params }: any) {
const { id } = params;
//从request body 中解析参数newTitle, newDescription 给title, description 赋值
const { newTitle: title, newDescription: description } = await request.json();
await connectMongoDb();
await Topic.findByIdAndUpdate(id, { title, description });
return NextResponse.json({ message: "Topic Updated" }, { status: 200 });
}

使用postman测试接口, 在mongodb里查看数据

[POST]插入一条数据



[GET]获取所有数据

[PUT]修改数据

列表页面

const getTopics = async () => {
const res = await fetch('http://localhost:3000/api/topics', {
method: 'GET',
cache: 'no-cache'//不使用缓存
}); return res.json();
} export default async function TopicList() {
const topics = await getTopics(); return (
<>
{topics.map((t: Topic) => (
<div
key={t._id}
className="p-4 border border-slate-300 my-3 flex justify-between gap-5 items-start">
<div>
<h2 className="font-bold text-2xl">{t.title}</h2>
<div>{t.description}</div>
</div>
<div className="flex gap-2">
<RemoveBtn id={t._id} />
<Link href={`/editTopic/${t._id}`}>
<HiPencilAlt size={24} />
</Link>
</div>
</div>
))}
</>
)
}

列表页面使用服务器端渲染, 页面初始化时就去调用接口加载数据,这里会发现【删除】按钮就单独封装出来,需要客户端交互就需要使用客户端组件,也就是普通的React组件。

删除组件

删除组件顶部需要使用'use client'指令

'use client'

import { HiOutlineTrash } from "react-icons/hi";
import { useRouter } from "next/navigation"; export default function RemoveBtn({ id }: any) {
const router = useRouter();
const removeTopic = async () => {
const confirmed = confirm("Are you sure?");
if (confirmed) {
const res = await fetch(`http://localhost:3000/api/topics?id=${id}`, {
method: "DELETE"
}) if (res.ok) {
router.refresh();
}
}
} return (
<button onClick={removeTopic} className="text-red-400">
<HiOutlineTrash size={24} />
</button>
)
}

连接Mongodb

connect

import mongoose from "mongoose";

const connectMongoDb = async () => {
try {
await mongoose.connect(process.env.MONGODB_URI!);
console.log("Connected to MongoDB");
} catch (error) {
console.log(error);
}
} export default connectMongoDb;

Schema

import mongoose, { Schema } from "mongoose";

const topicSchema = new Schema(
{
title: { type: String, required: true },
description: { type: String, required: true }
},
{
timestamps: true
}
) const Topic = mongoose.models.Topic || mongoose.model('Topic', topicSchema); export default Topic;

参考

https://gitee.com/garfieldzf/next-topiclist-app

Next.js + Mongodb CURD的更多相关文章

  1. 8 步搭建 Node.js + MongoDB 项目的自动化持续集成

    任何事情超过 90 秒就应该自动化,这是程序员的终极打开方式.Automating shapes smarter future. 这篇文章中,我们通过创建一个 Node.js + MongoDB 项目 ...

  2. 《Node.js+MongoDB+AngularJS Web开发》读书笔记及联想

    总体介绍 <Node.js+MongoDB+AngularJS Web开发>,于2015年6月出版,是一本翻译过来的书,原书名为<Node.js,MongoDB and Angula ...

  3. Node.JS + MongoDB技术浅谈

    看到一个Node.JS + MongoDB的小样例,分享给大家.魔乐科技软件学院(www.mldnjava.cn)的讲座 Node.JS + MongoDB技术讲座          云计算 +大数据 ...

  4. 利用Sails.js+MongoDB开发博客系统

    http://yoyoyohamapi.me/categories/利用Sails-js-MongoDB开发博客系统/ 利用Sails.js+MongoDB开发博客系统 Apr 14, 2016 利用 ...

  5. AngularJS + Node.js + MongoDB开发

    AngularJS + Node.js + MongoDB开发的基于位置的通讯录(by vczero) 一.闲扯 有一天班长说了,同学们希望我开发一个可以共享位置的通讯录,于是自己简单设计了下功能.包 ...

  6. node.js + mongodb

    node.js + mongodb 这次内容是结合bootstrap把登陆注册做好,还有就是express的中间件等问题. 看这篇博客之前建议先看我上篇写的那篇博客http://www.cnblogs ...

  7. 用Node.JS+MongoDB搭建个人博客(页面模板)(五)(结束)

    <差不多先生> 我是差不多先生,我的差不多是天生.也代表我很天真,也代表我是个闲人.这差不多的人生,总是见缝插针. 求学的道路上总是孤独的,即使别人不理解我,认为我是奇葩!但没关系,我会坚 ...

  8. node.js+mongodb 爬虫

    demo截图: 本demo爬瓜子二手车北京区的数据 (注:需要略懂 node.js / mongodb 不懂也没关系 因为我也不懂啊~~~) 之所以选择爬瓜子二手车网站有两点: 一.网站无需登录,少做 ...

  9. 一个基于Vue.js+Mongodb+Node.js的博客内容管理系统

    这个项目最初其实是fork别人的项目.当初想接触下mongodb数据库,找个例子学习下,后来改着改着就面目全非了.后台和数据库重构,前端增加了登录注册功能,仅保留了博客设置页面,但是也优化了. 一.功 ...

  10. CentOS 7部署Node.js+MongoDB:在VPS上从安装到Hello world

    写好代码,花钱买了VPS,看着Charges一直上涨却无从下手?记一位新手司机从购买VPS到成功访问的过程 0.购买VPS 首先,选择VPS提供商,部署一个新的服务器(Deploy New Serve ...

随机推荐

  1. Java高并发Lock接口讲解,精准通知线程间的执行顺序

    题目:两个线程操作一个变量,实现两个线程对同一个资源一个进行加1操作,另外一个进行减1操作,且需要交替实现,变量的初始值为0.即两个线程对同一个资源进行加一减一交替操作. Lock接口与Conditi ...

  2. vue 从后端拿到验证码并点击刷新

    验证码登录的实现思路1.前端从后端拿到验证码图片2.输入验证码进行登录3.后端拿到验证码进行比对,正确登录成功. 前端请求验证码直接写在img标签中即可,不必单独发送axios请求 // templa ...

  3. Sql高级

    sql高级 1. 索引与视图 常见的数据结构 栈:先进后出 队列:先进先出 数组:查询快,根据下标查询 链表:分为双链表与单链表.单链表指向下一个数据的存储位置:双链表指向前一个与下一个数据的存储位置 ...

  4. TCP-UDP-Socket调试工具以及使用教程(亲测好用!)

    前言 TCP-UDP老程序都不陌生吧,面试常问.所以在网络编程与网络应用开发的过程中,调试是一个至关重要的环节,它帮助开发者确保数据能够准确无误地在不同节点之间传输.尤其当涉及到TCP/IP.UDP等 ...

  5. CUDA编程学习 (4)——thread执行效率

    1. Warp 和 SIMD 硬件 1.1 作为调度单位的 Warp 每个 block 分为 32-thread warp 在 CUDA 编程模型中,虽然 warp 不是显式编程的一部分,但在硬件实现 ...

  6. HAL+CubeIDE,STM32F407ZGT6正点原子探索者,舵机驱动,从零开始

    CubeIDE_HAL库_从零开始玩舵机 1.材料准备 开发板:正点原子STM32F407ZGT6探索者 舵机:SG90 舵机线材分辨:褐色 / 红色 / 橘黄色 -- GND / VCC / PWM ...

  7. .NET操作Excel高效低内存的开源框架 - MiniExcel

    .Net平台上对Excel进行操作主要有两种方式.第一种,把Excel文件看成一个数据库,通过OleDb的方式进行读取与操作:第二种,调用Excel的COM组件.两种方式各有特点. 今天给大家介绍第三 ...

  8. Windows高级调试

    文档摘要: 本书<Windows高级调试>主要讲解Windows高级调试技术和工具,包括调试器简介.调试器揭密.符号文件与源文件的管理.栈内存破坏.堆内存破坏.安全.进程间通信.资源泄漏. ...

  9. typescript 接口和对象类型(四)

    在typescript中,我们定义对象的方式要用关键字interface(接口), 使用interface来定义一种约束,让数据的结构满足约束的格式.定义方式如下:   // 定义一个接口类型 int ...

  10. clickhouse之安装与基本使用

    近期要做一个数据统计功能,公司选择了clickhouse作为数据库:下面记录一下该数据库的特性和使用教程. clickhouse是一个列式数据库,主要用于数据分析:从目前使用看来,特点如下: 列式存储 ...