feat(blog): replace hardcoded posts with dynamic loading from markdown files
Dynamically load blog posts from markdown files instead of using hardcoded data. Posts are now sorted by date and categories/tags are automatically extracted from frontmatter. This makes content management easier and more maintainable.
This commit is contained in:
@@ -3,57 +3,69 @@ import BlogLayout from '../../layouts/BlogLayout.astro';
|
|||||||
import BlogList from '../../components/BlogList.tsx';
|
import BlogList from '../../components/BlogList.tsx';
|
||||||
import { type Lang } from '@/i18n/utils';
|
import { type Lang } from '@/i18n/utils';
|
||||||
import { defaultLang } from '@/i18n/ui';
|
import { defaultLang } from '@/i18n/ui';
|
||||||
|
import { type BlogPost } from '@/types';
|
||||||
|
|
||||||
// 使用Astro.currentLocale获取当前语言环境
|
// 使用Astro.currentLocale获取当前语言环境
|
||||||
const lang = Astro.currentLocale as Lang || defaultLang;
|
const lang = Astro.currentLocale as Lang || defaultLang;
|
||||||
|
|
||||||
// Sample blog data - inline for now
|
// 使用Astro.glob读取所有博客文章
|
||||||
const blogPosts = [
|
const allPosts = await Astro.glob('./posts/*.md');
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
title: "Mastering React Hooks: A Deep Dive",
|
|
||||||
description: "Explore the power of React Hooks to manage state and side effects in functional components, with practical examples and best practices.",
|
|
||||||
image: "https://images.unsplash.com/photo-1633356122544-f134324a6cee?w=400&h=250&fit=crop&crop=center",
|
|
||||||
date: "May 10, 2025",
|
|
||||||
readTime: "5 min read",
|
|
||||||
tags: ["React", "JavaScript", "Frontend"],
|
|
||||||
slug: "mastering-react-hooks"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
title: "Scaling Node.js Apps with Docker",
|
|
||||||
description: "Learn how to containerize Node.js applications using Docker for seamless deployment and scalability in production environments.",
|
|
||||||
image: "https://images.unsplash.com/photo-1605745341112-85968b19335b?w=400&h=250&fit=crop&crop=center",
|
|
||||||
date: "April 25, 2025",
|
|
||||||
readTime: "7 min read",
|
|
||||||
tags: ["Node.js", "Docker", "DevOps"],
|
|
||||||
slug: "scaling-nodejs-docker"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
title: "Building Modern UIs with Tailwind CSS",
|
|
||||||
description: "Discover how to create beautiful, responsive user interfaces using Tailwind CSS utility classes and component patterns.",
|
|
||||||
image: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=400&h=250&fit=crop&crop=center",
|
|
||||||
date: "April 15, 2025",
|
|
||||||
readTime: "6 min read",
|
|
||||||
tags: ["CSS", "Tailwind", "UI/UX"],
|
|
||||||
slug: "modern-ui-tailwind"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
title: "TypeScript Best Practices for Large Projects",
|
|
||||||
description: "Essential TypeScript patterns and practices for maintaining code quality and developer productivity in enterprise applications.",
|
|
||||||
image: "https://images.unsplash.com/photo-1516321318423-f06f85e504b3?w=400&h=250&fit=crop&crop=center",
|
|
||||||
date: "March 30, 2025",
|
|
||||||
readTime: "8 min read",
|
|
||||||
tags: ["TypeScript", "JavaScript", "Architecture"],
|
|
||||||
slug: "typescript-best-practices"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
// Sample categories and tags
|
// 处理博客文章数据
|
||||||
const categories = ["React", "Node.js", "TailwindCSS", "TypeScript", "DevOps"];
|
const blogPosts: BlogPost[] = allPosts.map((post) => {
|
||||||
const tags = ["#React", "#JavaScript", "#Frontend", "#Node.js", "#Docker", "#DevOps", "#TailwindCSS", "#CSS", "#TypeScript"];
|
const slug = post.url?.split('/').filter(Boolean).pop() || '';
|
||||||
|
|
||||||
|
// 获取文章的默认图片,如果frontmatter中没有指定
|
||||||
|
const defaultImage = "https://images.unsplash.com/photo-1516321318423-f06f85e504b3?w=400&h=250&fit=crop&crop=center";
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: post.frontmatter.title,
|
||||||
|
description: post.frontmatter.description || '',
|
||||||
|
image: post.frontmatter.image || defaultImage,
|
||||||
|
slug: slug,
|
||||||
|
tags: post.frontmatter.tags || [],
|
||||||
|
date: post.frontmatter.date || post.frontmatter.pubDate || '',
|
||||||
|
readTime: post.frontmatter.readTime || post.frontmatter.readingTime || '5 min read',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 按日期排序
|
||||||
|
const sortedBlogPosts = blogPosts
|
||||||
|
.filter(post => post.date) // 过滤掉没有日期的文章
|
||||||
|
.sort((a, b) => {
|
||||||
|
const dateA = new Date(a.date).getTime();
|
||||||
|
const dateB = new Date(b.date).getTime();
|
||||||
|
return dateB - dateA; // 降序排列,最新的文章在前
|
||||||
|
});
|
||||||
|
|
||||||
|
// 从博客文章中提取分类和标签
|
||||||
|
const allCategories = new Set<string>();
|
||||||
|
const allTags = new Set<string>();
|
||||||
|
|
||||||
|
// 收集所有文章的分类和标签
|
||||||
|
allPosts.forEach(post => {
|
||||||
|
// 处理分类
|
||||||
|
if (post.frontmatter?.category) {
|
||||||
|
const categories = Array.isArray(post.frontmatter.category)
|
||||||
|
? post.frontmatter.category
|
||||||
|
: [post.frontmatter.category];
|
||||||
|
|
||||||
|
categories.forEach(category => {
|
||||||
|
if (category) allCategories.add(category);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理标签
|
||||||
|
if (post.frontmatter?.tags && Array.isArray(post.frontmatter.tags)) {
|
||||||
|
post.frontmatter.tags.forEach(tag => {
|
||||||
|
if (tag) allTags.add(tag);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 转换为数组并排序
|
||||||
|
const categories = Array.from(allCategories).sort();
|
||||||
|
const tags = Array.from(allTags).map(tag => `#${tag}`).sort();
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -115,7 +127,7 @@ const tags = ["#React", "#JavaScript", "#Frontend", "#Node.js", "#Docker", "#Dev
|
|||||||
|
|
||||||
<!-- Blog Posts -->
|
<!-- Blog Posts -->
|
||||||
<div class="lg:col-span-3">
|
<div class="lg:col-span-3">
|
||||||
<BlogList posts={blogPosts} lang="en" client:load />
|
<BlogList posts={sortedBlogPosts} lang="en" client:load />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,57 +3,69 @@ import BlogLayout from '../../../layouts/BlogLayout.astro';
|
|||||||
import BlogList from '../../../components/BlogList.tsx';
|
import BlogList from '../../../components/BlogList.tsx';
|
||||||
import { type Lang } from '@/i18n/utils';
|
import { type Lang } from '@/i18n/utils';
|
||||||
import { defaultLang } from '@/i18n/ui';
|
import { defaultLang } from '@/i18n/ui';
|
||||||
|
import { type BlogPost } from '@/types';
|
||||||
|
|
||||||
// 使用Astro.currentLocale获取当前语言环境
|
// 使用Astro.currentLocale获取当前语言环境
|
||||||
const lang = Astro.currentLocale as Lang || defaultLang;
|
const lang = Astro.currentLocale as Lang || defaultLang;
|
||||||
|
|
||||||
// 示例博客数据 - 暂时内联
|
// 使用Astro.glob读取所有中文博客文章
|
||||||
const blogPosts = [
|
const allPosts = await Astro.glob('./posts/*.md');
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
title: "精通 React Hooks:深入探索",
|
|
||||||
description: "探索 React Hooks 在函数组件中管理状态和副作用的强大功能,包含实用示例和最佳实践。",
|
|
||||||
image: "https://images.unsplash.com/photo-1633356122544-f134324a6cee?w=400&h=250&fit=crop&crop=center",
|
|
||||||
date: "2025年5月10日",
|
|
||||||
readTime: "5分钟阅读",
|
|
||||||
tags: ["React", "JavaScript", "前端"],
|
|
||||||
slug: "mastering-react-hooks"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
title: "使用 Docker 扩展 Node.js 应用",
|
|
||||||
description: "学习如何使用 Docker 容器化 Node.js 应用程序,实现生产环境中的无缝部署和可扩展性。",
|
|
||||||
image: "https://images.unsplash.com/photo-1605745341112-85968b19335b?w=400&h=250&fit=crop&crop=center",
|
|
||||||
date: "2025年4月25日",
|
|
||||||
readTime: "7分钟阅读",
|
|
||||||
tags: ["Node.js", "Docker", "DevOps"],
|
|
||||||
slug: "scaling-nodejs-docker"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
title: "使用 Tailwind CSS 构建现代 UI",
|
|
||||||
description: "探索如何使用 Tailwind CSS 实用类和组件模式创建美观、响应式的用户界面。",
|
|
||||||
image: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=400&h=250&fit=crop&crop=center",
|
|
||||||
date: "2025年4月15日",
|
|
||||||
readTime: "6分钟阅读",
|
|
||||||
tags: ["CSS", "Tailwind", "UI/UX"],
|
|
||||||
slug: "modern-ui-tailwind"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
title: "大型项目的 TypeScript 最佳实践",
|
|
||||||
description: "在企业级应用中维护代码质量和开发者生产力的必备 TypeScript 模式和实践。",
|
|
||||||
image: "https://images.unsplash.com/photo-1516321318423-f06f85e504b3?w=400&h=250&fit=crop&crop=center",
|
|
||||||
date: "2025年3月30日",
|
|
||||||
readTime: "8分钟阅读",
|
|
||||||
tags: ["TypeScript", "JavaScript", "架构"],
|
|
||||||
slug: "typescript-best-practices"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
// 示例分类和标签
|
// 处理博客文章数据
|
||||||
const categories = ["React", "Node.js", "TailwindCSS", "TypeScript", "DevOps"];
|
const blogPosts: BlogPost[] = allPosts.map((post) => {
|
||||||
const tags = ["#React", "#JavaScript", "#前端", "#Node.js", "#Docker", "#DevOps", "#TailwindCSS", "#CSS", "#TypeScript"];
|
const slug = post.url?.split('/').filter(Boolean).pop() || '';
|
||||||
|
|
||||||
|
// 获取文章的默认图片,如果frontmatter中没有指定
|
||||||
|
const defaultImage = "https://images.unsplash.com/photo-1516321318423-f06f85e504b3?w=400&h=250&fit=crop&crop=center";
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: post.frontmatter.title,
|
||||||
|
description: post.frontmatter.description || '',
|
||||||
|
image: post.frontmatter.image || defaultImage,
|
||||||
|
slug: slug,
|
||||||
|
tags: post.frontmatter.tags || [],
|
||||||
|
date: post.frontmatter.date || post.frontmatter.pubDate || '',
|
||||||
|
readTime: post.frontmatter.readTime || post.frontmatter.readingTime || '5分钟阅读',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 按日期排序
|
||||||
|
const sortedBlogPosts = blogPosts
|
||||||
|
.filter(post => post.date) // 过滤掉没有日期的文章
|
||||||
|
.sort((a, b) => {
|
||||||
|
const dateA = new Date(a.date).getTime();
|
||||||
|
const dateB = new Date(b.date).getTime();
|
||||||
|
return dateB - dateA; // 降序排列,最新的文章在前
|
||||||
|
});
|
||||||
|
|
||||||
|
// 从博客文章中提取分类和标签
|
||||||
|
const allCategories = new Set<string>();
|
||||||
|
const allTags = new Set<string>();
|
||||||
|
|
||||||
|
// 收集所有文章的分类和标签
|
||||||
|
allPosts.forEach(post => {
|
||||||
|
// 处理分类
|
||||||
|
if (post.frontmatter?.category) {
|
||||||
|
const categories = Array.isArray(post.frontmatter.category)
|
||||||
|
? post.frontmatter.category
|
||||||
|
: [post.frontmatter.category];
|
||||||
|
|
||||||
|
categories.forEach(category => {
|
||||||
|
if (category) allCategories.add(category);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理标签
|
||||||
|
if (post.frontmatter?.tags && Array.isArray(post.frontmatter.tags)) {
|
||||||
|
post.frontmatter.tags.forEach(tag => {
|
||||||
|
if (tag) allTags.add(tag);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 转换为数组并排序
|
||||||
|
const categories = Array.from(allCategories).sort();
|
||||||
|
const tags = Array.from(allTags).map(tag => `#${tag}`).sort();
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -115,7 +127,7 @@ const tags = ["#React", "#JavaScript", "#前端", "#Node.js", "#Docker", "#DevOp
|
|||||||
|
|
||||||
<!-- 博客文章 -->
|
<!-- 博客文章 -->
|
||||||
<div class="lg:col-span-3">
|
<div class="lg:col-span-3">
|
||||||
<BlogList posts={blogPosts} lang="zh" client:load />
|
<BlogList posts={sortedBlogPosts} lang="zh" client:load />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user