背景

随着ChatGPT的炽热,国外很多开发者疾速响应,利用于不同场景的AI利用井喷式的暴发,并且根本集中在web畛域利用,而在疾速开发的背地,咱们能够看到,开发者大多抉择Next.js或者Nuxt.js全栈框架来开发,以疾速验证本人的产品。这种选型的背地,我感觉次要起因有:

  • SEO的重要性

    国外更加重视SEO的重要性,国内搜索引擎大多是靠花钱买搜寻流量,包含小程序、App这类对SEO的需要并不大

  • Edge Function的衰亡

    Serverless使得前端开发能疾速开发全栈利用,不便的托管本人后端服务,而不必过多关注部署,然而他的毛病是,少数Serverless都是采纳容器化的计划,因而冷启动工夫长,如果在本人的云函数转发申请OpenAI接口,可能会产生申请工夫很长的状况。现在, Vercel、CloudFlare、Supabase等厂商都有了Edge Function的能力,使得函数能够在一些间隔用户更近的边缘节点运行,为了更快的冷启动工夫来疾速响应用户,这种计划个别也加了局部限度,然而仍旧失去很多开发者的青眼

  • 云服务厂商的多样性

    云服务厂商提供了很多根底服务,当把我的项目托管给Vercel等服务时,能够与Github集成进行继续部署,同时还会调配一个域名。很多其余厂商也提供了很多的收费后端存储服务,例如:

    • Upsatsh提供的Redis
    • Supabase提供的PostgreSQL
    • PlantScale提供的MySQL
    • Clerk提供的用户认证和用户治理

以上这些厂商的收费打算对于集体开发齐全够用,当然也能够依据产品规模应用付费打算

而本文旨在尝试开发一个简略的导航页面,满足本人的收集嗜好,用于解放本人的收藏夹,来学习Next.js开发,体验Next.js带来的开发全栈利用的便捷性。

初始化我的项目

随着以tailwindcssunocss这种原子化CSS计划的呈现,基于此衍生进去的UI组件库也很多,比方Radix、daisyUI、flowbite, 其中的RadixUI组件库相当重视网页的可拜访性,组件都遵循WAI-ARIA规范,这使得开发者构建可拜访的UI界面变得更加容易,而因为他专一于可拜访性、属于Headless UI也就是无具体款式类名代码,因而shadcn作者开发了shadcn/ui组件库,在此RadixUI组件库根底上赋予了简洁好看的款式,失去了很多开发者的青眼, 也十分举荐大家体验下。

这里间接抉择克隆该作者的Nextjs模版初始化我的项目

git clone https://github.com/shadcn/next-template

该我的项目应用了Next.js最新的app router版本,并且曾经集成了tailwindcssshadcn/ui组件库。这里抉择做导航网站也是因为它足够简略,要害款式是针对侧边栏,因为tailwindcss是挪动端优先,所以这里设置默认暗藏,当屏幕宽度大于sm时展现。

<div className="fixed z-20 hidden min-h-screen sm:block">    ...</div>

而对于列表区域,采纳grid布局,默认挪动端优先一列,依据屏幕大小展现2列或者3

<div className="grid grid-cols-1 gap-3 md:grid-cols-2 md:gap-6 lg:grid-cols-3">   ...</div>

其余的款式能够借鉴一下他人的网站设计简略丑化下,对于不太熟悉css并且不足审美的我花了不少的工夫在调整,总感觉网站不够好看,然而又不晓得该如何丑化。

数据库集成

定义模型

数据库集成这里我抉择了Prisma,相似的比拟热门的还有Drizzle,针对Prisma的具体概念、应用和它的长处,能够参考我整顿记录的笔记。执行

npx prisma init

会创立prisma/schema.prisma文件,创立模型如下

generator client {  provider = "prisma-client-js"}datasource db {  provider = "mysql"  url      = env("DATABASE_URL")}model Category {  id          String   @id @default(cuid())  icon        String  title       String  description String  rank        Int?  createdAt   DateTime @default(now()) @map(name: "created_at")  updatedAt   DateTime @default(now()) @map(name: "updated_at")  links       Link[]  @@map(name: "category")}model Link {  id          String   @id @default(cuid())  icon        String  url         String  title       String  description String  rank        Int?  public      Boolean  @default(true)  status      Int      @default(1) @db.TinyInt  createdAt   DateTime @default(now()) @map(name: "created_at")  updatedAt   DateTime @default(now()) @map(name: "updated_at")  cid         String  catagory    Category @relation(fields: [cid], references: [id])  @@map(name: "link")}

其中DATABASE_URL为近程数据库地址,这里我应用的是PlantScaleMySQL。当然你也能够应用SupabasePostgreSQL或者其余数据库,创立.env文件,填入服务地址

DATABASE_URL='mysql://user:password@aws.connect.psdb.cloud/dev?sslaccept=strict'

能够在这个可视化Prisma模型网站看到关系图如下, 别离示意类别实例和网站链接实例

表同步

而后执行命令将本地模型同步到数据库表

npx prisma db push

而后可能会遇到上面报错

查阅后发现官网在文档中有阐明,PlanetScale的MySQL数据库不反对外键,须要非凡的指定relationMode

datasource db {  provider     = "mysql"  url          = env("DATABASE_URL")  relationMode = "prisma"}

relationMode默认值是foreignKeys,当应用PlanetScale数据库的MySQL连接器时,应启用此选项, 在Prisma Client中模仿关系。再次执行db push指令,将模型同步到数据库

插入和查问数据

而后执行

npx prisma studio

关上表编辑器增加本人的数据

执行命令生成PrismaClient实例

pnpm install @prisma/clientnpx prisma generate

而后就能够通过关联关系一次性查问出数据

import prisma from '@/lib/db';import type { Prisma } from '@prisma/client';export default async function getNavLinks() {  const res = await prisma.category.findMany({    orderBy: [      {        rank: 'asc',      }    ],    include: {      links: {        orderBy: {          rank: 'asc',        },        where: {          public: true,          status: 1,        },      },    },  });  return res;}export type CategoryWithLinks = Prisma.PromiseReturnType<typeof getNavLinks>

用户认证

Next.js中集成用户认证非常简单,能够间接应用NextAuth

NextAuth

NextAuth是一个为Next.js应用程序提供的开源身份验证解决方案。默认状况下,NextAuth应用JSON Web Tokens(JWT)保留用户会话。NextAuth反对风行的登录服务,如GoogleFacebookAuth0Apple、电子邮件以及OAuth 1.02.0服务等等,是一种灵便可配置的身份验证解决方案。

首先装置所需依赖

pnpm install next-auth @next-auth/prisma-adapter

依照官网文档指引增加用户相干模型

model Account {  id                 String  @id @default(cuid())  userId             String  type               String  provider           String  providerAccountId  String  refresh_token      String?  @db.Text  access_token       String?  @db.Text  expires_at         Int?  token_type         String?  scope              String?  id_token           String?  @db.Text  session_state      String?  user User @relation(fields: [userId], references: [id], onDelete: Cascade)  @@unique([provider, providerAccountId])}model Session {  id           String   @id @default(cuid())  sessionToken String   @unique  userId       String  expires      DateTime  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)}model User {  id            String    @id @default(cuid())  name          String?  email         String?   @unique  emailVerified DateTime?  image         String?  accounts      Account[]  sessions      Session[]}model VerificationToken {  identifier String  token      String   @unique  expires    DateTime  @@unique([identifier, token])}

GitHub

获取Github客户端ID,参考文档,蕴含以下步骤:

  1. 关上GitHub应用程序页面,点击创立Github利用。

  2. 输出名称、主页、以及受权后的回调地址,这里开发环境时填localhost:3000,上线时切换成本人的线上域名即可。

  3. 点击”生成”并保留Client IDClient Secret

Google

参考NextAuth文档指引,在Google开发者网站注册利用并抉择api服务

利用类型抉择Web利用,相似Github填上可信赖的域名和回调地址确认

创立 API 路由

首先在.env文件中增加下面保留的认证相干密钥,其中NEXTAUTH_SECRET是用户生成JWT的密钥

# google登录GOOGLE_CLIENT_ID="GOOGLE_CLIENT_ID"GOOGLE_CLIENT_SECRET="GOOGLE_CLIENT_SECRET"# github登录GITHUB_CLIENT_ID="GITHUB_CLIENT_ID"GITHUB_CLIENT_SECRET="GITHUB_CLIENT_SECRET"NEXTAUTH_URL="http://localhost:3000"NEXTAUTH_SECRET="webnav"

能够看到下面的API回调地址别离是/api/auth/github/api/auth/google, 创立app/api/auth/[…nextauth]/route.ts 文件,并增加以下代码片段:

import NextAuth, { type NextAuthOptions } from "next-auth";import GoogleProvider from "next-auth/providers/google";import GitHubProvider from "next-auth/providers/github";import CredentialsProvider from "next-auth/providers/credentials";import { PrismaAdapter } from "@next-auth/prisma-adapter";import { PrismaClient } from "@prisma/client";import { compare } from "bcrypt";const prisma = new PrismaClient();export const authOptions: NextAuthOptions = {  adapter: PrismaAdapter(prisma),  providers: [    GitHubProvider({      clientId: process.env.GITHUB_CLIENT_ID!,      clientSecret: process.env.GITHUB_CLIENT_SECRET!,    }),    GoogleProvider({      clientId: process.env.GOOGLE_CLIENT_ID!,      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,    }),    CredentialsProvider({      name: "Credentials",      credentials: {        email: { label: "Email", type: "email" },        password: { label: "Password", type: "password" },      },      async authorize(credentials) {        const { email, password } = credentials ?? {}        if (!email || !password) {          throw new Error("Missing username or password");        }        const user = await prisma.user.findUnique({          where: {            email: credentials?.email,          },        });        // if user doesn't exist or password doesn't match        if (!user || !(await compare(password, user.password!))) {          throw new Error("Invalid username or password");        }        return user;      },    })  ],  session: {    strategy: "jwt",  },  debug: process.env.NODE_ENV !== "production",};const handler = NextAuth(authOptions);export { handler as GET, handler as POST };

其中,GitHubProvider用户Github登录,GoogleProvider用于Google登录,CredentialsProvider用于自定义登录,这里会查看邮箱明码, 匹配上了就返回用户信息。

同时还须要创立注册登录页面,创立app/login/page.tsx文件和app/register/page.tsx 文件,页面间接复制的taxonomy 页面款式,本人加上google登录按钮,成果如图

页面上通过

import { signIn, signOut } from "next-auth/react"

signIn办法容许应用GoogleGitHub帐户进行登录。 signOut容许登记用户会话, 具体应用能够参考官网文档这部分

部署

[Vercel](https://vercel.com/)中间接导入我的项目,批改构建命令为

npx prisma generate && next build

build之前学生成PrismaClient类型,不然编译时会因为类型报错而失败,同时增加.env中所须要的环境变量

同时因为咱们的数据源在数据库,而Nextjs默认是构建时生成页面的也就是SSG模式,在数据库有数据更新时咱们须要更新页面内容,因而咱们须要应用它的增量动态生成ISR(Incremental Static Regeneration)模式,参考官网文档,在page.tsx中增加导出,这里对更新时效性要求不高所以设置为1

export const revalidate = 24 * 60 * 60;

从构建日志中能够看到曾经失效了

部署胜利后绑定自定义域名就完结撒花了。

总结

本文以简略的导航网站举例,联合Next.jsPrismaNextAuthshadcn/ui 来学习如何构建全栈利用,你能够关上页面体验,也能够在本我的项目开源地址查看残缺代码,最初码字不易,如果你都看到这了,欢送star一波,感激。