低成本一套式成为全栈 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

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

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返回给客户端,实现组件方式更为细化的控制渲染模式,优先会先使用服务端组件,但是一些交互性的组件还是会用客户端组件方式呈现

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

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

  1. APP Router 文档的选择

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

2. 开发

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

export const metadata:Metadata = {
    title: 'chat测试标签名称',
    description: 'chat'
}

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

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的插件安装

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

配置 schema.prisma文件


// 对话模型
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

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

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

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

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

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

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

通过prisma操作数据库

npm i @prisma/client

npx prisma generate // 更新安装的库

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


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路由

// 请求路由 /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全局共享

方法引用

 const {
    state: { dispalyNavigation }
 } = useAppContext()
 setState((v) => {
      return {
        ...v,
        dispalyNavigation: !v.dispalyNavigation
      }
})

方法定义

'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改造

// 原有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处理的文件夹

// 初始状态值参数
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()
    }
}

原文链接:https://juejin.cn/post/7332386070048948276 作者:聆枫web

(0)
上一篇 2024年2月7日 下午4:21
下一篇 2024年2月7日 下午4:31

相关推荐

发表回复

登录后才能评论