From c9cfb833f2f28bcfaa47cacff086b54fcc049366 Mon Sep 17 00:00:00 2001 From: zhuba-Ahhh <3477826311@qq.com> Date: Tue, 27 Aug 2024 10:44:12 +0800 Subject: [PATCH 1/4] =?UTF-8?q?refactor:=20:recycle:=20=E5=86=85=E5=AE=B9?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/blog/[id]/TableOfContents.tsx | 12 +- src/data/blogPosts.ts | 435 +------------------------- src/data/ts/1.ts | 412 ++++++++++++++++++++++++ src/data/ts/2.ts | 343 ++++++++++++++++++++ src/data/ts/index.ts | 2 + 5 files changed, 768 insertions(+), 436 deletions(-) create mode 100644 src/data/ts/1.ts create mode 100644 src/data/ts/2.ts create mode 100644 src/data/ts/index.ts diff --git a/src/app/blog/[id]/TableOfContents.tsx b/src/app/blog/[id]/TableOfContents.tsx index f3b4bb9..22bcebf 100644 --- a/src/app/blog/[id]/TableOfContents.tsx +++ b/src/app/blog/[id]/TableOfContents.tsx @@ -49,26 +49,26 @@ const TableOfContents: React.FC = ({ headings }) => { return (

文章目录

}> - - - - ) -} -\`\`\` - -## 新的图片组件 - -Next.js 13引入了新的\`Image\`组件,它提供了更好的性能和用户体验: - -\`\`\`jsx -import Image from 'next/image' - -export default function Avatar() { - return ( - User Avatar - ) -} -\`\`\` - -新的\`Image\`组件支持: - -- 自动图片优化 -- 延迟加载 -- 响应式图片 -- 防止布局偏移 - -## 字体优化 - -Next.js 13引入了新的字体系统,它可以自动优化和加载自定义字体: - -\`\`\`jsx -import { Inter } from 'next/font/google' - -const inter = Inter({ subsets: ['latin'] }) - -export default function RootLayout({ children }) { - return ( - - {children} - - ) -} -\`\`\` - -这个系统可以: - -- 自动内联字体CSS -- 消除布局偏移 -- 预加载关键字体文件 - -## 从Next.js 12迁移到13 - -迁移到Next.js 13需要注意以下几点: - -1. 创建新的\`app\`目录,逐步迁移路由 -2. 更新\`next/image\`导入为\`next/legacy/image\` -3. 将客户端组件标记为\`'use client'\` -4. 更新API路由到新的\`app/api\`目录 -5. 使用新的数据获取方法替代\`getServerSideProps\`和\`getStaticProps\` - -## 结论 - -Next.js 13带来了许多令人兴奋的新特性,这些特性不仅提高了开发效率,还大大改善了应用性能和用户体验。App Router、服务器组件和流式渲染等新特性为我们提供了更灵活、更强大的工具,使得构建现代Web应用变得更加容易。 - -随着Next.js的不断发展,我们可以期待看到更多令人兴奋的新特性和改进。如果您还没有尝试过Next.js 13,现在是时候了! - `, + content: content2, date: "2024-03-20", author: "李四", tags: ["Next.js", "服务器组件", "性能优化"], diff --git a/src/data/ts/1.ts b/src/data/ts/1.ts new file mode 100644 index 0000000..438fa64 --- /dev/null +++ b/src/data/ts/1.ts @@ -0,0 +1,412 @@ +export const content1 = ` +# 深入理解React Hooks + +React Hooks是React 16.8中引入的新特性,它彻底改变了我们编写React组件的方式。本文将深入探讨Hooks的工作原理,包括常用Hooks的实现细节和使用技巧,以及如何创建自定义Hooks。 + +## 为什么需要Hooks? + +在Hooks出现之前,React组件主要分为类组件和函数组件。类组件可以使用状态和生命周期方法,而函数组件则更简单,但功能有限。Hooks的出现使得函数组件也能够使用状态和其他React特性,从而带来以下优势: + +1. 更简洁的代码 +2. 更容易复用逻辑 +3. 更好的性能优化 +4. 更容易理解和维护的组件 + +## 常用Hooks详解 + +### useState + +\`useState\`是最基本的Hook,用于在函数组件中添加状态。 + +\`\`\`jsx +import React, { useState } from 'react'; + +function Counter() { + const [count, setCount] = useState(0); + + return ( +
+

You clicked {count} times

+ +
+ ); +} +\`\`\` + +\`useState\`返回一个数组,第一个元素是当前状态值,第二个元素是更新状态的函数。 + +### useEffect + +\`useEffect\`用于处理副作用,如数据获取、订阅或手动修改DOM等。 + +\`\`\`jsx +import React, { useState, useEffect } from 'react'; + +function DataFetcher() { + const [data, setData] = useState(null); + + useEffect(() => { + fetch('https://api.example.com/data') + .then(response => response.json()) + .then(data => setData(data)); + }, []); // 空数组表示只在组件挂载时执行一次 + + return ( +
+ {data ?
{JSON.stringify(data, null, 2)}
: 'Loading...'} +
+ ); +} +\`\`\` + +\`useEffect\`接受两个参数:一个函数和一个依赖数组。函数在组件渲染后执行,依赖数组决定了effect何时重新运行。 + +### useContext + +\`useContext\`用于访问React的Context API,使得组件可以订阅上下文变化。 + +\`\`\`jsx +import React, { useContext } from 'react'; + +const ThemeContext = React.createContext('light'); + +function ThemedButton() { + const theme = useContext(ThemeContext); + return ; +} +\`\`\` + +### useReducer + +\`useReducer\`是\`useState\`的替代方案,用于管理复杂的状态逻辑。 + +\`\`\`jsx +import React, { useReducer } from 'react'; + +function reducer(state, action) { + switch (action.type) { + case 'increment': + return {count: state.count + 1}; + case 'decrement': + return {count: state.count - 1}; + default: + throw new Error(); + } +} + +function Counter() { + const [state, dispatch] = useReducer(reducer, { count: 0 }); + return ( + <> + Count: {state.count} + + + + ); +} +\`\`\` + +## 自定义Hooks + +创建自定义Hook允许你将组件逻辑提取到可重用的函数中。 + +\`\`\`jsx +import { useState, useEffect } from 'react'; + +function useWindowWidth() { + const [width, setWidth] = useState(window.innerWidth); + + useEffect(() => { + const handleResize = () => setWidth(window.innerWidth); + window.addEventListener('resize', handleResize); + return () => { + window.removeEventListener('resize', handleResize); + }; + }, []); + + return width; +} + +function MyResponsiveComponent() { + const width = useWindowWidth(); + return
Window width is {width}
; +} +\`\`\` + +## Hooks的使用规则 + +使用Hooks时需要遵循两个重要规则: + +1. 只在最顶层使用Hooks +2. 只在React函数中调用Hooks + +这些规则确保Hooks在每次渲染时都以相同的顺序被调用,这对于Hooks的正确工作至关重要。 + +## Hooks与类组件的对比 + +Hooks和类组件各有优势。Hooks通常能让代码更简洁,逻辑更容易复用,但类组件在某些场景下仍然有其优势,如错误边界。 + +以下是一个简单的对比: + +| 特性 | Hooks | 类组件 | +| ---- | ----- | ------ | +| 代码简洁性 | ✅ | ❌ | +| 逻辑复用 | ✅ | ❌ | +| 学习曲线 | 中等 | 较陡 | +| 性能 | ✅ | ✅ | + + +## 高级Hooks详解 + +### useRef + +\`useRef\`用于创建一个可变的ref对象,其.current属性被初始化为传入的参数。 + +\`\`\`jsx +function TextInputWithFocusButton() { + const inputEl = useRef(null); + const onButtonClick = () => { + // \`current\` 指向已挂载到 DOM 上的文本输入元素 + inputEl.current.focus(); + }; + return ( + <> + + + + ); +} +\`\`\` + +### useLayoutEffect + +\`useLayoutEffect\`与\`useEffect\`类似,但它会在所有的DOM变更之后同步调用effect。 + +\`\`\`jsx +useLayoutEffect(() => { + // 在DOM更新后立即执行 + // 适用于需要在浏览器绘制之前进行DOM测量的场景 +}, [dependency]); +\`\`\` + +## Hooks的实现原理 + +React Hooks的实现依赖于JavaScript的闭包机制。每次组件渲染时,React都会创建一个新的执行上下文,其中包含了该次渲染的props和state。 + +以\`useState\`为例,其简化实现可能如下: + +\`\`\`javascript +let state; +function useState(initialValue) { + state = state || initialValue; + function setState(newValue) { + state = newValue; + render(); + } + return [state, setState]; +} +\`\`\` + +这解释了为什么Hooks需要在组件的顶层调用 - 它们依赖于被调用的顺序来正确地将内部状态与每个Hook调用关联起来。 + +## Hooks在实际项目中的应用 + +### 状态管理 + +使用\`useReducer\`和\`useContext\`可以创建一个简单的全局状态管理解决方案: + +\`\`\`jsx +const initialState = { count: 0 }; +const reducer = (state, action) => { + switch (action.type) { + case 'increment': return { count: state.count + 1 }; + case 'decrement': return { count: state.count - 1 }; + default: throw new Error(); + } +}; + +const CountContext = React.createContext(); + +function CountProvider({ children }) { + const [state, dispatch] = useReducer(reducer, initialState); + return ( + + {children} + + ); +} + +function Counter() { + const { state, dispatch } = useContext(CountContext); + return ( + <> + Count: {state.count} + + + + ); +} +\`\`\` + +这个例子展示了如何使用Hooks创建一个简单但功能强大的状态管理系统。 + +## 性能优化 + +Hooks提供了几种方式来优化组件性能: + +1. \`useMemo\`: 缓存计算结果 +2. \`useCallback\`: 缓存函数 +3. \`React.memo\`: 优化函数组件的重渲染 + +\`\`\`jsx +import React, { useMemo, useCallback } from 'react'; + +function ExpensiveComponent({ data, onItemClick }) { + const sortedData = useMemo(() => { + return data.sort((a, b) => a.id - b.id); + }, [data]); + + const handleClick = useCallback((item) => { + console.log('Item clicked:', item); + onItemClick(item); + }, [onItemClick]); + + return ( +
    + {sortedData.map(item => ( +
  • handleClick(item)}> + {item.name} +
  • + ))} +
+ ); +} + +export default React.memo(ExpensiveComponent); +\`\`\` + +## Hooks的工作原理与源码解析 + +为了更深入地理解Hooks的工作原理,我们需要探讨React的内部实现。React使用一个链表结构来存储组件的Hooks状态。 + +### Hooks的内部结构 + +在React的源码中,每个函数组件实例都与一个\`Fiber\`节点相关联。这个\`Fiber\`节点包含一个\`memoizedState\`属性,用于存储该组件的Hooks状态。每个Hook在内部表示为一个对象,大致结构如下: + +\`\`\`javascript +{ + memoizedState: any, + baseState: any, + baseQueue: Update | null, + queue: UpdateQueue | null, + next: Hook | null, +} +\`\`\` + +这些Hook对象通过\`next\`指针形成一个链表。 + +### 为什么Hooks不能在循环中使用 + +Hooks不能在循环、条件或嵌套函数中使用的原因与React如何将Hook的调用与其内部状态关联有关。React依赖于Hooks被调用的顺序来正确地将每个Hook与其对应的状态关联起来。 + +让我们看一个简化的React内部实现示例: + +\`\`\`javascript +let firstWorkInProgressHook = null; +let workInProgressHook = null; + +function updateWorkInProgressHook() { + if (workInProgressHook === null) { + // 这是组件中的第一个Hook + workInProgressHook = firstWorkInProgressHook; + } else { + // 后续的Hooks + workInProgressHook = workInProgressHook.next; + } + return workInProgressHook; +} + +function useState(initialState) { + let hook = updateWorkInProgressHook(); + + if (hook === null) { + // 首次渲染时初始化Hook + hook = { + memoizedState: initialState, + next: null + }; + + if (firstWorkInProgressHook === null) { + firstWorkInProgressHook = hook; + } + } + + // 使用或更新Hook的状态 + const setState = (newState) => { + hook.memoizedState = newState; + // 触发重新渲染 + }; + + return [hook.memoizedState, setState]; +} +\`\`\` + +在这个简化的实现中,我们可以看到React如何依赖于Hooks的调用顺序。如果在循环或条件语句中使用Hooks,可能会导致Hook的调用顺序在不同的渲染之间发生变化,从而破坏React对Hook状态的正确追踪。 + +例如,考虑以下代码: + +\`\`\`jsx +function Counter(props) { + if (props.count % 2 === 0) { + const [evenCount, setEvenCount] = useState(0); + } + const [count, setCount] = useState(0); + // ... +} +\`\`\` + +在这个例子中,\`evenCount\`的Hook只在\`props.count\`为偶数时创建。这意味着\`count\`的Hook在不同渲染之间可能对应于不同的内部Hook对象,导致状态混乱。 + +### 自定义Hook的实现原理 + +自定义Hook本质上是将一系列Hook调用封装到一个函数中。它们不依赖于特殊的React内部机制,而是利用了JavaScript的闭包特性。 + +例如,一个\`useWindowSize\`自定义Hook的实现可能如下: + +\`\`\`jsx +function useWindowSize() { + const [size, setSize] = useState({ width: window.innerWidth, height: window.innerHeight }); + + useEffect(() => { + const handleResize = () => { + setSize({ width: window.innerWidth, height: window.innerHeight }); + }; + window.addEventListener('resize', handleResize); + return () => window.removeEventListener('resize', handleResize); + }, []); + + return size; +} +\`\`\` + +这个自定义Hook组合了\`useState\`和\`useEffect\`,创建了一个可重用的逻辑单元。 + +## Hooks与函数式编程 + +Hooks的设计理念与函数式编程密切相关。它们鼓励我们将逻辑分解为小的、可组合的单元,这与函数式编程的核心原则一致。 + +例如,\`useReducer\`Hook实际上是在React组件中实现了一个简化版的Redux模式。这种方法使得状态管理逻辑更加清晰和可测试。 + +## 结论 + +React Hooks是一个强大的特性,它简化了状态管理和副作用处理,使得函数组件更加灵活和强大。通过深入理解Hooks的工作原理和使用技巧,我们可以编写出更简洁、可维护和高性能的React应用。 + +然而,Hooks并不是银弹。在某些场景下,类组件可能仍然是更好的选择。关键是要根据具体情况选择最合适的工具。随着React的不断发展,我们可以期待看到更多围绕Hooks的创新和最佳实践的出现。 + +通过深入理解Hooks的内部工作原理,我们可以更好地把握它们的使用限制和最佳实践。Hooks不仅改变了我们编写React组件的方式,还推动了整个React生态系统向更函数式、更声明式的方向发展。 + +随着对Hooks的深入理解,开发者可以创建更加模块化、可重用和易于测试的组件。然而,重要的是要记住,Hooks并不是解决所有问题的万能工具。在某些情况下,类组件或其他模式可能更适合特定的需求。关键是要根据具体情况选择最合适的工具和方法。 + +`; diff --git a/src/data/ts/2.ts b/src/data/ts/2.ts new file mode 100644 index 0000000..f6c9a0a --- /dev/null +++ b/src/data/ts/2.ts @@ -0,0 +1,343 @@ +export const content2 = ` +# Next.js 13新特性解析 + +Next.js 13是一个重大更新,引入了许多激动人心的新特性,如App Router、服务器组件、流式渲染等。本文将详细介绍这些新特性的使用方法和优势,以及如何从Next.js 12迁移到13版本。 + +## App Router + +App Router是Next.js 13最显著的新特性之一,它彻底改变了路由管理的方式。 + +### 基于文件系统的路由 + +App Router延续了Next.js基于文件系统的路由概念,但引入了新的\`app\`目录结构: + +\`\`\`plaintext +app/ + layout.js + page.js + about/ + page.js + blog/ + [slug]/ + page.js +\`\`\` + +在这个结构中,\`page.js\`文件定义了路由的主要内容,而\`layout.js\`则定义了共享布局。 + +### 嵌套布局 + +App Router支持嵌套布局,这使得创建复杂的页面结构变得更加简单: + +\`\`\`jsx +// app/layout.js +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} + +// app/blog/layout.js +export default function BlogLayout({ children }) { + return ( +
+ + {children} +
+ ) +} +\`\`\` + +### 服务器组件 + +默认情况下,App Router中的所有组件都是服务器组件。这意味着它们在服务器上渲染,可以直接访问后端资源,并且不会增加客户端的JavaScript包大小。 + +\`\`\`jsx +// app/page.js +async function getData() { + const res = await fetch('https://api.example.com/data') + return res.json() +} + +export default async function Page() { + const data = await getData() + return
{data.map(item =>
{item.title}
)}
+} +\`\`\` + +### 客户端组件 + +当需要客户端交互时,可以使用客户端组件 + +\`\`\`jsx +'use client' + +import { useState } from 'react' + +export default function Counter() { + const [count, setCount] = useState(0) + return ( + + ) +} +\`\`\` + +## 服务器组件 + +服务器组件是Next.js 13的另一个重要特性,它允许我们在服务器上渲染React组件,从而提高性能和SEO。 + +### 优势 + +1. 减少客户端JavaScript包大小 +2. 直接访问后端资源 +3. 自动代码分割 +4. 改善首次加载性能 + +### 使用场景 + +服务器组件特别适合于: + +- 需要访问后端资源的组件 +- 不需要客户端交互的静态内容 +- SEO关键的页面内容 + +\`\`\`jsx +// app/products/page.js +async function getProducts() { + const res = await fetch('https://api.example.com/products') + return res.json() +} + +export default async function ProductsPage() { + const products = await getProducts() + return ( +
+

Products

+
    + {products.map(product => ( +
  • {product.name}
  • + ))} +
+
+ ) +} +\`\`\` + +## 流式渲染 + +流式渲染允许将页面内容分块传输到客户端,这可以显著改善大型应用的用户体验。 + +### 实现方式 + +Next.js 13通过\`loading.js\`文件和React的\`Suspense\`组件支持流式渲染: + +\`\`\`jsx +// app/dashboard/loading.js +export default function Loading() { + return
Loading...
+} + +// app/dashboard/page.js +import { Suspense } from 'react' +import UserProfile from './UserProfile' +import UserPosts from './UserPosts' + +export default function Dashboard() { + return ( +
+

Dashboard

+ Loading profile...
}> + + + Loading posts...}> + + + + ) +} +\`\`\` + +## 新的图片组件 + +Next.js 13引入了新的\`Image\`组件,它提供了更好的性能和用户体验: + +\`\`\`jsx +import Image from 'next/image' + +export default function Avatar() { + return ( + User Avatar + ) +} +\`\`\` + +新的\`Image\`组件支持: + +- 自动图片优化 +- 延迟加载 +- 响应式图片 +- 防止布局偏移 + +## 字体优化 + +Next.js 13引入了新的字体系统,它可以自动优化和加载自定义字体: + +\`\`\`jsx +import { Inter } from 'next/font/google' + +const inter = Inter({ subsets: ['latin'] }) + +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +\`\`\` + +这个系统可以: + +- 自动内联字体CSS +- 消除布局偏移 +- 预加载关键字体文件 + +## 从Next.js 12迁移到13 + +迁移到Next.js 13需要注意以下几点: + +1. 创建新的\`app\`目录,逐步迁移路由 +2. 更新\`next/image\`导入为\`next/legacy/image\` +3. 将客户端组件标记为\`'use client'\` +4. 更新API路由到新的\`app/api\`目录 +5. 使用新的数据获取方法替代\`getServerSideProps\`和\`getStaticProps\` + +## 结论 + +Next.js 13带来了许多令人兴奋的新特性,这些特性不仅提高了开发效率,还大大改善了应用性能和用户体验。App Router、服务器组件和流式渲染等新特性为我们提供了更灵活、更强大的工具,使得构建现代Web应用变得更加容易。 + +随着Next.js的不断发展,我们可以期待看到更多令人兴奋的新特性和改进。如果您还没有尝试过Next.js 13,现在是时候了! + +## Turbopack - 新的打包工具 + +Next.js 13引入了Turbopack,这是一个用Rust编写的增量打包工具,旨在替代Webpack。 + +### Turbopack的主要优势: + +1. 更快的启动时间 - 比Webpack快700倍 +2. 更快的更新 - 比Webpack快20倍 +3. 内存效率更高 - 使用的内存比Webpack少少5倍 + +### 如何启用Turbopack: + +在开发模式下,只需添加 --turbo 标志: + +\`\`\`bash +next dev --turbo +\`\`\` + +注意:Turbopack目前仍处于beta阶段,可能存在一些兼容性问题。 + +## 改进的国际化支持 + +Next.js 13改进了国际化(i18n)支持,使得创建多语言应用变得更加简单。 + +### 新的i18n路由 + +在 \`app\` 目录中,你可以使用以下结构来支持多语言: + +\`\`\` +app/ + [lang]/ + page.js + layout.js + about/ + page.js +\`\`\` + +### 使用示例: + +\`\`\`jsx +// app/[lang]/layout.js +export async function generateStaticParams() { + return [{ lang: 'en' }, { lang: 'de' }, { lang: 'fr' }] +} + +export default function Layout({ children, params }) { + return ( + + {children} + + ) +} +\`\`\` + +## 新的Middleware API + +Next.js 13引入了新的Middleware API,使得在请求处理过程中进行拦截和修改变得更加容易。 + +### Middleware的使用场景: + +- 认证和授权 +- A/B测试 +- 地理位置基础的内容定制 +- 边缘计算 + +### 示例: + +\`\`\`typescript +// middleware.ts +import { NextResponse } from 'next/server' +import type { NextRequest } from 'next/server' + +export function middleware(request: NextRequest) { + const country = request.geo?.country || 'US' + return NextResponse.rewrite(new URL(\`/\${country}\${request.nextUrl.pathname}\`, request.url)) +} + +export const config = { + matcher: '/:path*', +} +\`\`\` + +## 改进的TypeScript支持 + +Next.js 13进一步增强了对TypeScript的支持,提供了更好的类型推断和更严格的类型检查。 + +### 主要改进: + +1. 自动生成类型定义文件 +2. 改进的路由类型 +3. 更好的API路由类型支持 + +### 示例: + +\`\`\`typescript +// app/api/user/[id]/route.ts +import { NextResponse } from 'next/server' + +export async function GET( + request: Request, + { params }: { params: { id: string } } +) { + const id = params.id + // 获取用户数据 + return NextResponse.json({ id, name: 'John Doe' }) +} +\`\`\` + +## 结论 + +Next.js 13不仅带来了App Router、服务器组件和流式渲染等重大新特性,还在性能优化、开发体验和国际化支持等方面做出了显著改进。Turbopack的引入预示着未来更快的构建速度,而改进的Middleware API和TypeScript支持则为开发者提供了更强大、更灵活的工具。 + +随着Next.js生态系统的不断发展,我们可以期待看到更多创新和改进。无论你是正在构建新项目还是考虑升级现有应用,Next.js 13都值得你认真考虑和尝试。 +`; diff --git a/src/data/ts/index.ts b/src/data/ts/index.ts new file mode 100644 index 0000000..062386e --- /dev/null +++ b/src/data/ts/index.ts @@ -0,0 +1,2 @@ +export * from "./1"; +export * from "./2"; \ No newline at end of file From a428a5bae54f4bd418e48b0337bd6673504bc325 Mon Sep 17 00:00:00 2001 From: zhuba-Ahhh <3477826311@qq.com> Date: Tue, 27 Aug 2024 14:24:34 +0800 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20:sparkles:=20=E4=B8=8A=E4=B8=80?= =?UTF-8?q?=E7=AF=87/=E4=B8=8B=E4=B8=80=E7=AF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/blog/[id]/AnimatedBlogPost.tsx | 6 +++- src/app/blog/[id]/BlogFooter.tsx | 38 +++++++++++++++++++++++--- src/app/blog/[id]/page.tsx | 11 ++++++-- src/utils/blogHelpers.ts | 6 ++-- 4 files changed, 50 insertions(+), 11 deletions(-) diff --git a/src/app/blog/[id]/AnimatedBlogPost.tsx b/src/app/blog/[id]/AnimatedBlogPost.tsx index 6f49671..60ab0c5 100644 --- a/src/app/blog/[id]/AnimatedBlogPost.tsx +++ b/src/app/blog/[id]/AnimatedBlogPost.tsx @@ -17,10 +17,14 @@ interface AnimatedBlogPostProps { headings: Heading[]; readingTime: number; relatedPosts: BlogPost[]; + prevPost?: BlogPost; + nextPost?: BlogPost; } export default function AnimatedBlogPost({ post, + prevPost, + nextPost, headings, readingTime, relatedPosts, @@ -55,7 +59,7 @@ export default function AnimatedBlogPost({ contentFile={post?.contentFile} components={mdxComponents} /> - +
@@ -21,9 +27,33 @@ export default function BlogFooter({ post }: BlogFooterProps) { ))}
- +
+
+ {prevPost && ( + + )} +
+
+ +
+
+ {nextPost ? ( + + ) : ( +
+ )} +
+
); } diff --git a/src/app/blog/[id]/page.tsx b/src/app/blog/[id]/page.tsx index 7910303..5858edf 100644 --- a/src/app/blog/[id]/page.tsx +++ b/src/app/blog/[id]/page.tsx @@ -21,9 +21,8 @@ export async function generateStaticParams() { } export default async function BlogPost({ params }: BlogPostParams) { - const post = blogPosts.find((p) => p.id.toString() === params.id) as - | BlogPost - | undefined; + const postIndex = blogPosts.findIndex((p) => p.id.toString() === params.id); + const post = blogPosts[postIndex]; if (!post) { notFound(); @@ -38,6 +37,10 @@ export default async function BlogPost({ params }: BlogPostParams) { ) .slice(0, 3); + const prevPost = postIndex > 0 ? blogPosts[postIndex - 1] : void 0; + const nextPost = + postIndex < blogPosts.length - 1 ? blogPosts[postIndex + 1] : void 0; + return ( <> @@ -46,6 +49,8 @@ export default async function BlogPost({ params }: BlogPostParams) { headings={headings} readingTime={readingTime} relatedPosts={relatedPosts} + prevPost={prevPost} + nextPost={nextPost} /> ); diff --git a/src/utils/blogHelpers.ts b/src/utils/blogHelpers.ts index d566710..7b8ccdc 100644 --- a/src/utils/blogHelpers.ts +++ b/src/utils/blogHelpers.ts @@ -1,4 +1,4 @@ -import { useMemo } from 'react'; +import { useMemo } from "react"; export function extractHeadings(content: string) { const headingRegex = /^(#{1,3})\s+(.+)$/gm; @@ -9,7 +9,7 @@ export function extractHeadings(content: string) { headings.push({ level: match[1].length, text: match[2], - slug: match[2].toLowerCase().replace(/\s+/g, "-"), + slug: `${match[2].toLowerCase().replace(/\s+/g, "-")}`, }); } @@ -28,4 +28,4 @@ export function useExtractHeadings(content: string) { export function useEstimateReadingTime(content: string) { return useMemo(() => estimateReadingTime(content), [content]); -} \ No newline at end of file +} From e8292a79b3a2130eca3409447b64ec820cf8942e Mon Sep 17 00:00:00 2001 From: zhuba-Ahhh <3477826311@qq.com> Date: Tue, 27 Aug 2024 14:56:12 +0800 Subject: [PATCH 3/4] refactor: :recycle: icon --- src/app/about/page.tsx | 2 +- src/app/blog/[id]/BlogHeader.tsx | 69 ++++++++++++++++++++++++++++---- src/app/page.tsx | 9 +++-- 3 files changed, 68 insertions(+), 12 deletions(-) diff --git a/src/app/about/page.tsx b/src/app/about/page.tsx index 408460d..97fcdad 100644 --- a/src/app/about/page.tsx +++ b/src/app/about/page.tsx @@ -57,7 +57,7 @@ export default function About() { theme === "dark" ? "text-indigo-300" : "text-indigo-700" }`} > - Zhuba-Ahhh + {process.env.NEXT_PUBLIC_AUTHOR_NAME}

前端开发者 / 技术博主 diff --git a/src/app/blog/[id]/BlogHeader.tsx b/src/app/blog/[id]/BlogHeader.tsx index 6e95007..a696154 100644 --- a/src/app/blog/[id]/BlogHeader.tsx +++ b/src/app/blog/[id]/BlogHeader.tsx @@ -7,17 +7,27 @@ interface BlogHeaderProps { } export default function BlogHeader({ post, readingTime }: BlogHeaderProps) { + const { title, author, date, coverImage } = post; return ( <> -

{post.title}

-
- {post.date} | {post.author} | - 预计阅读时间: {readingTime} 分钟 +

{title}

+
+ + + {date} + + | + {author} + | + + + 预计阅读时间: {readingTime} 分钟 +
- {post.coverImage && ( + {coverImage && ( {post.title} ); } + +const CalendarIcon = ({ className }: { className: string }) => { + return ( + + + + ); +}; + +const ClockIcon = ({ className }: { className: string }) => { + return ( + + + + + ); +}; diff --git a/src/app/page.tsx b/src/app/page.tsx index f3082c3..3e5af62 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -35,7 +35,8 @@ export default function Home() { animate={{ opacity: 1 }} transition={{ delay: 0.1, duration: 0.3 }} > - 你好,我是[Zhuba-Ahhh]。作为一名前端开发者,我热衷于探索和分享Web开发的最新趋势、技巧和最佳实践。 + 你好,我是[{process.env.NEXT_PUBLIC_AUTHOR_NAME} + ]。作为一名前端开发者,我热衷于探索和分享Web开发的最新趋势、技巧和最佳实践。 {/* 探索博客按钮 */} @@ -66,12 +67,12 @@ export default function Home() { transition={{ delay: 0.3, duration: 0.3 }} >

- 联系我: 3477826311@qq.com |{" "} - + 联系我: {process.env.NEXT_PUBLIC_AUTHOR_EMAIL} |{" "} + GitHub {" "} |{" "} - + Twitter

From 0496e1382509a3de8c993aaacf409a8304fc0e61 Mon Sep 17 00:00:00 2001 From: zhuba-Ahhh <3477826311@qq.com> Date: Tue, 27 Aug 2024 15:00:56 +0800 Subject: [PATCH 4/4] chore: :hammer: metadata --- src/app/layout.tsx | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 8549cbc..c32bb9a 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -11,8 +11,20 @@ import { ThemeProvider } from "next-themes"; import AnimatedLayout from "@/components/app/AnimatedLayout"; export const metadata: Metadata = { - title: "我的博客", - description: "欢迎来到我的博客", + title: { + default: "我的博客", + template: "%s | 我的博客", + }, + description: "欢迎来到我的博客,这里分享前端开发的最新趋势和技巧", + keywords: ["Next.js", "React", "JavaScript", "前端开发"], + authors: [{ name: process.env.NEXT_PUBLIC_AUTHOR_NAME }], + creator: process.env.NEXT_PUBLIC_AUTHOR_NAME, + openGraph: { + type: "website", + locale: "zh_CN", + url: "https://yourblog.com", + // site_name: "我的博客" + }, }; export default function RootLayout({