首页 > 其他分享 >使用 NextJS 和 TailwindCSS 重构我的博客

使用 NextJS 和 TailwindCSS 重构我的博客

时间:2022-10-07 18:33:55浏览次数:92  
标签:重构 const title Post posts NextJS TailwindCSS post id

使用 NextJS 和 TailwindCSS 重构我的博客_postgresql

  • git地址
  • 在线地址

这是笔者第三次重构博客,虽然博客应用是最简单的应用,但学习新技术何不从重构博客开始?

  • 第一版:使用 Hexo 和 Github pages
  • 优点:重新部署只要花5分钟,内容管理在本地 纯静态、免费;
  • 缺点:依赖Github,国内访问困难;
  • 第二版:React + Antd + Mysql 服务器是阿里云 ESC 最低配
  • 优点: 感觉没什么优点;
  • 缺点: 浏览器渲染,搜索引擎无法收录 ESO 优化难,Antd 组件使用方便,但前台页面定制需要覆盖样式;
  • 第三版:NextJS + TailwindCSS + Postgresql
  • 优点: 服务端渲染(SSR) + 静态生成, 访问速度极快,全新 UI 支持换肤;

TailwindCSS

在国外如火如荼,但是在国内却很少看到在生产上应用,对我来说, TailwindCSS 不仅仅是一个原子类的超级样式库;

1、我们在写样式的时候,经常会写类名,团队成员之间会存在样式冲突的可能,虽然我们可以使用 css modules 来避免,但却会存在取类名称的疲劳的问题,重复的类名称 ​​-header,-body -container --wrapper​​等;

2、Utility-First: 默认采用 rem 单位, 变量也就是16 的倍数, ​​px-1​​​是 16 的 ​​1/4​​​ 也就是 4 px,我们不会写出13px、17px 等不统一的单位变量,正所谓失之毫厘,差之千里。 配合 VScode 插件, 我们可以根据提示实时看到实际单位数值,写出高度还原高保真的样式;

使用 NextJS 和 TailwindCSS 重构我的博客_css_02

3、jwt 模式: just-in-time 模式,可以写出在原子类之外的样式,比如: ​​w-[762px]​​​表示​​width:762px​​​, ​​grid-cols-[1fr,700px,2fr]​​​ 表示​​grid-template-columns: 1fr 700px 2fr;​​​ 当然还有​​h-[calc(1000px-4rem)]​​​等等,这些都是运行时才生成的样式;配合在​​tailwind.config.js​​​ 中加入​​purge: ['./src/**/*.{js,ts,jsx,tsx}']​​打包时只会提取使用到的样式,让应用css最小化。

4、之前写了《使用 CSS variables 和Tailwind css实现主题换肤》也运用到了我的博客中。

Next.js

next.js 是一个 react 服务端渲染框架,相比react单页应用,网络爬虫可以识别 HTML 语义标签,更有利于 SEO。

接下来介绍下 NextJS 主要 API:

getServerSideProps 服务端渲染

下面是最简单的客户端渲染代码

import React, { ReactElement, useEffect, useState } from 'react'
import { useParams } from "react-router-dom";

export default function Post(): ReactElement {
let { slug } = useParams();
const [post, setPost] = useState({
title:'',
content:''
})
useEffect(() => {
fetch(`/api/post/${slug}`).then((res)=>res.json()).then((res)=>{
setPost(res)
})
}, [])
return (
<>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{
__html:post.content</div>
</>

改成 NextJS 后的代码如下

// pgaes/blog/[slug].tsx
import React, { ReactElement } from 'react'

export default function Post({ post }): ReactElement {
return (
<>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{
__html:post.content</div>
</>
)
}

export async function getServerSideProps(context) {
const { slug }=context.params
const res = await fetch(`https://.../api/post/${slug}`)
const post = await res.json()

return {
props: {
post
},
}
}

​getServerSideProps​​ 是在node端处理,每个 request 请求时执行。

而文章内容写完之后是通常不变的,所以可以先将页面静态存储在服务器上,这样就可以大大减小数据库压力。

getStaticProps 在构建时请求数据。

export async function getStaticProps(context) {
// fetch data
return {
props: {
//data

这样就需要在构建时获取全部文章列表,而博客详情页是一个动态路由,就需要 ​​getStaticPaths​​ 这个API

getStaticPaths 构建时获取动态路由的数据

export async function async getStaticPaths() {
const slugs= await getAllSlugs()
return {
paths: slugs.map(slug=>({
params:slug
})),
fallback: true //or false

当网站构建后,新写的文章也需要生成静态页面,这时就可以将​​fallback​​ 设置为true, 如果设为false,则在构建之外的文章都将返回404页面。

下面是文章详情页的主体代码

// pages/posts/[slug].js
import { useRouter } from 'next/router'

function Post({ post }) {
const router = useRouter()

// 如果页面还没静态生成,则先显示下面的loading
// 直到 `getStaticProps()`运行完成
if (router.isFallback) {
return <div>Loading...</div>
}

// Render post...
}

// 在构建时运行,获取全部文章路径
export async function getStaticPaths() {
return {
// 在打包时值生成 `/posts/1` 和 `/posts/2` 的静态页面
paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
// 开启其他页面的静态生成
// For example: `/posts/3`
fallback: true,
}
}

// 在构建时运行,根据params中的id 获取文章详情
export async function getStaticProps({ params }) {
// 如果页面的路由是 /posts/1, 这 params.id 的值就是1
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()

// 把数据专递给页面的props
return {
props: { post },
//当请求进入的时候再次生成文章详情页,比如修改文章重新生成
// 1s 内最多生成1次
reval(231, 243, 237); padding: 0px 3px; border-radius: 4px; overflow-wrap: break-word; text-indent: 0px;">​Prisma Migrate ​​命令, Prisma Schema 会生成 SQL 迁移并根据数据库执行它们。Prisma CRUD 查询由 Prisma Client 提供,这是一个针对 Node.js 和 TypeScript 的轻量级且完全类型安全的数据库客户端。

对比下二者代码

  1. Prisma Schema
model User {
id Int @id @default(autoincrement())
name String?
email String @unique
posts Post[]
}

model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
authorId Int?
author User? @relation(fields: [authorId], references: [id])
}

Schema 是一个描述文件,描述了数据模型直接的关系,再通过​​prisma generate​​ 生成 typescript 声明文件。

  1. TypeORM Entity
import { Entity, PrimaryGeneratedColumn, Column, OneToMany, ManyToOne } from 'typeorm'

@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number

@Column({ nullable: true })
name: string

@Column({ unique: true })
email: string

@OneToMany(
type Post,
post post.author
)
posts: Post[]
}

@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number

@Column()
title: string

@Column({ nullable: true })
content: string

@Column({ default: false })
published: boolean

@ManyToOne(
type User,
user user.posts
)
author: User

Entity 是在运行时,代码通过​​@Entity()​​来实现 JavaScript 类的继承。

过滤

  1. Prisma
const posts = await postRepository.find({
where: {
title: { contains: 'Hello World'
  1. TypeORM
const posts = await postRepository.find({
where: {
title: ILike('%Hello World%'),
},
})

多对多关系级联操作

  1. Prisma
type Categories={
id?: number
name: string
createdAt?: Date | null
}[]

type PostBody = Post & {
categories: Categories;
};

const { title, summary, slug, content, published, categories } =
req.body as PostBody;

const connectOrCreate = categories.map(({ name }) => {
return {
create: {
name,
},
where: {
name,
},
};
});
const newPost = await prisma.post.create({
data: {
title,
summary,
slug,
content,
published,
categories: {
connectOrCreate,
},
user: {
connect: {
id: req.user.id,
},
},
},
include: {
categories: true,
},
});

文章和分类是多对多的关系,一篇文章可以有多个分类,一个分类下可以有多篇文章,

​categories​​​ 可以选择已经存在的分类,也可以是新加的分类,通过​​name​​唯一熟悉来判断是否要新增还是级联。

  1. TypeORM
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number

@Column()
@IsNotEmpty()
title: string

@Column({
select: false,
type: 'text',
})
content: string

@ManyToMany((type) => Category, {
cascade: true,//级联插入修改 boolean | ("insert" | "update" | "remove" | "soft-remove" | "recover")[]
})
@JoinTable()
categories: Category[]
}

const newPost = postRepository.create({
...ctx.request.body,
})

typeorm 通过​​cascade​​ 属性 就可以级联增、删、改 软删除 等

Postgresql

本次重构还讲数据库迁移到了 Postgresql。

1、MySQL 里有只有 utf8mb4 才能显示 emoji 的坑, Pg 就没这个坑;

2、Pg可以存储 array 和 json, 可以在 array 和 json 上建索引;

代码编辑器

从上一版是 codemiror​ 和 remark 自己写的组件 ,这一版发现掘金的 Markdown 编辑比较好用,就直接使用了bytemd​, 底层都是使用了 remark 和 rehype,支持任何框架,并且拥有丰富的插件,还是比较好用的,但是在文章详情页却没有单独的 TOC(目录)组件,得单独封装一个TOC组件了。

小结

本文主要是笔者记录重构博客所用的知识和记录,当然还有很多不足,也还有很多功能得开发, 比如:图床、评论、SEO优化、 统计和监控等。

当然内容是最重要的,希望以后每周或者每两周能够有一篇文章,记录和总结知识。

喜欢的同学可以fork一下,免费部署到 Heroku​ 中,Heroku 支持免费的 Postgresql 数据库,也可以将程序部署到 vercel.app/ (国内比较快,不支持数据库),数据库还是选择 Heroku。

  • 收藏
  • 评论
  • 举报

上一篇:使用 CSS variables 和Tailwind css实现主题换肤

下一篇:使用 PostCSS 插件让你的网站支持暗黑模式

标签:重构,const,title,Post,posts,NextJS,TailwindCSS,post,id
From: https://blog.51cto.com/u_15757429/5734851

相关文章

  • TailwindCSS 资源推荐
    前言TailwindCSS发布了3.0,功能也越来越好用,那么是否有与之相关的组件库呢?每个项目都有awesome,TailwindCSS也有awesome-tailwindcss,你可以在这里找到插件、工具、......
  • kruskal重构树and笛卡尔树
    为什么放在了一起因为我个人旗帜鲜明的认为这是一个东西前者可以旗帜鲜明的解决图上(包括树上)限定最值的联通问题建立方式:跑kruskal时每连接两个点时建立一个新点把原......
  • Unity接入穿山甲GroMore广告——Android_Unity重构偏1(序)
    之所以需要重构一下,是因为我已经弃用了Unity版本的GroMore,因为每一次升级,都是要命。那么重构开始:1、使用Unity发布一个空的Android工程。2、将GroMore的AndroidDemo工......
  • [学习笔记]Kruskal以及Kruskal重构树
    1.\(\operatorname{Kruskal}\)最小生成树本来觉得这个没必要写但是强迫症发作只能写了qwq真实原因是我居然交了四发才过板子题可以说是人类之耻了\(\operatorname{Kru......
  • 如何以干净的方式在 NextJs 中创建请求中间件
    如何以干净的方式在NextJs中创建请求中间件中间件是每个API的一个重要方面,它们在NextJs项目中也很有用,但官方文档没有教你如何实现它们,幸好你找到了这篇我将教你如......
  • 计算机系统课程和重构设计模式图书推荐
    ▲计算机系统课程▲重构和设计模式的推荐书籍......
  • 房产中介管理软件第1天:为什么要重构下房产中介管理软件
    以前维护过公司的一个房地产中介管理软件,使用的架构是C#.NetFramework4.0+WebForm+NHibernate,数据库是SQLServer2008,前端使用的JQuery,权限验证使用的是Session......
  • 如何保持 NextJS 项目的清晰和干净
    如何保持NextJS项目的清晰和干净已经使用NextJS实现了许多项目,我真的不得不说,我喜欢它。您构建Web应用程序的速度,尤其是与Tailwind结合使用的速度令人难以置信。......
  • tailwindcss 设置图片位置
    链接https://tailwindcss.com/docs/object-position图示......
  • 基于ts重构axios
    ustbhuangyi老师的基于TypeScript从零重构axios学习记录。知识点TypeScript常用语法:基础类型、函数、变量声明、接口、类、泛型、类型推新、高级类型......