低成本一套式成为全栈 Next+Prisma

学习本意的初衷是想体验一波SSR的渲染模式,利于优化SEO的开发。遇到Prisma后突发奇想的成为前后端一套代码实现的工具。感觉以前做的聊天系统都可以用它来存储聊天数据,用不到indexDB了。

接下来的学习中主要会分两模块去阐述,如果只是想感受下Prisma的开发模式可以跳到第二部分,此次开发以React框架展开实现

一. Next

1. 安装

npx create-next-app

npm i react-icons // 图标库

npm i react-textarea-autosize // 输入框

npm i react-markdown

npm i remark-gfm

npm i --save-dev @types/react-syntax-highlighter

npm i uuid

2. 开发前准备

  1. 安装后的目录结构上的特点是访问page内容的时候通过嵌套在layout实现,并且在实现功能上的交互点击效果需要在顶部增加'use client'转换为客户端组件,不然会报错
  2. 现有的Next会自动提示是否引入了Tailwindcss框架(比较流行的UI框架,还是很便于开发的)
  3. app内创建的文件都可以直接访问路径无需配置,如创建在目录下创建了 app/test/page.tsx,访问地址是http://localhost:3000/test即可
  4. 只有根的page文件才可以写body、html标签,其他的页面不需要
  5. SSR渲染模式是通过服务端渲染执行JS代码,动态生成html内容,把包含网页的html返回给客户端,实现组件方式更为细化的控制渲染模式,优先会先使用服务端组件,但是一些交互性的组件还是会用客户端组件方式呈现
  1. APP Router 文档的选择

2. 开发

浏览器标签页的名字,在当前page文件中设置

javascript 复制代码
export const metadata:Metadata = {
    title: 'chat测试标签名称',
    description: 'chat'
}

GET请求, 创建文件夹: /app/api/test/route.ts

javascript 复制代码
import { NextRequest, NextResponse } from "next/server";
export async function GET( request: NextRequest ) {
    const { url } = request
    return NextResponse.json({ url })

}

二. Prisma

安装

npm i prisma --save-dev

npx prisma init // 初始化生成数据库

Vscode的插件安装

配置 schema.prisma文件

javascript 复制代码
// 对话模型
model Chat {
  id  String  @id @default(uuid())
  title String 
  updateTime DateTime @updatedAt
  Message  Message[]
}
// 消息模型
model Message {
  id String @id @default(uuid())
  content String 
  role String
  createTime DateTime @default(now())
  chatId String 
  chat Chat @relation(fields: [chatId], references: [id])
}
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = "file:./chatgpt-app.sqlite" //数据库的路径
}

执行命令生成数据库

npx prisma migrate dev --name init

我们连接的是sqlite,那么我们本级电脑需要下载相应的环境、并在环境变量的path中进行配置,参考网站sqlite3官网的地址,配置后我们就可以执行

控制台执行方式: sqlite3 prisma/chatgpt-app.sqlite

可视化网站执行方式: npx prisma studio

通过prisma操作数据库

npm i @prisma/client

npx prisma generate // 更新安装的库

Next存在热重载机制,会存在重复数据库,那么我们需要创建相关配置维护全局的prisma的实例, 文件创建/lib/prisma.ts

javascript 复制代码
import { PrismaClient } from '@prisma/client'

const prismaClientSingleton = () => {
    return new PrismaClient()
};

type PrismaClientSingleton = ReturnType<typeof prismaClientSingleton>

const globalForPrisma = globalThis as unknown as {
    prisma: PrismaClientSingleton | undefined
}

const prisma = globalForPrisma.prisma || prismaClientSingleton()

export default prisma

if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma

编写请求的API路由

javascript 复制代码
// 请求路由 /api/message/update/route.ts
import { NextRequest, NextResponse } from "next/server";
import prisma from "@/lib/prisma";

export async function POST( request: NextRequest ) {
    const body = await request.json()
    const {id, ...data} = body
    if(!data.chatId) {
        const chat = await prisma.chat.create({ //創建新的對話放
            data: {
                title: '新對話'
            }
        })
        data.chatId = chat.id
    }
    let message = await prisma.message.upsert({
        create:data,
        update:data,
        where:{
            id
        }
    })
    return NextResponse.json({code: '00', data: {message}})
}

// 接口请求
const response:any = await fetch("/api/message/update", {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(message)
})

三. React

自定义context全局共享

方法引用

javascript 复制代码
 const {
    state: { dispalyNavigation }
 } = useAppContext()
 setState((v) => {
      return {
        ...v,
        dispalyNavigation: !v.dispalyNavigation
      }
})

方法定义

javascript 复制代码
'use client'

import { Dispatch,ReactNode,SetStateAction,createContext,useMemo,useState ,useContext} from "react"

type State = {
    dispalyNavigation: boolean
}
type AppCOntextProps = {
    state:State
    setState: Dispatch<SetStateAction<State>>
}
// 创建Context
const AppContext = createContext<AppCOntextProps>(null!)  
// 抛出方法
export function useAppContext() {
    return useContext(AppContext)
}
export default function AppCOntextProvider({children}:{children:ReactNode}) {
    const [state,setState] = useState<State>({dispalyNavigation:true})
    const contextValue = useMemo(()=> {
        return {state,setState}
    },[state,setState])
    return (
        // value={{state,setState}}
        <AppContext.Provider  value={contextValue}>
            {children}
        </AppContext.Provider>
    )

}

配合reducers改造实现

对原有的Congtext改造

javascript 复制代码
// 原有AppCOntextProps 方法
type AppCOntextProps = {
    state:State
    dispatch: Dispatch<Action>
}
// 原有const [state,setState] = useState<State>({dispalyNavigation:true})
const [state,dispatch] = useReducer(reducer,initialState)

// 原有改变值是通过setstate去改变的
 dispatch({type:ActionType.UPDATE, fieId:'dispalyNavigation',value:!dispalyNavigation})

新建出一个reduce处理的文件夹

javascript 复制代码
// 初始状态值参数
export type State = {
    dispalyNavigation: boolean
}

// 定义枚举类型
export enum ActionType {
    UPDATE= 'UPDATE',
}

// 更新数据传递的参数
type UpdateAction = {
    type: ActionType.UPDATE
    fieId: string
    value: any
}

export type Action = UpdateAction

// 初始化状态
export const initialState: State = {
    dispalyNavigation: true
}

export function reducer(
    state: State = initialState,
    action: Action //动作状态
): State {
    switch (action.type) {
        case ActionType.UPDATE:
            return {
                ...state,
                [action.fieId]: action.value
            }
        default: throw new Error()
    }
}
相关推荐
崔庆才丨静觅3 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60614 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了4 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅4 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅4 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅5 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment5 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅5 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊5 小时前
jwt介绍
前端
爱敲代码的小鱼5 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax