关于typescript:使用Nextjs快速开发全栈导航网站

4次阅读

共计 6952 个字符,预计需要花费 18 分钟才能阅读完成。

背景

随着 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/client
npx 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 一波,感激。

正文完
 0