GitHub avatar

Fox's Blog

✨ AI Generated Article

How Does This Blog Work?

A deep dive into this blog's internals: React, Vite, Markdown, the

How Does This Blog Work?

Ever wondered how this blog works under the hood? In this article, I'll walk you through the entire architecture of the application, from the tech stack all the way to the process of writing an article. And yes, I'll even show you how I write my articles from VS Code!

The Tech Stack

This blog is built with modern web technologies:

  • React 19 -- for the user interface
  • TypeScript -- for typed and more reliable code
  • Vite -- as an ultra-fast build tool
  • React Router v7 -- for navigation between pages
  • react-markdown -- to transform Markdown into HTML
  • rehype-raw + rehype-sanitize -- to allow raw HTML in Markdown while staying secure

Project Structure

Here's what the project tree looks like:

├── .github/
│   └── workflows/
│       └── deploy.yml        ← CI/CD pipeline
├── public/
│   ├── home.md               ← Home page content
│   ├── portfolio.md           ← Portfolio content
│   └── articles/
│       ├── index.json         ← List of all articles
│       ├── hello-world.md     ← An article
│       ├── how-this-blog-works.md  ← This article!
│       └── /articles/assets/            ← Article images
├── src/
│   ├── main.tsx               ← React entry point
│   ├── App.tsx                ← Main router
│   ├── components/
│   │   ├── Header.tsx         ← Navigation bar
│   │   └── Footer.tsx         ← Footer
│   └── pages/
│       ├── Home.tsx           ← Home page
│       ├── BlogList.tsx       ← Article list
│       ├── Article.tsx        ← Article reader
│       ├── Portfolio.tsx      ← Portfolio page
│       └── NotFound.tsx       ← 404 page
└── vite.config.ts             ← Vite configuration

The core idea is simple: content is separated from code. Pages are written in Markdown in the public/ folder, and the React code in src/ takes care of rendering them.

The Routing System

The App.tsx file defines all application routes using React Router:

Route Page Description
/ Home Home page, loadshome.md
/blog BlogList List of all articles
/blog/:slug Article A single article, loadsarticles/{slug}.md
/portfolio Portfolio Portfolio page, loadsportfolio.md
* NotFound 404 page for unknown URLs

Each page has a well-defined role: it fetches a Markdown file, transforms it into HTML with react-markdown, and displays it on screen.

How Does an Article Work?

This is the most interesting part! Here's the lifecycle of an article:

1. The index.json File

All articles are referenced in public/articles/index.json. Each entry contains the article's metadata:

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug -- the unique identifier, used in the URL (/blog/hello-world)
  • title -- the title displayed in the list
  • description -- a short summary
  • date -- the publication date

2. The Markdown File

The article content is a simple .md file in public/articles/. The filename matches the slug defined in index.json.

You can put anything in there: headings, lists, images, tables, and even raw HTML thanks to rehype-raw!

3. React-Side Rendering

When you visit /blog/hello-world, here's what happens:

  1. React Router captures the slug parameter from the URL
  2. The Article.tsx component fetches /articles/hello-world.md
  3. The Markdown is transformed into HTML by react-markdown
  4. Links to /articles/assets/ are automatically rewritten to /articles//articles/assets/
  5. In parallel, metadata is loaded from index.json to display the date and description

It's as simple as that!

The Home Page and Portfolio

The Home and Portfolio pages work in exactly the same way: they load a Markdown file (home.md or portfolio.md) and render it as HTML.

The special thing is that they use a custom sanitization schema that allows class and style attributes on all HTML elements. This lets me write styled HTML directly in Markdown, like image galleries for example.

The Header is pinned to the top of the page with position: fixed. It contains:

  • My GitHub avatar (loaded directly from github.com/fox3000foxy.png)
  • The blog title
  • Navigation links: Home, Blog, Portfolio

The Footer is minimalist: just a copyright with the current year calculated dynamically.

The Dark Theme

The site is always in dark mode -- no light/dark toggle. This is a deliberate choice: color-scheme: dark is set in the global styles, with a black background #000 and white text #fff. Links are blue (#64b5f6) and turn green on hover (#81c784).

How I Write an Article

Now for the practical part! Here's my workflow for writing a new article:

Step 1: Create the Markdown File

I open VS Code and create a new .md file in public/articles/:

Step 2: Write the Content

I write the article content directly in Markdown. VS Code offers an excellent built-in Markdown preview:

For images, I place them in public/articles//articles/assets/ and reference them using standard Markdown syntax:

![description](/articles/assets/my-image.png)

The Article.tsx component automatically rewrites the /articles/assets/ path to /articles//articles/assets/ so that images display correctly.

Step 3: Register the Article in index.json

Once the article is done, I add it to public/articles/index.json so it shows up in the blog list:

Step 4: Test Locally

I start the Vite dev server:

pnpm dev

Vite starts in milliseconds and I can see my article in real time at localhost:5173:

Step 5: Publish

A simple git push is all it takes! The CI/CD pipeline handles the rest automatically.

The CI/CD Deployment Pipeline

I've set up a full GitHub Actions pipeline that automates linting, building, and deploying the site every time I push to main. Let's break it down.

The workflow lives in .github/workflows/deploy.yml and is split into two jobs: build and deploy.

Triggers

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

The pipeline runs on every push to main and on every pull request targeting main. This means PRs get checked (lint + build) before merging, but only pushes to main actually trigger a deployment.

Job 1: Build

The build job runs on ubuntu-latest and goes through these steps:

  1. Checkout -- Clones the repository with full history (fetch-depth: 0)
  2. Setup pnpm -- Installs the latest version of pnpm using pnpm/action-setup@v4
  3. Setup Node.js 20 -- Configures Node with pnpm caching enabled for faster installs
  4. Install dependencies -- Runs pnpm install --frozen-lockfile to ensure reproducible builds (no lockfile changes allowed)
  5. Lint -- Runs pnpm run lint (ESLint) to catch code quality issues before building
  6. Build -- Runs pnpm run build, which first checks TypeScript types (tsc -b) then bundles everything with Vite
  7. Upload artifact -- Uploads the dist/ folder as a build artifact for the deploy job

If any step fails -- a lint error, a type error, a build error -- the whole pipeline stops and nothing gets deployed. This keeps the live site safe from broken code.

Job 2: Deploy

The deploy job only runs if:

  • The build job succeeded (needs: build)
  • The event is a push (not a PR)
  • The branch is main
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

It then:

  1. Downloads the build artifact -- Grabs the dist/ folder produced by the build job
  2. Configures GitHub Pages -- Sets up the Pages environment
  3. Uploads to Pages -- Packages the dist/ folder for GitHub Pages
  4. Deploys -- Publishes the site using actions/deploy-pages@v4

The Full Picture

Here's what happens from writing to deployment:

Write article in VS Code
        ↓
   git add & commit
        ↓
      git push
        ↓
  GitHub Actions triggers
        ↓
  ┌─────────────────┐
  │   BUILD JOB     │
  │  1. Checkout    │
  │  2. Setup pnpm  │
  │  3. Setup Node  │
  │  4. Install     │
  │  5. Lint ✓      │
  │  6. Build ✓     │
  │  7. Upload dist │
  └────────┬────────┘
           ↓
  ┌─────────────────┐
  │  DEPLOY JOB     │
  │  1. Download    │
  │  2. Configure   │
  │  3. Upload      │
  │  4. Deploy 🚀   │
  └─────────────────┘
           ↓
    Live on GitHub Pages!

The entire process takes about a minute from push to live. No manual deploy, no FTP, no SSH -- just git push and it's done.

The Production Build

Under the hood, the pnpm build command runs:

  1. tsc -b -- Checks TypeScript types
  2. vite build -- Bundles and optimizes all the code

Vite produces minified and optimized files with automatic code-splitting. The result is a blazing-fast static site.

Why This Architecture?

I could have used a CMS, a static site generator like Hugo or Jekyll, or even Next.js. But here's why I chose this approach:

  • Simplicity -- Write in Markdown, push to GitHub, it's live
  • Full control -- No dependency on a CMS or database
  • Performance -- Vite + React = fast loading
  • Flexibility -- I can mix Markdown and HTML however I want
  • Learning -- It's a great project to master React and TypeScript
  • CI/CD -- Automated quality checks and deployment with GitHub Actions

Conclusion

This blog is a simple but well-thought-out project: Markdown for content, React for rendering, Vite for performance, GitHub Actions for CI/CD, and GitHub Pages for hosting. No database, no backend server, just static files served efficiently with an automated pipeline ensuring quality at every push.

Thanks for reading, and see you in the next article! 🦊

✨ AI Generated Article

Comment fonctionne ce blog ?

Les coulisses du blog : React, Vite, Markdown, la pipeline CI/CD

Comment Fonctionne Ce Blog ?

Tu t'es déjà demandé comment ce blog fonctionne sous le capot ? Dans cet article, je vais te détailler toute l'architecture de l'application, de la stack technique jusqu'au processus de rédaction d'un article. Et oui, je vais même te montrer comment j'écris mes articles depuis VS Code !

La Stack Technique

Ce blog est construit avec des technologies web modernes :

  • React 19 -- pour l'interface utilisateur
  • TypeScript -- pour un code typé et plus fiable
  • Vite -- comme outil de build ultra-rapide
  • React Router v7 -- pour la navigation entre les pages
  • react-markdown -- pour transformer le Markdown en HTML
  • rehype-raw + rehype-sanitize -- pour autoriser du HTML brut dans le Markdown en toute sécurité

Structure du Projet

Voici à quoi ressemble l'arborescence du projet :

├── .github/
│   └── workflows/
│       └── deploy.yml              ← Pipeline CI/CD
├── public/
│   ├── home.md                     ← Contenu de la page d'accueil
│   ├── portfolio.md                ← Contenu du portfolio
│   └── articles/
│       ├── index.json              ← Liste de tous les articles
│       ├── hello-world.md          ← Un article
│       ├── how-this-blog-works.md  ← Cet article !
│       └── /articles/assets/                 ← Images des articles
├── src/
│   ├── main.tsx                    ← Point d'entrée React
│   ├── App.tsx                     ← Routeur principal
│   ├── components/
│   │   ├── Header.tsx              ← Barre de navigation
│   │   └── Footer.tsx              ← Pied de page
│   └── pages/
│       ├── Home.tsx                ← Page d'accueil
│       ├── BlogList.tsx            ← Liste des articles
│       ├── Article.tsx             ← Lecteur d'article
│       ├── Portfolio.tsx           ← Page portfolio
│       └── NotFound.tsx            ← Page 404
└── vite.config.ts                  ← Configuration Vite

L'idée centrale est simple : le contenu est séparé du code. Les pages sont écrites en Markdown dans le dossier public/, et le code React dans src/ s'occupe de les afficher.

Le Système de Routage

Le fichier App.tsx définit toutes les routes de l'application avec React Router :

Route Page Description
/ Home Page d'accueil, charge home.md
/blog BlogList Liste de tous les articles
/blog/:slug Article Un article, charge articles/{slug}.md
/portfolio Portfolio Page portfolio, charge portfolio.md
* NotFound Page 404 pour les URLs inconnues

Chaque page a un rôle bien défini : elle récupère un fichier Markdown, le transforme en HTML avec react-markdown, et l'affiche à l'écran.

Comment Fonctionne un Article ?

C'est la partie la plus intéressante ! Voici le cycle de vie d'un article :

1. Le Fichier index.json

Tous les articles sont référencés dans public/articles/index.json. Chaque entrée contient les métadonnées de l'article :

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug -- l'identifiant unique, utilisé dans l'URL (/blog/hello-world)
  • title -- le titre affiché dans la liste
  • description -- un court résumé
  • date -- la date de publication

2. Le Fichier Markdown

Le contenu de l'article est un simple fichier .md dans public/articles/. Le nom du fichier correspond au slug défini dans index.json.

Tu peux y mettre ce que tu veux : titres, listes, images, tableaux, et même du HTML brut grâce à rehype-raw !

3. Le Rendu Côté React

Quand tu visites /blog/hello-world, voici ce qui se passe :

  1. React Router récupère le paramètre slug depuis l'URL
  2. Le composant Article.tsx charge /articles/hello-world.md
  3. Le Markdown est transformé en HTML par react-markdown
  4. Les liens vers /articles/assets/ sont automatiquement réécrits vers /articles//articles/assets/
  5. En parallèle, les métadonnées sont chargées depuis index.json pour afficher la date et la description

C'est aussi simple que ça !

La Page d'Accueil et le Portfolio

Les pages Accueil et Portfolio fonctionnent exactement de la même manière : elles chargent un fichier Markdown (home.md ou portfolio.md) et le rendent en HTML.

La particularité, c'est qu'elles utilisent un schéma de sanitization personnalisé qui autorise les attributs class et style sur tous les éléments HTML. Ça me permet d'écrire du HTML stylisé directement dans le Markdown, comme des galeries d'images par exemple.

Le Header est épinglé en haut de la page avec position: fixed. Il contient :

  • Mon avatar GitHub (chargé directement depuis github.com/fox3000foxy.png)
  • Le titre du blog
  • Les liens de navigation : Accueil, Blog, Portfolio

Le Footer est minimaliste : juste un copyright avec l'année courante calculée dynamiquement.

Le Thème Sombre

Le site est toujours en mode sombre -- pas de bascule jour/nuit. C'est un choix délibéré : color-scheme: dark est défini dans les styles globaux, avec un fond noir #000 et du texte blanc #fff. Les liens sont bleus (#64b5f6) et deviennent verts au survol (#81c784).

Comment J'Écris un Article

Passons à la pratique ! Voici mon workflow pour écrire un nouvel article :

Étape 1 : Créer le Fichier Markdown

J'ouvre VS Code et je crée un nouveau fichier .md dans public/articles/ :

Étape 2 : Écrire le Contenu

J'écris le contenu de l'article directement en Markdown. VS Code a un excellent aperçu Markdown intégré :

Pour les images, je les place dans public/articles//articles/assets/ et je les référence avec la syntaxe Markdown standard :

![description](/articles/assets/my-image.png)

Le composant Article.tsx réécrit automatiquement le chemin /articles/assets/ vers /articles//articles/assets/ pour que les images s'affichent correctement.

Étape 3 : Enregistrer l'Article dans index.json

Une fois l'article terminé, je l'ajoute dans public/articles/index.json pour qu'il apparaisse dans la liste du blog :

Étape 4 : Tester en Local

Je lance le serveur de développement Vite :

pnpm dev

Vite démarre en quelques millisecondes et je peux voir mon article en temps réel sur localhost:5173 :

Étape 5 : Publier

Un simple git push suffit ! Le pipeline CI/CD s'occupe du reste automatiquement.

Le Pipeline de Déploiement CI/CD

J'ai mis en place un pipeline GitHub Actions complet qui automatise le lint, le build et le déploiement du site à chaque push sur main. Voyons ça en détail.

Le workflow se trouve dans .github/workflows/deploy.yml et est divisé en deux jobs : build et deploy.

Déclencheurs

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

Le pipeline s'exécute à chaque push sur main et à chaque pull request visant main. Les PRs sont donc vérifiées (lint + build) avant d'être mergées, mais seuls les pushes sur main déclenchent un déploiement.

Job 1 : Build

Le job de build tourne sur ubuntu-latest et suit ces étapes :

  1. Checkout -- Clone le dépôt avec tout l'historique (fetch-depth: 0)
  2. Setup pnpm -- Installe la dernière version de pnpm avec pnpm/action-setup@v4
  3. Setup Node.js 20 -- Configure Node avec le cache pnpm activé pour des installations plus rapides
  4. Install dependencies -- Exécute pnpm install --frozen-lockfile pour garantir des builds reproductibles (pas de modification du lockfile autorisée)
  5. Lint -- Exécute pnpm run lint (ESLint) pour vérifier la qualité du code avant le build
  6. Build -- Exécute pnpm run build, qui vérifie d'abord les types TypeScript (tsc -b) puis bundle le tout avec Vite
  7. Upload artifact -- Téléverse le dossier dist/ comme artefact de build pour le job de déploiement

Si une étape échoue -- une erreur de lint, de type ou de build -- tout le pipeline s'arrête et rien n'est déployé. Ça protège le site en production du code cassé.

Job 2 : Deploy

Le job de déploiement ne s'exécute que si :

  • Le job de build a réussi (needs: build)
  • L'événement est un push (pas une PR)
  • La branche est main
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

Il procède ensuite :

  1. Télécharge l'artefact de build -- Récupère le dossier dist/ produit par le job de build
  2. Configure GitHub Pages -- Met en place l'environnement Pages
  3. Téléverse vers Pages -- Prépare le dossier dist/ pour GitHub Pages
  4. Déploie -- Publie le site avec actions/deploy-pages@v4

Le Tableau Complet

Voici ce qui se passe de l'écriture au déploiement :

Écrire l'article dans VS Code
         ↓
   git add & commit
         ↓
      git push
         ↓
  GitHub Actions se déclenche
         ↓
  ┌─────────────────┐
  │   BUILD JOB     │
  │  1. Checkout    │
  │  2. Setup pnpm  │
  │  3. Setup Node  │
  │  4. Install     │
  │  5. Lint ✓      │
  │  6. Build ✓     │
  │  7. Upload dist │
  └────────┬────────┘
           ↓
  ┌─────────────────┐
  │  DEPLOY JOB     │
  │  1. Download    │
  │  2. Configure   │
  │  3. Upload      │
  │  4. Deploy 🚀   │
  └─────────────────┘
           ↓
    En ligne sur GitHub Pages !

Le processus entier prend environ une minute entre le push et la mise en ligne. Pas de déploiement manuel, pas de FTP, pas de SSH -- juste git push et c'est fait.

Le Build de Production

Sous le capot, la commande pnpm build exécute :

  1. tsc -b -- Vérifie les types TypeScript
  2. vite build -- Bundle et optimise tout le code

Vite produit des fichiers minifiés et optimisés avec du code-splitting automatique. Le résultat est un site statique ultra-rapide.

Pourquoi Cette Architecture ?

J'aurais pu utiliser un CMS, un générateur de site statique comme Hugo ou Jekyll, ou même Next.js. Mais voici pourquoi j'ai choisi cette approche :

  • Simplicité -- Écris en Markdown, push sur GitHub, c'est en ligne
  • Contrôle total -- Pas de dépendance à un CMS ou une base de données
  • Performance -- Vite + React = chargement rapide
  • Flexibilité -- Je peux mélanger Markdown et HTML comme je veux
  • Apprentissage -- C'est un super projet pour maîtriser React et TypeScript
  • CI/CD -- Vérifications de qualité et déploiement automatisés avec GitHub Actions

Conclusion

Ce blog est un projet simple mais bien pensé : Markdown pour le contenu, React pour le rendu, Vite pour la performance, GitHub Actions pour le CI/CD, et GitHub Pages pour l'hébergement. Pas de base de données, pas de serveur backend, juste des fichiers statiques servis efficacement avec un pipeline automatisé qui garantit la qualité à chaque push.

Merci d'avoir lu, et à bientôt dans le prochain article ! 🦊

✨ AI Generated Article

这个博客是如何运作的?

深入解析这个博客的内部架构:React、Vite、Markdown、CI/CD 流水线和文章写作流程。

这个博客是如何运作的?

想知道这个博客在底层是如何运作的吗?在这篇文章中,我会带你了解整个应用的架构,从技术栈到写文章的全流程。没错,我甚至还会展示我是如何从 VS Code 里写文章的!

技术栈

这个博客是用现代 Web 技术构建的:

  • React 19 -- 用户界面
  • TypeScript -- 类型安全、更可靠的代码
  • Vite -- 超快的构建工具
  • React Router v7 -- 页面导航
  • react-markdown -- 将 Markdown 转换为 HTML
  • rehype-raw + rehype-sanitize -- 在 Markdown 中安全使用原始 HTML

项目结构

这是项目的目录树:

├── .github/
│   └── workflows/
│       └── deploy.yml        ← CI/CD 流水线
├── public/
│   ├── home.md               ← 首页内容
│   ├── portfolio.md           ← 作品集内容
│   └── articles/
│       ├── index.json         ← 所有文章的列表
│       ├── hello-world.md     ← 一篇文章
│       ├── how-this-blog-works.md  ← 就是这篇文章!
│       └── /articles/assets/            ← 文章配图
├── src/
│   ├── main.tsx               ← React 入口
│   ├── App.tsx                ← 主路由器
│   ├── components/
│   │   ├── Header.tsx         ← 导航栏
│   │   └── Footer.tsx         ← 页脚
│   └── pages/
│       ├── Home.tsx           ← 首页
│       ├── BlogList.tsx       ← 文章列表
│       ├── Article.tsx        ← 文章阅读器
│       ├── Portfolio.tsx      ← 作品集页面
│       └── NotFound.tsx       ← 404 页面
└── vite.config.ts             ← Vite 配置

核心思想很简单:内容与代码分离。页面以 Markdown 格式写在 public/ 文件夹中,src/ 中的 React 代码负责渲染它们。

路由系统

App.tsx 使用 React Router 定义了所有应用路由:

路由 页面 说明
/ Home 首页,加载 home.md
/blog BlogList 所有文章的列表
/blog/:slug Article 单篇文章,加载 articles/{slug}.md
/portfolio Portfolio 作品集页面,加载 portfolio.md
* NotFound 未知 URL 的 404 页面

每个页面都有明确的职责:获取 Markdown 文件,用 react-markdown 转换为 HTML,然后显示在屏幕上。

文章是如何运作的?

这是最有趣的部分!以下是文章的生命周期:

1. index.json 文件

所有文章都在 public/articles/index.json 中引用。每条记录包含文章的元数据:

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug -- 唯一标识符,用于 URL(/blog/hello-world)
  • title -- 列表中显示的标题
  • description -- 简短摘要
  • date -- 发布日期

2. Markdown 文件

文章内容是一个简单的 .md 文件,放在 public/articles/ 中。文件名与 index.json 中定义的 slug 一致。

你可以放任何内容进去:标题、列表、图片、表格,甚至借助 rehype-raw 还可以写原始 HTML!

3. React 端渲染

当你访问 /blog/hello-world 时,会发生以下事情:

  1. React Router 从 URL 中提取 slug 参数
  2. Article.tsx 组件获取 /articles/hello-world.md
  3. react-markdown 将 Markdown 转换为 HTML
  4. 对 /articles/assets/ 的链接会自动重写为 /articles//articles/assets/
  5. 同时从 index.json 加载元数据以显示日期和描述

就是这么简单!

首页和作品集

首页和作品集页面的工作方式完全一样:加载一个 Markdown 文件(home.md 或 portfolio.md)并渲染为 HTML。

特别之处在于它们使用了一个自定义的清理模式,允许所有 HTML 元素上使用 class 和 style 属性。这样我就可以在 Markdown 中直接编写带样式的 HTML,比如图片画廊。

头部和页脚

头部固定在页面顶部(position: fixed)。它包含:

  • 我的 GitHub 头像(直接从 github.com/fox3000foxy.png 加载)
  • 博客标题
  • 导航链接:首页、博客、作品集

页脚极简:只有版权信息,年份动态计算。

深色主题

网站始终处于深色模式----没有亮/暗切换。这是刻意的选择:全局样式中设置了 color-scheme: dark,黑色背景 #000,白色文字 #fff。链接为蓝色(#64b5f6),悬停时变为绿色(#81c784)。

我是如何写文章的

现在来说实际操作部分!以下是我写新文章的工作流:

第一步:创建 Markdown 文件

我打开 VS Code,在 public/articles/ 中创建一个新的 .md 文件:

第二步:写内容

我直接用 Markdown 写文章内容。VS Code 提供了出色的内置 Markdown 预览:

对于图片,我把它们放在 public/articles//articles/assets/ 中,然后用标准 Markdown 语法引用:

![description](/articles/assets/my-image.png)

Article.tsx 组件会自动将 /articles/assets/ 路径重写为 /articles//articles/assets/,确保图片正确显示。

第三步:在 index.json 中注册文章

文章写完后,我把它添加到 public/articles/index.json 中,这样它就会出现在博客列表中:

第四步:本地测试

我启动 Vite 开发服务器:

pnpm dev

Vite 在毫秒内启动,我可以在 localhost:5173 实时查看文章:

第五步:发布

只需一个 git push 就搞定了!CI/CD 流水线会自动处理后续工作。

CI/CD 部署流水线

我设置了一个完整的 GitHub Actions 流水线,每次推送到 main 时自动运行代码检查、构建和部署。我们来分解一下。

工作流位于 .github/workflows/deploy.yml,分为两个任务:构建和部署。

触发条件

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

流水线在每次推送到 main 时以及每个针对 main 的拉动请求时运行。这意味着 PR 在合并前会经过检查(代码检查+构建),但只有推送到 main 才会触发部署。

任务 1:构建

构建任务在 ubuntu-latest 上运行,包含以下步骤:

  1. 检出代码 -- 克隆仓库,包含完整历史(fetch-depth: 0)
  2. 设置 pnpm -- 使用 pnpm/action-setup@v4 安装最新版 pnpm
  3. 设置 Node.js 20 -- 配置 Node,启用 pnpm 缓存以加快安装速度
  4. 安装依赖 -- 运行 pnpm install --frozen-lockfile 确保可重现构建(不允许更改锁文件)
  5. 代码检查 -- 运行 pnpm run lint(ESLint)在构建前检查代码质量
  6. 构建 -- 运行 pnpm run build,先检查 TypeScript 类型(tsc -b),然后用 Vite 打包
  7. 上传产物 -- 将 dist/ 文件夹作为构建产物上传,供部署任务使用

如果任何一步失败----代码检查错误、类型错误、构建错误----整个流水线停止,不会部署任何内容。这样可以防止问题代码上线。

任务 2:部署

部署任务仅在以下条件满足时运行:

  • 构建任务成功(needs: build)
  • 事件是推送(不是 PR)
  • 分支是 main
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

然后它:

  1. 下载构建产物 -- 获取构建任务生成的 dist/ 文件夹
  2. 配置 GitHub Pages -- 设置 Pages 环境
  3. 上传到 Pages -- 打包 dist/ 文件夹供 GitHub Pages 使用
  4. 部署 -- 使用 actions/deploy-pages@v4 发布网站

全景图

以下是从写作到部署的完整流程:

在 VS Code 中写文章
        ↓
   git add & commit
        ↓
      git push
        ↓
  GitHub Actions 触发
        ↓
  ┌─────────────────┐
  │   构建任务       │
  │  1. 检出代码    │
  │  2. 设置 pnpm   │
  │  3. 设置 Node   │
  │  4. 安装依赖    │
  │  5. 代码检查 ✓  │
  │  6. 构建 ✓      │
  │  7. 上传 dist   │
  └────────┬────────┘
           ↓
  ┌─────────────────┐
  │  部署任务       │
  │  1. 下载产物    │
  │  2. 配置        │
  │  3. 上传        │
  │  4. 部署 🚀     │
  └─────────────────┘
           ↓
    在 GitHub Pages 上线!

从推送到上线,整个过程大约需要一分钟。无需手动部署、无需 FTP、无需 SSH----只需 git push 就完成了。

生产构建

在底层,pnpm build 命令运行:

  1. tsc -b -- 检查 TypeScript 类型
  2. vite build -- 打包和优化所有代码

Vite 生成经过压缩和优化的文件,并自动进行代码分割。最终成果是一个极快的静态网站。

为什么选择这种架构?

我本可以使用 CMS、像 Hugo 或 Jekyll 这样的静态站点生成器,甚至 Next.js。但我选择这个方案的原因如下:

  • 简单 -- 用 Markdown 写,推送到 GitHub,自动上线
  • 完全掌控 -- 不依赖 CMS 或数据库
  • 性能 -- Vite + React = 加载飞快
  • 灵活 -- 我可以随意混用 Markdown 和 HTML
  • 学习 -- 这是一个掌握 React 和 TypeScript 的好项目
  • CI/CD -- 通过 GitHub Actions 实现自动化质量检查和部署

总结

这个博客虽然简单,但经过精心设计:Markdown 负责内容,React 负责渲染,Vite 负责性能,GitHub Actions 负责 CI/CD,GitHub Pages 负责托管。没有数据库,没有后端服务器,只有高效提供的静态文件,以及每次推送时保证质量的自动化流水线。

感谢阅读,下篇文章见!🦊

✨ AI Generated Article

このブログの仕組み

このブログの内部構造を深掘り:React、Vite、Markdown、CI/CDパイプライン、記事作成ワークフロー。

このブログの仕組み

このブログが内部でどう動いてるか気になったことはない?この記事では、技術スタックから記事の執筆プロセスまで、アプリケーションのアーキテクチャ全体を解説する。そう、VS Codeからどうやって記事を書いてるかまで見せるよ!

技術スタック

このブログはモダンなWeb技術で作られている:

  • React 19 -- ユーザーインターフェース
  • TypeScript -- 型付きでより信頼性の高いコード
  • Vite -- 超高速ビルドツール
  • React Router v7 -- ページ間のナビゲーション
  • react-markdown -- MarkdownをHTMLに変換
  • rehype-raw + rehype-sanitize -- Markdown内で生のHTMLを安全に許可

プロジェクト構成

プロジェクトツリーはこんな感じ:

├── .github/
│   └── workflows/
│       └── deploy.yml        ← CI/CDパイプライン
├── public/
│   ├── home.md               ← ホームページコンテンツ
│   ├── portfolio.md           ← ポートフォリオコンテンツ
│   └── articles/
│       ├── index.json         ← 全記事の一覧
│       ├── hello-world.md     ← サンプル記事
│       ├── how-this-blog-works.md  ← この記事!
│       └── /articles/assets/            ← 記事の画像
├── src/
│   ├── main.tsx               ← Reactのエントリーポイント
│   ├── App.tsx                ← メインルーター
│   ├── components/
│   │   ├── Header.tsx         ← ナビゲーションバー
│   │   └── Footer.tsx         ← フッター
│   └── pages/
│       ├── Home.tsx           ← ホームページ
│       ├── BlogList.tsx       ← 記事一覧
│       ├── Article.tsx        ← 記事リーダー
│       ├── Portfolio.tsx      ← ポートフォリオページ
│       └── NotFound.tsx       ← 404ページ
└── vite.config.ts             ← Vite設定

コアとなる考え方はシンプルだ:コンテンツはコードから分離されている。ページはpublic/フォルダにMarkdownで書かれ、src/のReactコードがそれらをレンダリングする。

ルーティングシステム

App.tsxがReact Routerを使って全アプリケーションルートを定義している:

ルート ページ 説明
/ Home ホームページ。home.mdを読み込む
/blog BlogList 全記事の一覧
/blog/:slug Article 個別記事。articles/{slug}.mdを読み込む
/portfolio Portfolio ポートフォリオページ。portfolio.mdを読み込む
* NotFound 不明なURL用の404ページ

各ページは明確に定義された役割を持つ:Markdownファイルを取得し、react-markdownでHTMLに変換し、画面に表示する。

記事の仕組み

これが一番面白い部分だ!記事のライフサイクルはこんな感じ:

1. index.jsonファイル

すべての記事はpublic/articles/index.jsonで参照される。各エントリには記事のメタデータが含まれる:

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug -- 一意の識別子。URLで使われる(/blog/hello-world)
  • title -- 一覧に表示されるタイトル
  • description -- 短い要約
  • date -- 公開日

2. Markdownファイル

記事のコンテンツはpublic/articles/にある単純な.mdファイル。ファイル名はindex.jsonで定義されたslugと一致する。

見出し、リスト、画像、テーブル、そしてrehype-rawのおかげで生のHTMLさえも入れられる!

3. React側のレンダリング

/blog/hello-worldにアクセスすると、こんなことが起きる:

  1. React RouterがURLからslugパラメータを取得
  2. Article.tsxコンポーネントが/articles/hello-world.mdをフェッチ
  3. Markdownがreact-markdownによってHTMLに変換される
  4. /articles/assets/へのリンクは自動的に/articles//articles/assets/に書き換えられる
  5. 並行して、index.jsonからメタデータが読み込まれ、日付と説明が表示される

これだけのシンプルさだ!

ホームページとポートフォリオ

ホームページとポートフォリオもまったく同じように動作する:Markdownファイル(home.mdまたはportfolio.md)を読み込み、HTMLとしてレンダリングする。

特別なのは、すべてのHTML要素でclassとstyle属性を許可するカスタムサニタイゼーションスキーマを使っていることだ。これにより、Markdown内でスタイル付きHTMLを直接書ける。例えば画像ギャラリーなどだ。

ヘッダーとフッター

ヘッダーはposition: fixedでページの上部に固定されている。中身は:

  • 俺のGitHubアバター(github.com/fox3000foxy.pngから直接読み込み)
  • ブログのタイトル
  • ナビゲーションリンク:Home、Blog、Portfolio

フッターはミニマリスト:現在の年を動的に計算した著作権表示だけ。

ダークテーマ

サイトは常にダークモードだ----ライト/ダーク切り替えはない。これは意図的な選択:グローバルスタイルでcolor-scheme: darkが設定されていて、背景は黒#000、テキストは白#fff。リンクは青(#64b5f6)で、ホバー時に緑(#81c784)に変わる。

記事の書き方

実践的な部分だ!新しい記事を書くときのワークフロー:

ステップ1:Markdownファイルを作成

VS Codeを開いて、public/articles/に新しい.mdファイルを作成する:

ステップ2:コンテンツを書く

記事の内容をMarkdownで直接書く。VS Codeには優れたMarkdownプレビュー機能が組み込まれている:

画像はpublic/articles//articles/assets/に配置し、標準的なMarkdown記法で参照する:

![description](/articles/assets/my-image.png)

Article.tsxコンポーネントが自動的に/articles/assets/パスを/articles//articles/assets/に書き換え、画像が正しく表示されるようにする。

ステップ3:index.jsonに記事を登録

記事ができたら、public/articles/index.jsonに追加してブログ一覧に表示させる:

ステップ4:ローカルでテスト

Vite開発サーバーを起動:

pnpm dev

Viteはミリ秒で起動し、localhost:5173で記事がリアルタイムで見れる:

ステップ5:公開

git pushするだけ!CI/CDパイプラインが残りを自動で処理する。

CI/CDデプロイパイプライン

GitHub Actionsのフルパイプラインを設定していて、mainにプッシュするたびにリンター、ビルド、デプロイを自動化している。

ワークフローは.github/workflows/deploy.ymlにあり、buildとdeployの2つのジョブに分かれている。

トリガー

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

パイプラインはmainへのプッシュと、mainをターゲットにするプルリクエストのたびに実行される。つまり、PRはマージ前にチェック(lint + build)されるが、デプロイが実際にトリガーされるのはmainへのプッシュのみだ。

ジョブ1:Build

ビルドジョブはubuntu-latestで実行され、以下のステップを経る:

  1. Checkout -- 完全な履歴でリポジトリをクローン(fetch-depth: 0)
  2. Setup pnpm -- pnpm/action-setup@v4を使って最新版のpnpmをインストール
  3. Setup Node.js 20 -- pnpmキャッシュを有効にしてNodeを設定、高速インストール
  4. Install dependencies -- pnpm install --frozen-lockfileで再現可能なビルドを確保(ロックファイルの変更不可)
  5. Lint -- pnpm run lint(ESLint)でコード品質をチェック
  6. Build -- pnpm run buildを実行。まずTypeScriptの型をチェック(tsc -b)、それからViteでバンドル
  7. Upload artifact -- dist/フォルダをビルド成果物としてアップロード

いずれかのステップが失敗すると----lintエラー、型エラー、ビルドエラー----パイプライン全体が停止し、何もデプロイされない。これで本番サイトが壊れたコードから守られる。

ジョブ2:Deploy

デプロイジョブは以下の場合のみ実行される:

  • ビルドジョブが成功した(needs: build)
  • イベントがプッシュである(PRではない)
  • ブランチがmainである
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

そして:

  1. ビルド成果物をダウンロード -- ビルドジョブが生成したdist/フォルダを取得
  2. GitHub Pagesを設定 -- Pages環境をセットアップ
  3. Pagesにアップロード -- dist/フォルダをGitHub Pages用にパッケージ
  4. デプロイ -- actions/deploy-pages@v4でサイトを公開

全体像

執筆からデプロイまでの流れ:

VS Codeで記事を書く
        ↓
   git add & commit
        ↓
      git push
        ↓
  GitHub Actionsが起動
        ↓
  ┌─────────────────┐
  │   BUILD JOB     │
  │  1. Checkout    │
  │  2. Setup pnpm  │
  │  3. Setup Node  │
  │  4. Install     │
  │  5. Lint ✓      │
  │  6. Build ✓     │
  │  7. Upload dist │
  └────────┬────────┘
           ↓
  ┌─────────────────┐
  │  DEPLOY JOB     │
  │  1. Download    │
  │  2. Configure   │
  │  3. Upload      │
  │  4. Deploy 🚀   │
  └─────────────────┘
           ↓
    GitHub Pagesで公開!

プッシュから公開まで約1分。手動デプロイ不要、FTP不要、SSH不要----git pushだけで完了だ。

プロダクションビルド

内部では、pnpm buildコマンドが以下を実行する:

  1. tsc -b -- TypeScriptの型をチェック
  2. vite build -- 全コードをバンドルして最適化

Viteは自動コード分割で最小化・最適化されたファイルを生成する。結果は爆速の静的サイトだ。

なぜこのアーキテクチャ?

CMSやHugo、Jekyllのような静的サイトジェネレーター、Next.jsを使うこともできた。でもなぜこのアプローチを選んだか:

  • シンプルさ -- Markdownで書いてGitHubにプッシュするだけで公開
  • 完全な制御 -- CMSやデータベースに依存しない
  • パフォーマンス -- Vite + React = 高速読み込み
  • 柔軟性 -- MarkdownとHTMLを好きなように混ぜられる
  • 学習 -- ReactとTypeScriptを極めるのに最適なプロジェクト
  • CI/CD -- GitHub Actionsによる自動品質チェックとデプロイ

結論

このブログはシンプルだがよく考え抜かれたプロジェクトだ:コンテンツにMarkdown、レンダリングにReact、パフォーマンスにVite、CI/CDにGitHub Actions、ホスティングにGitHub Pages。データベースもバックエンドサーバーもなく、自動化パイプラインがあらゆるプッシュで品質を確保しながら、効率的に提供される静的ファイルだけだ。

読んでくれてありがとう、次の記事で会おう!🦊

✨ AI Generated Article

이 블로그는 어떻게 동작하나요?

이 블로그의 내부 동작에 대한 심층 분석: React, Vite, Markdown, CI/CD 파이프라인, 글 작성 워크플로우

이 블로그는 어떻게 동작하나요?

이 블로그가 내부적으로 어떻게 돌아가는지 궁금했어? 이 글에서는 앱의 전체 아키텍처를 기술 스택부터 글을 쓰는 과정까지 모두 설명할게. 그리고 맞아, VS Code에서 어떻게 글을 쓰는지도 보여줄 거야!

기술 스택

이 블로그는 최신 웹 기술로 만들어졌어:

  • React 19 -- 사용자 인터페이스용
  • TypeScript -- 타입이 있는 더 안정적인 코드
  • Vite -- 초고속 빌드 도구
  • React Router v7 -- 페이지 간 네비게이션
  • react-markdown -- Markdown을 HTML로 변환
  • rehype-raw + rehype-sanitize -- 보안을 유지하면서 Markdown에서 원시 HTML 허용

프로젝트 구조

프로젝트 트리는 이렇게 생겼어:

├── .github/
│   └── workflows/
│       └── deploy.yml        ← CI/CD 파이프라인
├── public/
│   ├── home.md               ← 홈 페이지 콘텐츠
│   ├── portfolio.md          ← 포트폴리오 콘텐츠
│   └── articles/
│       ├── index.json        ← 모든 글 목록
│       ├── hello-world.md    ← 글 예시
│       ├── how-this-blog-works.md  ← 이 글!
│       └── /articles/assets/           ← 글 이미지들
├── src/
│   ├── main.tsx              ← React 진입점
│   ├── App.tsx               ← 메인 라우터
│   ├── components/
│   │   ├── Header.tsx        ← 네비게이션 바
│   │   └── Footer.tsx        ← 푸터
│   └── pages/
│       ├── Home.tsx          ← 홈 페이지
│       ├── BlogList.tsx      ← 글 목록
│       ├── Article.tsx       ← 글 읽기 페이지
│       ├── Portfolio.tsx     ← 포트폴리오 페이지
│       └── NotFound.tsx      ← 404 페이지
└── vite.config.ts            ← Vite 설정

핵심 아이디어는 간단해: 콘텐츠는 코드와 분리된다. 페이지는 public/ 폴더에 Markdown으로 작성되고, src/의 React 코드가 렌더링을 담당해.

라우팅 시스템

App.tsx 파일이 React Router를 사용해 모든 애플리케이션 경로를 정의해:

경로 페이지 설명
/ Home 홈 페이지, home.md 로드
/blog BlogList 모든 글 목록
/blog/:slug Article 개별 글, articles/{slug}.md 로드
/portfolio Portfolio 포트폴리오 페이지, portfolio.md 로드
* NotFound 알 수 없는 URL용 404 페이지

각 페이지는 명확한 역할이 있어: Markdown 파일을 가져와서 react-markdown으로 HTML로 변환하고 화면에 표시해.

글은 어떻게 동작하나요?

이게 제일 재미있는 부분이야! 글의 생애주기는 이렇게 돼:

1. index.json 파일

모든 글은 public/articles/index.json에 등록돼. 각 항목에는 글의 메타데이터가 들어있어:

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug -- 고유 식별자, URL에 사용됨 (/blog/hello-world)
  • title -- 목록에 표시되는 제목
  • description -- 짧은 요약
  • date -- 발행일

2. Markdown 파일

글 콘텐츠는 public/articles/ 안에 있는 간단한 .md 파일이야. 파일명은 index.json에 정의된 slug와 일치해.

rehype-raw 덕분에 제목, 목록, 이미지, 테이블, 심지어 원시 HTML까지 무엇이든 넣을 수 있어!

3. React 측 렌더링

/blog/hello-world에 방문하면 이런 일이 일어나:

  1. React Router가 URL에서 slug 파라미터를 가져와
  2. Article.tsx 컴포넌트가 /articles/hello-world.md를 가져와
  3. react-markdown이 Markdown을 HTML로 변환해
  4. /articles/assets/ 링크가 자동으로 /articles//articles/assets/로 재작성돼
  5. 동시에 index.json에서 메타데이터를 로드해 날짜와 설명을 표시해

이렇게 간단해!

홈 페이지와 포트폴리오

홈과 포트폴리오 페이지도 똑같은 방식으로 동작해: Markdown 파일(home.md 또는 portfolio.md)을 로드해서 HTML로 렌더링해.

특별한 점은 모든 HTML 요소에 class와 style 속성을 허용하는 커스텀 새니티제이션 스키마를 사용한다는 거야. 이렇게 하면 이미지 갤러리 같은 스타일된 HTML을 Markdown에서 직접 작성할 수 있어.

헤더와 푸터

헤더는 position: fixed로 페이지 상단에 고정되어 있어. 내용은:

  • 내 GitHub 아바타 (github.com/fox3000foxy.png에서 직접 로드)
  • 블로그 제목
  • 네비게이션 링크: Home, Blog, Portfolio

푸터는 미니멀해: 현재 연도가 동적으로 계산된 저작권 표시만 있어.

다크 테마

사이트는 항상 다크 모드야 -- 라이트/다크 토글이 없어. 의도적인 선택이지: 전역 스타일에 color-scheme: dark가 설정되어 있고, 검은 배경 #000에 흰색 텍스트 #fff를 사용해. 링크는 파란색(#64b5f6)이고 호버 시 초록색(#81c784)으로 변해.

내가 글을 쓰는 방법

이제 실용적인 부분이야! 새 글을 쓰는 내 워크플로우:

1단계: Markdown 파일 생성

VS Code를 열고 public/articles/에 새 .md 파일을 만들어:

2단계: 내용 작성

글 내용을 Markdown으로 직접 작성해. VS Code는 훌륭한 내장 Markdown 미리보기를 제공해:

이미지는 public/articles//articles/assets/에 넣고 표준 Markdown 문법으로 참조해:

![description](/articles/assets/my-image.png)

Article.tsx 컴포넌트가 /articles/assets/ 경로를 자동으로 /articles//articles/assets/로 재작성해서 이미지가 올바르게 표시돼.

3단계: index.json에 글 등록

글이 완성되면 public/articles/index.json에 추가해서 블로그 목록에 나타나게 해:

4단계: 로컬에서 테스트

Vite 개발 서버를 시작해:

pnpm dev

Vite는 몇 밀리초 만에 시작하고 localhost:5173에서 글을 실시간으로 볼 수 있어:

5단계: 배포

git push 하나면 끝이야! CI/CD 파이프라인이 나머지를 자동으로 처리해.

CI/CD 배포 파이프라인

main에 푸시할 때마다 린팅, 빌드, 배포를 자동화하는 완전한 GitHub Actions 파이프라인을 설정했어. 자세히 살펴보자.

워크플로우는 .github/workflows/deploy.yml에 있고 build와 deploy 두 개의 작업으로 나뉘어.

트리거

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

파이프라인은 main에 push할 때마다와 main을 대상으로 하는 pull request마다 실행돼. PR은 병합 전에 검사(린트 + 빌드)를 받지만, main에 푸시할 때만 실제 배포가 이루어져.

작업 1: Build

Build 작업은 ubuntu-latest에서 실행되며 다음 단계를 거쳐:

  1. Checkout -- 전체 히스토리로 저장소 클론 (fetch-depth: 0)
  2. Setup pnpm -- pnpm/action-setup@v4로 최신 pnpm 설치
  3. Setup Node.js 20 -- 더 빠른 설치를 위해 pnpm 캐싱 활성화하여 Node 설정
  4. Install dependencies -- pnpm install --frozen-lockfile 실행 (락파일 변경 불가)
  5. Lint -- pnpm run lint (ESLint)로 코드 품질 확인
  6. Build -- pnpm run build 실행, 먼저 TypeScript 타입 검사(tsc -b) 후 Vite로 번들링
  7. Upload artifact -- dist/ 폴더를 빌드 아티팩트로 업로드

어느 단계든 실패하면 -- 린트 에러, 타입 에러, 빌드 에러 -- 전체 파이프라인이 중단되고 아무것도 배포되지 않아. 이렇게 라이브 사이트가 깨진 코드로부터 안전해.

작업 2: Deploy

Deploy 작업은 다음 조건에서만 실행돼:

  • Build 작업이 성공했을 때 (needs: build)
  • 이벤트가 push일 때 (PR이 아닐 때)
  • 브랜치가 main일 때
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

그러면:

  1. 빌드 아티팩트 다운로드 -- Build 작업이 만든 dist/ 폴더를 가져와
  2. GitHub Pages 설정 -- Pages 환경 구성
  3. Pages에 업로드 -- GitHub Pages용으로 dist/ 폴더 패키징
  4. 배포 -- actions/deploy-pages@v4로 사이트 게시

전체 그림

글을 쓰고 배포까지의 과정:

VS Code에서 글 작성
        ↓
   git add & commit
        ↓
      git push
        ↓
  GitHub Actions 트리거
        ↓
  ┌─────────────────┐
  │   BUILD 작업     │
  │  1. Checkout    │
  │  2. Setup pnpm  │
  │  3. Setup Node  │
  │  4. Install     │
  │  5. Lint ✓      │
  │  6. Build ✓     │
  │  7. Upload dist │
  └────────┬────────┘
           ↓
  ┌─────────────────┐
  │  DEPLOY 작업     │
  │  1. Download    │
  │  2. Configure   │
  │  3. Upload      │
  │  4. Deploy 🚀   │
  └─────────────────┘
           ↓
    GitHub Pages에 라이브!

푸시부터 라이브까지 전체 과정은 약 1분 정도 걸려. 수동 배포, FTP, SSH 없음 -- 그냥 git push면 끝이야.

프로덕션 빌드

내부적으로 pnpm build 명령은 다음을 실행해:

  1. tsc -b -- TypeScript 타입 검사
  2. vite build -- 모든 코드 번들링 및 최적화

Vite는 자동 코드 분할로 축소 및 최적화된 파일을 생성해. 결과는 엄청 빠른 정적 사이트야.

왜 이 아키텍처인가?

CMS, Hugo나 Jekyll 같은 정적 사이트 생성기, 또는 Next.js를 사용할 수도 있었어. 하지만 이 방식을 선택한 이유는:

  • 단순함 -- Markdown으로 작성하고 GitHub에 푸시하면 바로 라이브
  • 완전한 제어 -- CMS나 데이터베이스에 의존하지 않음
  • 성능 -- Vite + React = 빠른 로딩
  • 유연성 -- Markdown과 HTML을 원하는 대로 섞어 쓸 수 있음
  • 학습 -- React와 TypeScript를 마스터하기 좋은 프로젝트
  • CI/CD -- GitHub Actions로 자동화된 품질 검사 및 배포

결론

이 블로그는 단순하지만 잘 생각된 프로젝트야: 콘텐츠는 Markdown, 렌더링은 React, 성능은 Vite, CI/CD는 GitHub Actions, 호스팅은 GitHub Pages. 데이터베이스도, 백엔드 서버도 없이, 자동화된 파이프라인이 모든 푸시에서 품질을 보장하면서 효율적으로 제공되는 정적 파일일 뿐이야.

읽어줘서 고마워, 다음 글에서 보자! 🦊

✨ AI Generated Article

Bu Blog Nasıl Çalışıyor?

Bu blogun iç işleyişine derin bir dalış: React, Vite, Markdown,

Bu Blog Nasıl Çalışıyor?

Hiç bu blogun perde arkasında nasıl çalıştığını merak ettin mi? Bu yazıda, uygulamanın tüm mimarisini, teknoloji yığınından makale yazma sürecine kadar adım adım anlatacağım. Ve evet, makalelerimi VS Code'dan nasıl yazdığımı bile göstereceğim!

Teknoloji Yığını

Bu blog, modern web teknolojileriyle inşa edildi:

  • React 19 -- kullanıcı arayüzü için
  • TypeScript -- tipli ve daha güvenilir kod için
  • Vite -- ultra hızlı bir derleme aracı olarak
  • React Router v7 -- sayfalar arası navigasyon için
  • react-markdown -- Markdown'ı HTML'ye dönüştürmek için
  • rehype-raw + rehype-sanitize -- Markdown'da ham HTML'e izin verirken güvenli kalmak için

Proje Yapısı

Proje ağacı şöyle görünüyor:

├── .github/
│   └── workflows/
│       └── deploy.yml         ← CI/CD pipeline'ı
├── public/
│   ├── home.md                ← Ana sayfa içeriği
│   ├── portfolio.md           ← Portfolyo içeriği
│   └── articles/
│       ├── index.json         ← Tüm makalelerin listesi
│       ├── hello-world.md     ← Bir makale
│       ├── how-this-blog-works.md  ← Bu makale!
│       └── /articles/assets/            ← Makale görselleri
├── src/
│   ├── main.tsx               ← React giriş noktası
│   ├── App.tsx                ← Ana yönlendirici
│   ├── components/
│   │   ├── Header.tsx         ← Navigasyon çubuğu
│   │   └── Footer.tsx         ← Alt bilgi
│   └── pages/
│       ├── Home.tsx           ← Ana sayfa
│       ├── BlogList.tsx       ← Makale listesi
│       ├── Article.tsx        ← Makale okuyucu
│       ├── Portfolio.tsx      ← Portfolyo sayfası
│       └── NotFound.tsx       ← 404 sayfası
└── vite.config.ts             ← Vite yapılandırması

Temel fikir basit: içerik koddan ayrılmıştır. Sayfalar public/ klasöründe Markdown olarak yazılır ve src/ içindeki React kodu onları görüntülemekle ilgilenir.

Yönlendirme Sistemi

App.tsx dosyası, React Router kullanarak tüm uygulama rotalarını tanımlar:

Rota Sayfa Açıklama
/ Home Ana sayfa, home.md dosyasını yükler
/blog BlogList Tüm makalelerin listesi
/blog/:slug Article Tek bir makale, articles/{slug}.md dosyasını yükler
/portfolio Portfolio Portfolyo sayfası, portfolio.md dosyasını yükler
* NotFound Bilinmeyen URL'ler için 404 sayfası

Her sayfanın iyi tanımlanmış bir rolü vardır: bir Markdown dosyasını getirir, react-markdown ile HTML'ye dönüştürür ve ekranda gösterir.

Bir Makale Nasıl Çalışır?

Bu en ilginç kısım! İşte bir makalenin yaşam döngüsü:

1. index.json Dosyası

Tüm makaleler public/articles/index.json dosyasında referanslanır. Her girdi, makalenin metaverilerini içerir:

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug -- benzersiz tanımlayıcı, URL'de kullanılır (/blog/hello-world)
  • title -- listede görüntülenen başlık
  • description -- kısa bir özet
  • date -- yayınlanma tarihi

2. Markdown Dosyası

Makale içeriği, public/articles/ içinde basit bir .md dosyasıdır. Dosya adı, index.json içinde tanımlanan slug ile eşleşir.

İçine her şeyi koyabilirsin: başlıklar, listeler, görseller, tablolar ve hatta rehype-raw sayesinde ham HTML!

3. React Tarafında İşleme

/blog/hello-world sayfasını ziyaret ettiğinde şunlar olur:

  1. React Router, URL'den slug parametresini alır
  2. Article.tsx bileşeni /articles/hello-world.md dosyasını getirir
  3. Markdown, react-markdown tarafından HTML'ye dönüştürülür
  4. /articles/assets/ bağlantıları otomatik olarak /articles//articles/assets/ olarak yeniden yazılır
  5. Paralel olarak, metaveriler index.json'dan yüklenerek tarih ve açıklama görüntülenir

Bu kadar basit!

Ana Sayfa ve Portfolyo

Ana sayfa ve Portfolyo sayfaları tam olarak aynı şekilde çalışır: bir Markdown dosyası (home.md veya portfolio.md) yükler ve HTML olarak işler.

Özel olan şey, tüm HTML öğelerinde class ve style niteliklerine izin veren özel bir temizleme şeması kullanmalarıdır. Bu, Markdown içinde doğrudan stillendirilmiş HTML yazmama olanak tanır, örneğin görsel galerileri gibi.

Başlık ve Alt Bilgi

Başlık, sayfanın üstüne position: fixed ile sabitlenmiştir. Şunları içerir:

  • GitHub avatarım (doğrudan github.com/fox3000foxy.png adresinden yüklenir)
  • Blog başlığı
  • Gezinme bağlantıları: Ana Sayfa, Blog, Portfolyo

Alt bilgi minimalisttir: sadece dinamik olarak hesaplanan güncel yılı içeren bir telif hakkı.

Karanlık Tema

Site her zaman karanlık moddadır -- açık/karanlık geçişi yoktur. Bu bilinçli bir seçimdir: global stillerde siyah arka plan #000 ve beyaz metin #fff ile color-scheme: dark ayarlanmıştır. Bağlantılar mavidir (#64b5f6) ve üzerine gelindiğinde yeşile döner (#81c784).

Bir Makaleyi Nasıl Yazıyorum

Şimdi pratik kısma geçelim! İşte yeni bir makale yazma iş akışım:

Adım 1: Markdown Dosyasını Oluştur

VS Code'u açarım ve public/articles/ içinde yeni bir .md dosyası oluştururum:

Adım 2: İçeriği Yaz

Makale içeriğini doğrudan Markdown olarak yazarım. VS Code mükemmel bir yerleşik Markdown önizlemesi sunar:

Görseller için, onları public/articles//articles/assets/ klasörüne koyarım ve standart Markdown sözdizimiyle referans veririm:

![açıklama](/articles/assets/my-image.png)

Article.tsx bileşeni, /articles/assets/ yolunu otomatik olarak /articles//articles/assets/ olarak yeniden yazar, böylece görseller doğru görüntülenir.

Adım 3: Makaleyi index.json'a Kaydet

Makale bittiğinde, blog listesinde görünmesi için public/articles/index.json dosyasına eklerim:

Adım 4: Yerelde Test Et

Vite geliştirme sunucusunu başlatırım:

pnpm dev

Vite milisaniyeler içinde başlar ve makalemi localhost:5173 adresinde gerçek zamanlı olarak görebilirim:

Adım 5: Yayınla

Sadece git push yapmak yeterli! CI/CD pipeline'ı gerisini otomatik olarak halleder.

CI/CD Dağıtım Pipeline'ı

main branch'ine her push yaptığımda lint, build ve deploy işlemlerini otomatikleştiren tam bir GitHub Actions pipeline'ı kurdum. Gelin adım adım inceleyelim.

Workflow, .github/workflows/deploy.yml dosyasında yaşar ve iki job'a ayrılmıştır: build ve deploy.

Tetikleyiciler

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

Pipeline, main branch'ine her pushta ve main'i hedefleyen her pull requestte çalışır. Bu sayede PR'lar birleştirilmeden önce kontrol edilir (lint + build), ancak sadece main'e yapılan push'lar gerçekten dağıtımı tetikler.

Job 1: Build

Build job'ı ubuntu-latest üzerinde çalışır ve şu adımlardan geçer:

  1. Checkout -- Repoyu tam geçmişiyle klonlar (fetch-depth: 0)
  2. Setup pnpm -- pnpm/action-setup@v4 kullanarak en son pnpm sürümünü kurar
  3. Setup Node.js 20 -- Node'u pnpm önbelleği etkinken yapılandırır (daha hızlı kurulum için)
  4. Install dependencies -- Tekrarlanabilir derlemeler için pnpm install --frozen-lockfile çalıştırır (lockfile değişikliklerine izin verilmez)
  5. Lint -- Derlemeden önce kod kalitesi sorunlarını yakalamak için pnpm run lint (ESLint) çalıştırır
  6. Build -- Önce TypeScript türlerini kontrol eden (tsc -b) ardından her şeyi Vite ile paketleyen pnpm run build çalıştırır
  7. Upload artifact -- dist/ klasörünü deploy job'ı için bir build yapıtı olarak yükler

Herhangi bir adım başarısız olursa -- bir lint hatası, bir tür hatası, bir derleme hatası -- tüm pipeline durur ve hiçbir şey dağıtılmaz. Bu, canlı siteyi bozuk koddan korur.

Job 2: Deploy

Deploy job'ı sadece şu durumlarda çalışır:

  • Build job'ı başarılı olduysa (needs: build)
  • Olay bir push ise (PR değil)
  • Branch main ise
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

Ardından:

  1. Build yapıtını indirir -- Build job'ının ürettiği dist/ klasörünü alır
  2. GitHub Pages'i yapılandırır -- Pages ortamını kurar
  3. Pages'e yükler -- dist/ klasörünü GitHub Pages için paketler
  4. Dağıtır -- actions/deploy-pages@v4 kullanarak siteyi yayınlar

Tam Resim

İşte yazmadan dağıtıma kadar olan süreç:

VS Code'da makaleyi yaz
        ↓
   git add & commit
        ↓
      git push
        ↓
  GitHub Actions tetiklenir
        ↓
  ┌─────────────────┐
  │   BUILD JOB'ı   │
  │  1. Checkout    │
  │  2. Setup pnpm  │
  │  3. Setup Node  │
  │  4. Install     │
  │  5. Lint ✓      │
  │  6. Build ✓     │
  │  7. Upload dist │
  └────────┬────────┘
           ↓
  ┌─────────────────┐
  │  DEPLOY JOB'ı   │
  │  1. Download    │
  │  2. Configure   │
  │  3. Upload      │
  │  4. Deploy 🚀   │
  └─────────────────┘
           ↓
    GitHub Pages'de canlı!

Tüm süreç, push'tan canlı yayına kadar yaklaşık bir dakika sürer. Manuel dağıtım yok, FTP yok, SSH yok -- sadece git push ve iş tamam.

Production Derlemesi

Perde arkasında, pnpm build komutu şunları çalıştırır:

  1. tsc -b -- TypeScript türlerini kontrol eder
  2. vite build -- Tüm kodu paketler ve optimize eder

Vite, otomatik kod bölme ile küçültülmüş ve optimize edilmiş dosyalar üretir. Sonuç, uçuk hızlı bir statik sitedir.

Neden Bu Mimari?

Bir CMS, Hugo veya Jekyll gibi bir statik site oluşturucu, hatta Next.js kullanabilirdim. Ama bu yaklaşımı seçmemin nedeni şu:

  • Basitlik -- Markdown yaz, GitHub'a pushla, canlıya çıksın
  • Tam kontrol -- Bir CMS veya veritabanına bağımlılık yok
  • Performans -- Vite + React = hızlı yükleme
  • Esneklik -- Markdown ve HTML'i dilediğim gibi karıştırabilirim
  • Öğrenme -- React ve TypeScript'i ustalaşmak için harika bir proje
  • CI/CD -- GitHub Actions ile otomatik kalite kontrolleri ve dağıtım

Sonuç

Bu blog basit ama iyi düşünülmüş bir proje: içerik için Markdown, işleme için React, performans için Vite, CI/CD için GitHub Actions ve barındırma için GitHub Pages. Veritabanı yok, arka uç sunucusu yok, sadece her push'ta kaliteyi sağlayan otomatik bir pipeline ile verimli bir şekilde sunulan statik dosyalar.

Okuduğun için teşekkürler, bir sonraki makalede görüşmek üzere! 🦊

✨ AI Generated Article

Come Funziona Questo Blog?

Un'analisi approfondita degli interni di questo blog: React, Vite,

Come Funziona Questo Blog?

Ti sei mai chiesto come funziona questo blog sotto il cofano? In questo articolo, ti guiderò attraverso l'intera architettura dell'applicazione, dal tech stack fino al processo di scrittura di un articolo. E sì, ti mostrerò anche come scrivo i miei articoli da VS Code!

Il Tech Stack

Questo blog è costruito con tecnologie web moderne:

  • React 19 -- per l'interfaccia utente
  • TypeScript -- per codice tipizzato e più affidabile
  • Vite -- come strumento di build ultra-veloce
  • React Router v7 -- per la navigazione tra le pagine
  • react-markdown -- per trasformare Markdown in HTML
  • rehype-raw + rehype-sanitize -- per permettere HTML grezzo in Markdown rimanendo sicuri

Struttura del Progetto

Ecco com'è l'albero del progetto:

├── .github/
│   └── workflows/
│       └── deploy.yml        ← Pipeline CI/CD
├── public/
│   ├── home.md               ← Contenuto della homepage
│   ├── portfolio.md          ← Contenuto del portfolio
│   └── articles/
│       ├── index.json        ← Elenco di tutti gli articoli
│       ├── hello-world.md    ← Un articolo
│       ├── how-this-blog-works.md  ← Questo articolo!
│       └── /articles/assets/           ← Immagini degli articoli
├── src/
│   ├── main.tsx              ← Punto di ingresso React
│   ├── App.tsx               ← Router principale
│   ├── components/
│   │   ├── Header.tsx        ← Barra di navigazione
│   │   └── Footer.tsx        ← Footer
│   └── pages/
│       ├── Home.tsx          ← Homepage
│       ├── BlogList.tsx      ← Elenco articoli
│       ├── Article.tsx       ← Lettore articoli
│       ├── Portfolio.tsx     ← Pagina portfolio
│       └── NotFound.tsx      ← Pagina 404
└── vite.config.ts            ← Configurazione Vite

L'idea centrale è semplice: il contenuto è separato dal codice. Le pagine sono scritte in Markdown nella cartella public/, e il codice React in src/ si occupa di renderizzarle.

Il Sistema di Routing

Il file App.tsx definisce tutte le rotte dell'applicazione usando React Router:

Route Pagina Descrizione
/ Home Homepage, carica home.md
/blog BlogList Elenco di tutti gli articoli
/blog/:slug Article Un singolo articolo, carica articles/{slug}.md
/portfolio Portfolio Pagina portfolio, carica portfolio.md
* NotFound Pagina 404 per URL sconosciuti

Ogni pagina ha un ruolo ben definito: recupera un file Markdown, lo trasforma in HTML con react-markdown, e lo mostra a schermo.

Come Funziona un Articolo?

Questa è la parte più interessante! Ecco il ciclo di vita di un articolo:

1. Il File index.json

Tutti gli articoli sono referenziati in public/articles/index.json. Ogni voce contiene i metadati dell'articolo:

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "Un post di esempio per il blog di Fox.",
    "date": "2026-03-08"
  }
]
  • slug -- l'identificatore univoco, usato nell'URL (/blog/hello-world)
  • title -- il titolo mostrato nell'elenco
  • description -- un breve riassunto
  • date -- la data di pubblicazione

2. Il File Markdown

Il contenuto dell'articolo è un semplice file .md in public/articles/. Il nome del file corrisponde allo slug definito in index.json.

Puoi metterci qualsiasi cosa: intestazioni, elenchi, immagini, tabelle e persino HTML grezzo grazie a rehype-raw!

3. Rendering lato React

Quando visiti /blog/hello-world, ecco cosa succede:

  1. React Router cattura il parametro slug dall'URL
  2. Il componente Article.tsx recupera /articles/hello-world.md
  3. Il Markdown viene trasformato in HTML da react-markdown
  4. I link a /articles/assets/ vengono automaticamente riscritti a /articles//articles/assets/
  5. In parallelo, i metadati vengono caricati da index.json per mostrare data e descrizione

Semplice, no?

La Homepage e il Portfolio

Le pagine Home e Portfolio funzionano esattamente allo stesso modo: caricano un file Markdown (home.md o portfolio.md) e lo renderizzano come HTML.

La particolarità è che usano uno schema di sanitizzazione personalizzato che permette gli attributi class e style su tutti gli elementi HTML. Questo mi permette di scrivere HTML stilizzato direttamente in Markdown, come le gallerie di immagini per esempio.

L'Header è fissato in cima alla pagina con position: fixed. Contiene:

  • Il mio avatar GitHub (caricato direttamente da github.com/fox3000foxy.png)
  • Il titolo del blog
  • Link di navigazione: Home, Blog, Portfolio

Il Footer è minimalista: solo un copyright con l'anno corrente calcolato dinamicamente.

Il Tema Scuro

Il sito è sempre in modalità scura -- niente interruttore chiaro/scuro. È una scelta deliberata: color-scheme: dark è impostato negli stili globali, con sfondo nero #000 e testo bianco #fff. I link sono blu (#64b5f6) e diventano verdi al passaggio del mouse (#81c784).

Come Scrivo un Articolo

Ora la parte pratica! Ecco il mio flusso di lavoro per scrivere un nuovo articolo:

Passo 1: Creare il File Markdown

Apro VS Code e creo un nuovo file .md in public/articles/:

Passo 2: Scrivere il Contenuto

Scrivo il contenuto dell'articolo direttamente in Markdown. VS Code offre un'ottima anteprima Markdown integrata:

Per le immagini, le metto in public/articles//articles/assets/ e le referenzio usando la sintassi Markdown standard:

![descrizione](/articles/assets/my-image.png)

Il componente Article.tsx riscrive automaticamente il percorso /articles/assets/ in /articles//articles/assets/ in modo che le immagini vengano visualizzate correttamente.

Passo 3: Registrare l'Articolo in index.json

Una volta che l'articolo è finito, lo aggiungo a public/articles/index.json così appare nell'elenco del blog:

Passo 4: Testare Localmente

Avvio il server di sviluppo Vite:

pnpm dev

Vite si avvia in millisecondi e posso vedere il mio articolo in tempo reale su localhost:5173:

Passo 5: Pubblicare

Un semplice git push è tutto ciò che serve! La pipeline CI/CD si occupa automaticamente del resto.

La Pipeline di Deploy CI/CD

Ho configurato una pipeline completa GitHub Actions che automatizza linting, build e deploy del sito ogni volta che faccio push su main. Analizziamola.

Il workflow vive in .github/workflows/deploy.yml ed è suddiviso in due job: build e deploy.

Trigger

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

La pipeline viene eseguita a ogni push su main e a ogni pull request che ha come target main. Questo significa che le PR vengono controllate (lint + build) prima del merge, ma solo i push su main attivano effettivamente un deploy.

Job 1: Build

Il job di build viene eseguito su ubuntu-latest e passa attraverso questi step:

  1. Checkout -- Clona il repository con tutta la storia (fetch-depth: 0)
  2. Setup pnpm -- Installa l'ultima versione di pnpm usando pnpm/action-setup@v4
  3. Setup Node.js 20 -- Configura Node con la cache di pnpm per installazioni più veloci
  4. Installa dipendenze -- Esegue pnpm install --frozen-lockfile per garantire build riproducibili (niente modifiche al lockfile)
  5. Lint -- Esegue pnpm run lint (ESLint) per individuare problemi di qualità del codice prima del build
  6. Build -- Esegue pnpm run build, che prima controlla i tipi TypeScript (tsc -b) poi impacchetta tutto con Vite
  7. Carica artefatto -- Carica la cartella dist/ come artefatto di build per il job di deploy

Se uno qualsiasi degli step fallisce -- un errore di lint, un errore di tipo, un errore di build -- l'intera pipeline si ferma e nulla viene pubblicato. Questo protegge il sito live da codice rotto.

Job 2: Deploy

Il job di deploy viene eseguito solo se:

  • Il job di build è riuscito (needs: build)
  • L'evento è un push (non una PR)
  • Il branch è main
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

Poi:

  1. Scarica l'artefatto di build -- Prende la cartella dist/ prodotta dal job di build
  2. Configura GitHub Pages -- Prepara l'ambiente Pages
  3. Carica su Pages -- Impacchetta la cartella dist/ per GitHub Pages
  4. Pubblica -- Pubblica il sito usando actions/deploy-pages@v4

Il Quadro Completo

Ecco cosa succede dalla scrittura al deploy:

Scrivi articolo in VS Code
        ↓
   git add & commit
        ↓
      git push
        ↓
  GitHub Actions si attiva
        ↓
  ┌─────────────────┐
  │   BUILD JOB     │
  │  1. Checkout    │
  │  2. Setup pnpm  │
  │  3. Setup Node  │
  │  4. Installa    │
  │  5. Lint ✓      │
  │  6. Build ✓     │
  │  7. Carica dist │
  └────────┬────────┘
           ↓
  ┌─────────────────┐
  │  DEPLOY JOB     │
  │  1. Scarica     │
  │  2. Configura   │
  │  3. Carica      │
  │  4. Pubblica 🚀  │
  └─────────────────┘
           ↓
    Live su GitHub Pages!

L'intero processo richiede circa un minuto dal push alla pubblicazione. Nessun deploy manuale, niente FTP, niente SSH -- solo git push ed è fatta.

Il Build di Produzione

Sotto il cofano, il comando pnpm build esegue:

  1. tsc -b -- Controlla i tipi TypeScript
  2. vite build -- Impacchetta e ottimizza tutto il codice

Vite produce file minificati e ottimizzati con code-splitting automatico. Il risultato è un sito statico velocissimo.

Perché Questa Architettura?

Avrei potuto usare un CMS, un generatore di siti statici come Hugo o Jekyll, o persino Next.js. Ma ecco perché ho scelto questo approccio:

  • Semplicità -- Scrivi in Markdown, fai push su GitHub, è live
  • Controllo totale -- Nessuna dipendenza da un CMS o database
  • Performance -- Vite + React = caricamento veloce
  • Flessibilità -- Posso mischiare Markdown e HTML come voglio
  • Apprendimento -- È un bel progetto per padroneggiare React e TypeScript
  • CI/CD -- Controlli di qualità automatici e deploy con GitHub Actions

Conclusione

Questo blog è un progetto semplice ma ben pensato: Markdown per i contenuti, React per il rendering, Vite per le performance, GitHub Actions per la CI/CD, e GitHub Pages per l'hosting. Niente database, niente server backend, solo file statici serviti in modo efficiente con una pipeline automatizzata che garantisce la qualità a ogni push.

Grazie per aver letto, e ci vediamo al prossimo articolo! 🦊

✨ AI Generated Article

Wie funktioniert dieser Blog?

Ein tiefer Einblick in die Interna dieses Blogs: React, Vite,

Wie funktioniert dieser Blog?

Schon mal gefragt, wie dieser Blog unter der Haube funktioniert? In diesem Artikel zeige ich dir die gesamte Architektur der Anwendung, vom Tech-Stack bis zum Prozess des Artikel-Schreibens. Und ja, ich zeige dir sogar, wie ich meine Artikel direkt aus VS Code schreibe!

Der Tech-Stack

Dieser Blog wurde mit modernen Web-Technologien gebaut:

  • React 19 -- für die Benutzeroberfläche
  • TypeScript -- für typisierten und zuverlässigeren Code
  • Vite -- als ultraschnelles Build-Tool
  • React Router v7 -- für die Navigation zwischen Seiten
  • react-markdown -- um Markdown in HTML zu verwandeln
  • rehype-raw + rehype-sanitize -- um rohes HTML in Markdown zu erlauben, dabei aber sicher zu bleiben

Projektstruktur

So sieht der Projektbaum aus:

├── .github/
│   └── workflows/
│       └── deploy.yml        ← CI/CD-Pipeline
├── public/
│   ├── home.md               ← Inhalt der Startseite
│   ├── portfolio.md           ← Portfolio-Inhalt
│   └── articles/
│       ├── index.json         ← Liste aller Artikel
│       ├── hello-world.md     ← Ein Artikel
│       ├── how-this-blog-works.md  ← Dieser Artikel!
│       └── /articles/assets/            ← Artikel-Bilder
├── src/
│   ├── main.tsx               ← React-Einstiegspunkt
│   ├── App.tsx                ← Haupt-Router
│   ├── components/
│   │   ├── Header.tsx         ← Navigationsleiste
│   │   └── Footer.tsx         ← Fußzeile
│   └── pages/
│       ├── Home.tsx           ← Startseite
│       ├── BlogList.tsx       ← Artikelliste
│       ├── Article.tsx        ← Artikel-Reader
│       ├── Portfolio.tsx      ← Portfolioseite
│       └── NotFound.tsx       ← 404-Seite
└── vite.config.ts             ← Vite-Konfiguration

Die Kernidee ist einfach: Inhalt ist vom Code getrennt. Seiten werden als Markdown im public/-Ordner geschrieben, und der React-Code in src/ kümmert sich um die Darstellung.

Das Routing-System

Die App.tsx-Datei definiert alle Anwendungsrouten mit React Router:

Route Seite Beschreibung
/ Home Startseite, lädt home.md
/blog BlogList Liste aller Artikel
/blog/:slug Article Ein einzelner Artikel, lädt articles/{slug}.md
/portfolio Portfolio Portfolioseite, lädt portfolio.md
* NotFound 404-Seite für unbekannte URLs

Jede Seite hat eine klar definierte Rolle: Sie holt eine Markdown-Datei, wandelt sie mit react-markdown in HTML um und zeigt sie auf dem Bildschirm an.

Wie funktioniert ein Artikel?

Das ist der interessanteste Teil! Hier ist der Lebenszyklus eines Artikels:

1. Die index.json-Datei

Alle Artikel werden in public/articles/index.json referenziert. Jeder Eintrag enthält die Metadaten des Artikels:

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug -- die eindeutige Kennung, die in der URL verwendet wird (/blog/hello-world)
  • title -- der Titel, der in der Liste angezeigt wird
  • description -- eine kurze Zusammenfassung
  • date -- das Veröffentlichungsdatum

2. Die Markdown-Datei

Der Artikel-Inhalt ist eine einfache .md-Datei in public/articles/. Der Dateiname entspricht dem slug aus der index.json.

Du kannst alles Mögliche hineinpacken: Überschriften, Listen, Bilder, Tabellen und sogar rohes HTML, dank rehype-raw!

3. Rendering auf React-Seite

Wenn du /blog/hello-world besuchst, passiert Folgendes:

  1. React Router fängt den slug-Parameter aus der URL ab
  2. Die Article.tsx-Komponente holt /articles/hello-world.md
  3. Das Markdown wird von react-markdown in HTML umgewandelt
  4. Links zu /articles/assets/ werden automatisch zu /articles//articles/assets/ umgeschrieben
  5. Parallel dazu werden die Metadaten aus index.json geladen, um Datum und Beschreibung anzuzeigen

So einfach ist das!

Die Startseite und das Portfolio

Die Startseite und die Portfolioseite funktionieren genauso: Sie laden eine Markdown-Datei (home.md oder portfolio.md) und rendern sie als HTML.

Das Besondere ist, dass sie ein benutzerdefiniertes Sanitierungs-Schema verwenden, das class- und style-Attribute auf allen HTML-Elementen erlaubt. Dadurch kann ich gestyltes HTML direkt in Markdown schreiben, wie zum Beispiel Bildergalerien.

Der Header ist mit position: fixed oben auf der Seite fixiert. Er enthält:

  • Mein GitHub-Avatar (direkt von github.com/fox3000foxy.png geladen)
  • Den Blog-Titel
  • Navigationslinks: Home, Blog, Portfolio

Der Footer ist minimalistisch: nur ein Copyright mit dem aktuellen Jahr, dynamisch berechnet.

Das dunkle Design

Die Seite ist immer im Dark Mode -- kein Hell/Dunkel-Umschalter. Das ist eine bewusste Entscheidung: color-scheme: dark ist in den globalen Styles gesetzt, mit schwarzem Hintergrund #000 und weißem Text #fff. Links sind blau (#64b5f6) und werden beim Überfahren grün (#81c784).

Wie ich einen Artikel schreibe

Jetzt zum praktischen Teil! Hier ist mein Workflow zum Schreiben eines neuen Artikels:

Schritt 1: Markdown-Datei erstellen

Ich öffne VS Code und erstelle eine neue .md-Datei in public/articles/:

Schritt 2: Inhalt schreiben

Ich schreibe den Artikel-Inhalt direkt in Markdown. VS Code bietet eine hervorragende integrierte Markdown-Vorschau:

Für Bilder lege ich sie in public/articles//articles/assets/ ab und verweise mit der standardmäßigen Markdown-Syntax darauf:

![description](/articles/assets/my-image.png)

Die Article.tsx-Komponente schreibt den Pfad /articles/assets/ automatisch zu /articles//articles/assets/ um, damit die Bilder korrekt angezeigt werden.

Schritt 3: Artikel in index.json registrieren

Sobald der Artikel fertig ist, füge ich ihn zu public/articles/index.json hinzu, damit er in der Blog-Liste erscheint:

Schritt 4: Lokal testen

Ich starte den Vite-Dev-Server:

pnpm dev

Vite startet in Millisekunden und ich kann meinen Artikel in Echtzeit unter localhost:5173 sehen:

Schritt 5: Veröffentlichen

Ein einfaches git push reicht! Die CI/CD-Pipeline erledigt den Rest automatisch.

Die CI/CD-Deployment-Pipeline

Ich habe eine vollständige GitHub Actions-Pipeline eingerichtet, die Linting, Build und Deployment der Website automatisiert, jedes Mal wenn ich auf main pushe. Schauen wir sie uns genauer an.

Der Workflow lebt in .github/workflows/deploy.yml und ist in zwei Jobs aufgeteilt: build und deploy.

Auslöser

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

Die Pipeline läuft bei jedem Push auf main und bei jedem Pull Request, der auf main abzielt. Das bedeutet, dass PRs vor dem Merge geprüft werden (Lint + Build), aber nur Pushs auf main lösen tatsächlich ein Deployment aus.

Job 1: Build

Der Build-Job läuft auf ubuntu-latest und durchläuft diese Schritte:

  1. Checkout -- Klont das Repository mit vollständiger Historie (fetch-depth: 0)
  2. Setup pnpm -- Installiert die neueste Version von pnpm mit pnpm/action-setup@v4
  3. Setup Node.js 20 -- Konfiguriert Node mit pnpm-Caching für schnellere Installationen
  4. Abhängigkeiten installieren -- Führt pnpm install --frozen-lockfile aus, um reproduzierbare Builds zu gewährleisten (keine Lockfile-Änderungen erlaubt)
  5. Lint -- Führt pnpm run lint (ESLint) aus, um Code-Qualitätsprobleme vor dem Build zu erkennen
  6. Build -- Führt pnpm run build aus, das zuerst TypeScript-Typen prüft (tsc -b) und dann alles mit Vite bündelt
  7. Artifact hochladen -- Lädt den dist/-Ordner als Build-Artefakt für den Deploy-Job hoch

Wenn einer der Schritte fehlschlägt -- ein Lint-Fehler, ein Typ-Fehler, ein Build-Fehler -- stoppt die gesamte Pipeline und es wird nichts deployed. Das schützt die Live-Seite vor fehlerhaftem Code.

Job 2: Deploy

Der Deploy-Job läuft nur, wenn:

  • Der Build-Job erfolgreich war (needs: build)
  • Das Ereignis ein Push ist (kein PR)
  • Der Branch main ist
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

Dann:

  1. Build-Artefakt herunterladen -- Holt den dist/-Ordner aus dem Build-Job
  2. GitHub Pages konfigurieren -- Richtet die Pages-Umgebung ein
  3. Zu Pages hochladen -- Packt den dist/-Ordner für GitHub Pages
  4. Deployen -- Veröffentlicht die Seite mit actions/deploy-pages@v4

Das vollständige Bild

So läuft es vom Schreiben bis zum Deployment:

Artikel in VS Code schreiben
        ↓
   git add & commit
        ↓
      git push
        ↓
  GitHub Actions wird ausgelöst
        ↓
  ┌─────────────────┐
  │   BUILD-JOB     │
  │  1. Checkout    │
  │  2. Setup pnpm  │
  │  3. Setup Node  │
  │  4. Installieren│
  │  5. Lint ✓      │
  │  6. Build ✓     │
  │  7. Upload dist │
  └────────┬────────┘
           ↓
  ┌─────────────────┐
  │  DEPLOY-JOB     │
  │  1. Herunterl.  │
  │  2. Konfigurieren│
  │  3. Hochladen   │
  │  4. Deploy 🚀   │
  └─────────────────┘
           ↓
    Live auf GitHub Pages!

Der gesamte Vorgang dauert etwa eine Minute vom Push bis zur Live-Schaltung. Kein manuelles Deployment, kein FTP, kein SSH -- nur git push und es ist erledigt.

Der Production Build

Unter der Haube führt pnpm build Folgendes aus:

  1. tsc -b -- Prüft TypeScript-Typen
  2. vite build -- Bündelt und optimiert den gesamten Code

Vite produziert minifizierte und optimierte Dateien mit automatischem Code-Splitting. Das Ergebnis ist eine blitzschnelle statische Website.

Warum diese Architektur?

Ich hätte ein CMS, einen Static-Site-Generator wie Hugo oder Jekyll oder sogar Next.js verwenden können. Aber hier ist, warum ich mich für diesen Ansatz entschieden habe:

  • Einfachheit -- In Markdown schreiben, auf GitHub pushen, es ist live
  • Volle Kontrolle -- Keine Abhängigkeit von einem CMS oder einer Datenbank
  • Leistung -- Vite + React = schnelles Laden
  • Flexibilität -- Ich kann Markdown und HTML nach Belieben mischen
  • Lernen -- Es ist ein großartiges Projekt, um React und TypeScript zu meistern
  • CI/CD -- Automatisierte Qualitätschecks und Deployment mit GitHub Actions

Fazit

Dieser Blog ist ein einfaches, aber durchdachtes Projekt: Markdown für Inhalte, React fürs Rendering, Vite für Leistung, GitHub Actions für CI/CD und GitHub Pages fürs Hosting. Keine Datenbank, kein Backend-Server, nur statische Dateien, die effizient ausgeliefert werden, mit einer automatisierten Pipeline, die bei jedem Push die Qualität sicherstellt.

Danke fürs Lesen und bis zum nächsten Artikel! 🦊

✨ AI Generated Article

Как работает этот блог?

Глубокое погружение во внутреннее устройство блога: React, Vite,

Как работает этот блог?

Когда-нибудь задумывался, как этот блог устроен под капотом? В этой статье я проведу тебя по всей архитектуре приложения -- от технологического стека до процесса написания статьи. И да, я даже покажу, как я пишу статьи прямо из VS Code!

Технологический стек

Этот блог построен на современных веб-технологиях:

  • React 19 -- для пользовательского интерфейса
  • TypeScript -- для типизированного и более надёжного кода
  • Vite -- как сверхбыстрый инструмент сборки
  • React Router v7 -- для навигации между страницами
  • react-markdown -- для преобразования Markdown в HTML
  • rehype-raw + rehype-sanitize -- чтобы разрешить сырой HTML в Markdown, оставаясь в безопасности

Структура проекта

Вот как выглядит дерево проекта:

├── .github/
│   └── workflows/
│       └── deploy.yml              ← CI/CD пайплайн
├── public/
│   ├── home.md                     ← Контент главной страницы
│   ├── portfolio.md                ← Контент портфолио
│   └── articles/
│       ├── index.json              ← Список всех статей
│       ├── hello-world.md          ← Пример статьи
│       ├── how-this-blog-works.md  ← Эта статья!
│       └── /articles/assets/                 ← Изображения для статей
├── src/
│   ├── main.tsx                    ← Точка входа React
│   ├── App.tsx                     ← Главный роутер
│   ├── components/
│   │   ├── Header.tsx              ← Панель навигации
│   │   └── Footer.tsx              ← Подвал
│   └── pages/
│       ├── Home.tsx                ← Главная страница
│       ├── BlogList.tsx            ← Список статей
│       ├── Article.tsx             ← Читалка статей
│       ├── Portfolio.tsx           ← Страница портфолио
│       └── NotFound.tsx            ← Страница 404
└── vite.config.ts                  ← Конфигурация Vite

Основная идея проста: контент отделён от кода. Страницы написаны в Markdown в папке public/, а React-код в src/ занимается их отрисовкой.

Система маршрутизации

Файл App.tsx определяет все маршруты приложения через React Router:

Маршрут Страница Описание
/ Home Главная страница, загружает home.md
/blog BlogList Список всех статей
/blog/:slug Article Одна статья, загружает articles/{slug}.md
/portfolio Portfolio Страница портфолио, загружает portfolio.md
* NotFound Страница 404 для неизвестных URL

У каждой страницы есть чётко определённая роль: она загружает Markdown-файл, преобразует его в HTML с помощью react-markdown и отображает на экране.

Как работает статья?

Это самая интересная часть! Вот жизненный цикл статьи:

1. Файл index.json

Все статьи указаны в public/articles/index.json. Каждая запись содержит метаданные статьи:

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug -- уникальный идентификатор, используется в URL (/blog/hello-world)
  • title -- заголовок, отображаемый в списке
  • description -- краткое описание
  • date -- дата публикации

2. Markdown-файл

Содержимое статьи -- это простой .md файл в public/articles/. Имя файла соответствует slug, указанному в index.json.

Туда можно поместить что угодно: заголовки, списки, изображения, таблицы и даже сырой HTML благодаря rehype-raw!

3. Отрисовка на стороне React

Когда ты заходишь на /blog/hello-world, происходит следующее:

  1. React Router извлекает параметр slug из URL
  2. Компонент Article.tsx загружает /articles/hello-world.md
  3. Markdown преобразуется в HTML с помощью react-markdown
  4. Ссылки на /articles/assets/ автоматически переписываются в /articles//articles/assets/
  5. Параллельно загружаются метаданные из index.json для отображения даты и описания

Вот так всё просто!

Главная страница и портфолио

Главная страница и страница портфолио работают точно так же: они загружают Markdown-файл (home.md или portfolio.md) и отображают его как HTML.

Особенность в том, что они используют собственную схему санитизации, которая разрешает атрибуты class и style на всех HTML-элементах. Это позволяет писать стилизованный HTML прямо в Markdown, например, галереи изображений.

Шапка и подвал

Шапка закреплена вверху страницы с помощью position: fixed. Она содержит:

  • Мой аватар GitHub (загружается напрямую с github.com/fox3000foxy.png)
  • Название блога
  • Ссылки навигации: Home, Blog, Portfolio

Подвал минималистичен: просто копирайт с текущим годом, вычисляемым динамически.

Тёмная тема

Сайт всегда в тёмном режиме -- никакого переключения светлой/тёмной темы. Это осознанный выбор: color-scheme: dark установлен в глобальных стилях, с чёрным фоном #000 и белым текстом #fff. Ссылки синие (#64b5f6) и становятся зелёными при наведении (#81c784).

Как я пишу статью

А теперь практическая часть! Вот мой воркфлоу для написания новой статьи:

Шаг 1: Создание Markdown-файла

Я открываю VS Code и создаю новый .md файл в public/articles/:

Шаг 2: Написание контента

Я пишу содержимое статьи прямо в Markdown. В VS Code есть отличный встроенный предпросмотр Markdown:

Для изображений я помещаю их в public/articles//articles/assets/ и ссылаюсь через стандартный синтаксис Markdown:

![description](/articles/assets/my-image.png)

Компонент Article.tsx автоматически переписывает путь /articles/assets/ в /articles//articles/assets/, чтобы изображения отображались корректно.

Шаг 3: Регистрация статьи в index.json

Когда статья готова, я добавляю её в public/articles/index.json, чтобы она появилась в списке блога:

Шаг 4: Локальное тестирование

Я запускаю дев-сервер Vite:

pnpm dev

Vite запускается за миллисекунды, и я могу видеть свою статью в реальном времени на localhost:5173:

Шаг 5: Публикация

Простой git push -- и всё! CI/CD пайплайн обрабатывает остальное автоматически.

Пайплайн CI/CD деплоя

Я настроил полноценный пайплайн GitHub Actions, который автоматизирует линтинг, сборку и деплой сайта каждый раз, когда я пущу в main. Давай разберём его.

Воркфлоу находится в .github/workflows/deploy.yml и разделён на две задачи: build и deploy.

Триггеры

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

Пайплайн запускается при каждом пуше в main и при каждом пулл-реквесте, нацеленном на main. Это значит, что PR проверяются (линт + сборка) перед слиянием, но только пуши в main запускают деплой.

Задача 1: Build

Задача сборки выполняется на ubuntu-latest и проходит следующие шаги:

  1. Checkout -- Клонирует репозиторий с полной историей (fetch-depth: 0)
  2. Setup pnpm -- Устанавливает последнюю версию pnpm через pnpm/action-setup@v4
  3. Setup Node.js 20 -- Настраивает Node с кешированием pnpm для более быстрой установки
  4. Install dependencies -- Выполняет pnpm install --frozen-lockfile для воспроизводимой сборки (никаких изменений lockfile)
  5. Lint -- Запускает pnpm run lint (ESLint) для проверки качества кода перед сборкой
  6. Build -- Запускает pnpm run build, который сначала проверяет типы TypeScript (tsc -b), затем собирает всё через Vite
  7. Upload artifact -- Загружает папку dist/ как артефакт сборки для задачи деплоя

Если какой-то шаг завершается ошибкой -- ошибка линтинга, ошибка типов, ошибка сборки -- весь пайплайн останавливается, и ничего не деплоится. Это защищает живой сайт от сломанного кода.

Задача 2: Deploy

Задача деплоя запускается только если:

  • Задача сборки завершена успешно (needs: build)
  • Событие -- это push (не PR)
  • Ветка -- main
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

Затем она:

  1. Загружает артефакт сборки -- получает папку dist/, созданную задачей сборки
  2. Настраивает GitHub Pages -- настраивает окружение Pages
  3. Загружает на Pages -- упаковывает папку dist/ для GitHub Pages
  4. Деплоит -- Публикует сайт с помощью actions/deploy-pages@v4

Полная картина

Вот что происходит от написания до деплоя:

Написание статьи в VS Code
         ↓
    git add & commit
         ↓
       git push
         ↓
  GitHub Actions запускается
         ↓
  ┌─────────────────┐
  │   BUILD JOB     │
  │  1. Checkout    │
  │  2. Setup pnpm  │
  │  3. Setup Node  │
  │  4. Install     │
  │  5. Lint ✓      │
  │  6. Build ✓     │
  │  7. Upload dist │
  └────────┬────────┘
           ↓
  ┌─────────────────┐
  │  DEPLOY JOB     │
  │  1. Download    │
  │  2. Configure   │
  │  3. Upload      │
  │  4. Deploy 🚀   │
  └─────────────────┘
           ↓
    Запущено на GitHub Pages!

Весь процесс занимает около минуты от пуша до запуска. Никакого ручного деплоя, FTP или SSH -- просто git push, и готово.

Production-сборка

Под капотом команда pnpm build выполняет:

  1. tsc -b -- Проверяет типы TypeScript
  2. vite build -- Собирает и оптимизирует весь код

Vite создаёт минифицированные и оптимизированные файлы с автоматическим разделением кода. В результате получается молниеносный статический сайт.

Почему такая архитектура?

Я мог бы использовать CMS, генератор статических сайтов вроде Hugo или Jekyll, или даже Next.js. Но вот почему я выбрал этот подход:

  • Простота -- Пиши в Markdown, пуши на GitHub, готово
  • Полный контроль -- Никакой зависимости от CMS или базы данных
  • Производительность -- Vite + React = быстрая загрузка
  • Гибкость -- Я могу смешивать Markdown и HTML как захочу
  • Обучение -- Отличный проект для освоения React и TypeScript
  • CI/CD -- Автоматизированные проверки качества и деплой через GitHub Actions

Заключение

Этот блог -- простой, но продуманный проект: Markdown для контента, React для рендеринга, Vite для производительности, GitHub Actions для CI/CD и GitHub Pages для хостинга. Никакой базы данных, никакого бэкенд-сервера, просто статические файлы, эффективно обслуживаемые с автоматизированным пайплайном, обеспечивающим качество при каждом пуше.

Спасибо за чтение, и увидимся в следующей статье! 🦊

✨ AI Generated Article

¿Cómo Funciona Este Blog?

Una exploración a fondo de los internos de este blog: React, Vite,

¿Cómo Funciona Este Blog?

¿Alguna vez te has preguntado cómo funciona este blog por dentro? En este artículo, te explicaré toda la arquitectura de la aplicación, desde el stack tecnológico hasta el proceso de escribir un artículo. Y sí, ¡incluso te mostraré cómo escribo mis artículos desde VS Code!

El Stack Tecnológico

Este blog está construido con tecnologías web modernas:

  • React 19 -- para la interfaz de usuario
  • TypeScript -- para código tipado y más fiable
  • Vite -- como herramienta de construcción ultrarrápida
  • React Router v7 -- para la navegación entre páginas
  • react-markdown -- para transformar Markdown en HTML
  • rehype-raw + rehype-sanitize -- para permitir HTML crudo en Markdown de forma segura

Estructura del Proyecto

Así es como se ve el árbol del proyecto:

├── .github/
│   └── workflows/
│       └── deploy.yml              ← Pipeline de CI/CD
├── public/
│   ├── home.md                     ← Contenido de la página de inicio
│   ├── portfolio.md                ← Contenido del portafolio
│   └── articles/
│       ├── index.json              ← Lista de todos los artículos
│       ├── hello-world.md          ← Un artículo
│       ├── how-this-blog-works.md  ← ¡Este artículo!
│       └── /articles/assets/                 ← Imágenes de los artículos
├── src/
│   ├── main.tsx                    ← Punto de entrada de React
│   ├── App.tsx                     ← Enrutador principal
│   ├── components/
│   │   ├── Header.tsx              ← Barra de navegación
│   │   └── Footer.tsx              ← Pie de página
│   └── pages/
│       ├── Home.tsx                ← Página de inicio
│       ├── BlogList.tsx            ← Lista de artículos
│       ├── Article.tsx             ← Lector de artículos
│       ├── Portfolio.tsx           ← Página de portafolio
│       └── NotFound.tsx            ← Página 404
└── vite.config.ts                  ← Configuración de Vite

La idea central es simple: el contenido está separado del código. Las páginas están escritas en Markdown en la carpeta public/, y el código React en src/ se encarga de renderizarlas.

El Sistema de Enrutamiento

El archivo App.tsx define todas las rutas de la aplicación usando React Router:

Ruta Página Descripción
/ Home Página de inicio, carga home.md
/blog BlogList Lista de todos los artículos
/blog/:slug Article Un artículo individual, carga articles/{slug}.md
/portfolio Portfolio Página de portafolio, carga portfolio.md
* NotFound Página 404 para URLs desconocidas

Cada página tiene un rol bien definido: obtiene un archivo Markdown, lo transforma en HTML con react-markdown, y lo muestra en pantalla.

¿Cómo Funciona un Artículo?

¡Esta es la parte más interesante! Aquí está el ciclo de vida de un artículo:

1. El Archivo index.json

Todos los artículos están referenciados en public/articles/index.json. Cada entrada contiene los metadatos del artículo:

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug -- el identificador único, usado en la URL (/blog/hello-world)
  • title -- el título mostrado en la lista
  • description -- un resumen corto
  • date -- la fecha de publicación

2. El Archivo Markdown

El contenido del artículo es un simple archivo .md en public/articles/. El nombre del archivo coincide con el slug definido en index.json.

¡Puedes poner cualquier cosa ahí: encabezados, listas, imágenes, tablas, e incluso HTML crudo gracias a rehype-raw!

3. Renderizado del Lado de React

Cuando visitas /blog/hello-world, esto es lo que sucede:

  1. React Router captura el parámetro slug de la URL
  2. El componente Article.tsx obtiene /articles/hello-world.md
  3. El Markdown se transforma en HTML mediante react-markdown
  4. Los enlaces a /articles/assets/ se reescriben automáticamente a /articles//articles/assets/
  5. En paralelo, los metadatos se cargan desde index.json para mostrar la fecha y la descripción

¡Así de simple!

La Página de Inicio y el Portafolio

Las páginas de Inicio y Portafolio funcionan exactamente igual: cargan un archivo Markdown (home.md o portfolio.md) y lo renderizan como HTML.

Lo especial es que usan un esquema de sanitización personalizado que permite atributos class y style en todos los elementos HTML. Esto me permite escribir HTML con estilo directamente en Markdown, como galerías de imágenes por ejemplo.

El Encabezado y el Pie de Página

El Header está fijado en la parte superior de la página con position: fixed. Contiene:

  • Mi avatar de GitHub (cargado directamente de github.com/fox3000foxy.png)
  • El título del blog
  • Enlaces de navegación: Inicio, Blog, Portafolio

El Footer es minimalista: solo un copyright con el año actual calculado dinámicamente.

El Tema Oscuro

El sitio está siempre en modo oscuro -- no hay interruptor claro/oscuro. Esta es una elección deliberada: color-scheme: dark está configurado en los estilos globales, con fondo negro #000 y texto blanco #fff. Los enlaces son azules (#64b5f6) y se vuelven verdes al pasar el ratón (#81c784).

Cómo Escribo un Artículo

¡Ahora la parte práctica! Aquí está mi flujo de trabajo para escribir un nuevo artículo:

Paso 1: Crear el Archivo Markdown

Abro VS Code y creo un nuevo archivo .md en public/articles/:

Paso 2: Escribir el Contenido

Escribo el contenido del artículo directamente en Markdown. VS Code ofrece una excelente vista previa integrada de Markdown:

Para las imágenes, las coloco en public/articles//articles/assets/ y las referencio usando la sintaxis estándar de Markdown:

![description](/articles/assets/my-image.png)

El componente Article.tsx reescribe automáticamente la ruta /articles/assets/ a /articles//articles/assets/ para que las imágenes se muestren correctamente.

Paso 3: Registrar el Artículo en index.json

Una vez que el artículo está terminado, lo añado a public/articles/index.json para que aparezca en la lista del blog:

Paso 4: Probar Localmente

Inicio el servidor de desarrollo de Vite:

pnpm dev

Vite se inicia en milisegundos y puedo ver mi artículo en tiempo real en localhost:5173:

Paso 5: Publicar

¡Un simple git push es todo lo que se necesita! El pipeline de CI/CD se encarga del resto automáticamente.

El Pipeline de Despliegue CI/CD

He configurado un pipeline completo de GitHub Actions que automatiza el linting, la construcción y el despliegue del sitio cada vez que hago push a main. Vamos a desglosarlo.

El workflow vive en .github/workflows/deploy.yml y está dividido en dos trabajos: build y deploy.

Disparadores

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

El pipeline se ejecuta en cada push a main y en cada pull request dirigido a main. Esto significa que los PRs se verifican (lint + build) antes de fusionarse, pero solo los pushes a main desencadenan un despliegue real.

Trabajo 1: Build

El trabajo de build se ejecuta en ubuntu-latest y pasa por estos pasos:

  1. Checkout -- Clona el repositorio con historial completo (fetch-depth: 0)
  2. Setup pnpm -- Instala la última versión de pnpm usando pnpm/action-setup@v4
  3. Setup Node.js 20 -- Configura Node con caché de pnpm habilitada para instalaciones más rápidas
  4. Install dependencies -- Ejecuta pnpm install --frozen-lockfile para garantizar builds reproducibles (no se permiten cambios en el lockfile)
  5. Lint -- Ejecuta pnpm run lint (ESLint) para detectar problemas de calidad del código antes de construir
  6. Build -- Ejecuta pnpm run build, que primero verifica los tipos de TypeScript (tsc -b) y luego empaqueta todo con Vite
  7. Upload artifact -- Sube la carpeta dist/ como un artefacto de construcción para el trabajo de deploy

Si algún paso falla -- un error de lint, un error de tipo, un error de build -- todo el pipeline se detiene y no se despliega nada. Esto mantiene el sitio en vivo a salvo de código roto.

Trabajo 2: Deploy

El trabajo de deploy solo se ejecuta si:

  • El trabajo de build tuvo éxito (needs: build)
  • El evento es un push (no un PR)
  • La rama es main
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

Entonces:

  1. Descarga el artefacto de build -- Obtiene la carpeta dist/ producida por el trabajo de build
  2. Configura GitHub Pages -- Prepara el entorno de Pages
  3. Sube a Pages -- Empaqueta la carpeta dist/ para GitHub Pages
  4. Despliega -- Publica el sitio usando actions/deploy-pages@v4

El Panorama Completo

Esto es lo que sucede desde la escritura hasta el despliegue:

Escribir artículo en VS Code
        ↓
   git add & commit
        ↓
     git push
        ↓
  GitHub Actions se activa
        ↓
  ┌─────────────────┐
  │  TRABAJO BUILD  │
  │  1. Checkout    │
  │  2. Setup pnpm  │
  │  3. Setup Node  │
  │  4. Install     │
  │  5. Lint ✓      │
  │  6. Build ✓     │
  │  7. Upload dist │
  └────────┬────────┘
           ↓
  ┌─────────────────┐
  │ TRABAJO DEPLOY  │
  │  1. Download    │
  │  2. Configure   │
  │  3. Upload      │
  │  4. Deploy 🚀   │
  └─────────────────┘
           ↓
   ¡En vivo en GitHub Pages!

Todo el proceso toma aproximadamente un minuto desde el push hasta la publicación. Sin despliegue manual, sin FTP, sin SSH -- solo git push y está hecho.

El Build de Producción

Bajo el capó, el comando pnpm build ejecuta:

  1. tsc -b -- Verifica los tipos de TypeScript
  2. vite build -- Empaqueta y optimiza todo el código

Vite produce archivos minificados y optimizados con división de código automática. El resultado es un sitio estático ultrarrápido.

¿Por Qué Esta Arquitectura?

Podría haber usado un CMS, un generador de sitios estáticos como Hugo o Jekyll, o incluso Next.js. Pero aquí está por qué elegí este enfoque:

  • Simplicidad -- Escribe en Markdown, haz push a GitHub, está en vivo
  • Control total -- Sin dependencia de un CMS o base de datos
  • Rendimiento -- Vite + React = carga rápida
  • Flexibilidad -- Puedo mezclar Markdown y HTML como quiera
  • Aprendizaje -- Es un gran proyecto para dominar React y TypeScript
  • CI/CD -- Verificaciones de calidad automatizadas y despliegue con GitHub Actions

Conclusión

Este blog es un proyecto simple pero bien pensado: Markdown para el contenido, React para el renderizado, Vite para el rendimiento, GitHub Actions para CI/CD y GitHub Pages para el alojamiento. Sin base de datos, sin servidor backend, solo archivos estáticos servidos eficientemente con un pipeline automatizado que garantiza la calidad en cada push.

Gracias por leer, y nos vemos en el próximo artículo! 🦊

✨ AI Generated Article

Como este blog funciona?

Os bastidores do blog: React, Vite, Markdown, o pipeline CI/CD

Como Este Blog Funciona?

Você já se perguntou como este blog funciona por baixo dos panos? Neste artigo, vou detalhar toda a arquitetura da aplicação, desde a stack técnica até o processo de redação de um artigo. E sim, vou até te mostrar como escrevo meus artigos diretamente do VS Code!

A Stack Técnica

Este blog é construído com tecnologias web modernas:

  • React 19 -- para a interface do usuário
  • TypeScript -- para um código tipado e mais confiável
  • Vite -- como ferramenta de build ultra-rápida
  • React Router v7 -- para a navegação entre as páginas
  • react-markdown -- para transformar Markdown em HTML
  • rehype-raw + rehype-sanitize -- para permitir HTML bruto no Markdown com segurança

Estrutura do Projeto

Aqui está como é a árvore do projeto:

├── .github/
│   └── workflows/
│       └── deploy.yml              ← Pipeline CI/CD
├── public/
│   ├── home.md                     ← Conteúdo da página inicial
│   ├── portfolio.md                ← Conteúdo do portfólio
│   └── articles/
│       ├── index.json              ← Lista de todos os artigos
│       ├── hello-world.md          ← Um artigo
│       ├── how-this-blog-works.md  ← Este artigo!
│       └── /articles/assets/                 ← Imagens dos artigos
├── src/
│   ├── main.tsx                    ← Ponto de entrada React
│   ├── App.tsx                     ← Roteador principal
│   ├── components/
│   │   ├── Header.tsx              ← Barra de navegação
│   │   └── Footer.tsx              ← Rodapé
│   └── pages/
│       ├── Home.tsx                ← Página inicial
│       ├── BlogList.tsx            ← Lista de artigos
│       ├── Article.tsx             ← Leitor de artigos
│       ├── Portfolio.tsx           ← Página de portfólio
│       └── NotFound.tsx            ← Página 404
└── vite.config.ts                  ← Configuração Vite

A ideia central é simples: o conteúdo é separado do código. As páginas são escritas em Markdown na pasta public/, e o código React em src/ cuida de exibi-las.

O Sistema de Roteamento

O arquivo App.tsx define todas as rotas da aplicação com React Router:

Rota Página Descrição
/ Home Página inicial, carrega home.md
/blog BlogList Lista de todos os artigos
/blog/:slug Article Um artigo, carrega articles/{slug}.md
/portfolio Portfolio Página de portfólio, carrega portfolio.md
* NotFound Página 404 para URLs desconhecidas

Cada página tem um papel bem definido: ela busca um arquivo Markdown, transforma em HTML com react-markdown, e o exibe na tela.

Como Funciona um Artigo?

Esta é a parte mais interessante! Aqui está o ciclo de vida de um artigo:

1. O Arquivo index.json

Todos os artigos são referenciados em public/articles/index.json. Cada entrada contém os metadados do artigo:

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug -- o identificador único, usado na URL (/blog/hello-world)
  • title -- o título exibido na lista
  • description -- um breve resumo
  • date -- a data de publicação

2. O Arquivo Markdown

O conteúdo do artigo é um simples arquivo .md em public/articles/. O nome do arquivo corresponde ao slug definido em index.json.

Você pode colocar o que quiser: títulos, listas, imagens, tabelas, e até HTML bruto graças ao rehype-raw!

3. A Renderização no React

Quando você visita /blog/hello-world, aqui está o que acontece:

  1. React Router captura o parâmetro slug da URL
  2. O componente Article.tsx carrega /articles/hello-world.md
  3. O Markdown é transformado em HTML pelo react-markdown
  4. Os links para /articles/assets/ são automaticamente reescritos para /articles//articles/assets/
  5. Em paralelo, os metadados são carregados do index.json para exibir a data e a descrição

É simples assim!

A Página Inicial e o Portfólio

As páginas Inicial e Portfólio funcionam exatamente da mesma forma: elas carregam um arquivo Markdown (home.md ou portfolio.md) e o renderizam em HTML.

A particularidade é que elas usam um esquema de sanitização personalizado que permite os atributos class e style em todos os elementos HTML. Isso me permite escrever HTML estilizado diretamente no Markdown, como galerias de imagens, por exemplo.

O Header está fixo no topo da página com position: fixed. Ele contém:

  • Meu avatar do GitHub (carregado diretamente de github.com/fox3000foxy.png)
  • O título do blog
  • Os links de navegação: Início, Blog, Portfólio

O Footer é minimalista: apenas um copyright com o ano atual calculado dinamicamente.

O Tema Escuro

O site está sempre no modo escuro -- sem alternância dia/noite. É uma escolha deliberada: color-scheme: dark está definido nos estilos globais, com fundo preto #000 e texto branco #fff. Os links são azuis (#64b5f6) e ficam verdes ao passar o mouse (#81c784).

Como Eu Escrevo um Artigo

Vamos à prática! Aqui está meu fluxo de trabalho para escrever um novo artigo:

Etapa 1: Criar o Arquivo Markdown

Abro o VS Code e crio um novo arquivo .md em public/articles/:

Etapa 2: Escrever o Conteúdo

Escrevo o conteúdo do artigo diretamente em Markdown. O VS Code tem uma excelente pré-visualização de Markdown integrada:

Para as imagens, coloco-as em public/articles//articles/assets/ e as referencio com a sintaxe Markdown padrão:

![descrição](/articles/assets/my-image.png)

O componente Article.tsx reescreve automaticamente o caminho /articles/assets/ para /articles//articles/assets/ para que as imagens sejam exibidas corretamente.

Etapa 3: Registrar o Artigo no index.json

Assim que o artigo estiver pronto, eu o adiciono em public/articles/index.json para que apareça na lista do blog:

Etapa 4: Testar Localmente

Inicio o servidor de desenvolvimento Vite:

pnpm dev

O Vite inicia em alguns milissegundos e eu posso ver meu artigo em tempo real em localhost:5173:

Etapa 5: Publicar

Um simples git push é suficiente! O pipeline CI/CD cuida do resto automaticamente.

O Pipeline de Deploy CI/CD

Montei um pipeline GitHub Actions completo que automatiza o lint, o build e o deploy do site a cada push na main. Vamos ver isso em detalhes.

O workflow está em .github/workflows/deploy.yml e é dividido em dois jobs: build e deploy.

Gatilhos

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

O pipeline é executado a cada push na main e a cada pull request visando main. As PRs são verificadas (lint + build) antes de serem mescladas, mas apenas pushes na main disparam um deploy.

Job 1: Build

O job de build roda em ubuntu-latest e segue estas etapas:

  1. Checkout -- Clona o repositório com todo o histórico (fetch-depth: 0)
  2. Setup pnpm -- Instala a versão mais recente do pnpm com pnpm/action-setup@v4
  3. Setup Node.js 20 -- Configura o Node com cache pnpm ativado para instalações mais rápidas
  4. Install dependencies -- Executa pnpm install --frozen-lockfile para garantir builds reproduzíveis (nenhuma modificação do lockfile é permitida)
  5. Lint -- Executa pnpm run lint (ESLint) para verificar a qualidade do código antes do build
  6. Build -- Executa pnpm run build, que primeiro verifica os tipos TypeScript (tsc -b) e então empacota tudo com Vite
  7. Upload artifact -- Envia a pasta dist/ como artefato de build para o job de deploy

Se alguma etapa falhar -- um erro de lint, tipo ou build -- todo o pipeline para e nada é implantado. Isso protege o site em produção contra código quebrado.

Job 2: Deploy

O job de deploy só é executado se:

  • O job de build tiver sucesso (needs: build)
  • O evento for um push (não uma PR)
  • A branch for main
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

Ele então prossegue:

  1. Baixa o artefato de build -- Recupera a pasta dist/ produzida pelo job de build
  2. Configura GitHub Pages -- Prepara o ambiente Pages
  3. Envia para Pages -- Prepara a pasta dist/ para o GitHub Pages
  4. Deploy -- Publica o site com actions/deploy-pages@v4

O Fluxo Completo

Aqui está o que acontece desde a escrita até o deploy:

Escrever o artigo no VS Code
         ↓
   git add & commit
         ↓
      git push
         ↓
  GitHub Actions é acionado
         ↓
  ┌─────────────────┐
  │   BUILD JOB     │
  │  1. Checkout    │
  │  2. Setup pnpm  │
  │  3. Setup Node  │
  │  4. Install     │
  │  5. Lint ✓      │
  │  6. Build ✓     │
  │  7. Upload dist │
  └────────┬────────┘
           ↓
  ┌─────────────────┐
  │  DEPLOY JOB     │
  │  1. Download    │
  │  2. Configure   │
  │  3. Upload      │
  │  4. Deploy 🚀   │
  └─────────────────┘
           ↓
    Online no GitHub Pages!

O processo inteiro leva cerca de um minuto entre o push e a publicação. Sem deploy manual, sem FTP, sem SSH -- apenas git push e pronto.

O Build de Produção

Por baixo dos panos, o comando pnpm build executa:

  1. tsc -b -- Verifica os tipos TypeScript
  2. vite build -- Empacota e otimiza todo o código

Vite produz arquivos minificados e otimizados com code-splitting automático. O resultado é um site estático ultra-rápido.

Por Que Esta Arquitetura?

Eu poderia ter usado um CMS, um gerador de site estático como Hugo ou Jekyll, ou até Next.js. Mas aqui está por que escolhi esta abordagem:

  • Simplicidade -- Escreva em Markdown, faça push no GitHub, está online
  • Controle total -- Sem dependência de CMS ou banco de dados
  • Performance -- Vite + React = carregamento rápido
  • Flexibilidade -- Posso misturar Markdown e HTML como quiser
  • Aprendizado -- É um ótimo projeto para dominar React e TypeScript
  • CI/CD -- Verificações de qualidade e deploy automatizados com GitHub Actions

Conclusão

Este blog é um projeto simples, mas bem pensado: Markdown para o conteúdo, React para a renderização, Vite para performance, GitHub Actions para CI/CD, e GitHub Pages para hospedagem. Sem banco de dados, sem servidor backend, apenas arquivos estáticos servidos eficientemente com um pipeline automatizado que garante a qualidade a cada push.

Obrigado por ler, e até o próximo artigo! 🦊

✨ AI Generated Article

Bagaimana Cara Kerja Blog Ini?

Di balik layar blog: React, Vite, Markdown, pipeline CI/CD dan alur penulisan.

Bagaimana Cara Kerja Blog Ini?

Kamu pernah bertanya-tanya bagaimana blog ini bekerja di balik layar? Di artikel ini, aku akan menjelaskan seluruh arsitektur aplikasi, dari stack teknis hingga proses penulisan artikel. Dan ya, aku bahkan akan menunjukkan cara aku menulis artikel dari VS Code!

Stack Teknis

Blog ini dibangun dengan teknologi web modern:

  • React 19 -- untuk antarmuka pengguna
  • TypeScript -- untuk kode yang tertipe dan lebih andal
  • Vite -- sebagai alat build yang sangat cepat
  • React Router v7 -- untuk navigasi antar halaman
  • react-markdown -- untuk mengubah Markdown menjadi HTML
  • rehype-raw + rehype-sanitize -- untuk mengizinkan HTML mentah dalam Markdown dengan aman

Struktur Proyek

Berikut adalah struktur direktori proyek:

├── .github/
│   └── workflows/
│       └── deploy.yml              ← Pipeline CI/CD
├── public/
│   ├── home.md                     ← Konten halaman utama
│   ├── portfolio.md                ← Konten portofolio
│   └── articles/
│       ├── index.json              ← Daftar semua artikel
│       ├── hello-world.md          ← Sebuah artikel
│       ├── how-this-blog-works.md  ← Artikel ini!
│       └── /articles/assets/                 ← Gambar artikel
├── src/
│   ├── main.tsx                    ← Entry point React
│   ├── App.tsx                     ← Router utama
│   ├── components/
│   │   ├── Header.tsx              ← Bilah navigasi
│   │   └── Footer.tsx              ← Catatan kaki
│   └── pages/
│       ├── Home.tsx                ← Halaman utama
│       ├── BlogList.tsx            ← Daftar artikel
│       ├── Article.tsx             ← Pembaca artikel
│       ├── Portfolio.tsx           ← Halaman portofolio
│       └── NotFound.tsx            ← Halaman 404
└── vite.config.ts                  ← Konfigurasi Vite

Ide utamanya sederhana: konten dipisahkan dari kode. Halaman ditulis dalam Markdown di folder public/, dan kode React di src/ bertugas menampilkannya.

Sistem Routing

File App.tsx mendefinisikan semua rute aplikasi dengan React Router:

Rute Halaman Deskripsi
/ Home Halaman utama, memuat home.md
/blog BlogList Daftar semua artikel
/blog/:slug Article Sebuah artikel, memuat articles/{slug}.md
/portfolio Portfolio Halaman portofolio, memuat portfolio.md
* NotFound Halaman 404 untuk URL yang tidak dikenal

Setiap halaman memiliki peran yang jelas: mengambil file Markdown, mengubahnya menjadi HTML dengan react-markdown, dan menampilkannya di layar.

Bagaimana Cara Kerja Sebuah Artikel?

Ini bagian yang paling menarik! Berikut siklus hidup sebuah artikel:

1. File index.json

Semua artikel direferensikan di public/articles/index.json. Setiap entri berisi metadata artikel:

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug -- pengidentifikasi unik, digunakan di URL (/blog/hello-world)
  • title -- judul yang ditampilkan di daftar
  • description -- ringkasan singkat
  • date -- tanggal publikasi

2. File Markdown

Konten artikel adalah file .md biasa di public/articles/. Nama file sesuai dengan slug yang ditentukan di index.json.

Kamu bisa menaruh apa pun yang kamu mau: judul, daftar, gambar, tabel, dan bahkan HTML mentah berkat rehype-raw!

3. Render di Sisi React

Saat kamu mengunjungi /blog/hello-world, inilah yang terjadi:

  1. React Router mengambil parameter slug dari URL
  2. Komponen Article.tsx memuat /articles/hello-world.md
  3. Markdown diubah menjadi HTML oleh react-markdown
  4. Tautan ke /articles/assets/ secara otomatis ditulis ulang ke /articles//articles/assets/
  5. Secara paralel, metadata dimuat dari index.json untuk menampilkan tanggal dan deskripsi

Sesederhana itu!

Halaman Utama dan Portofolio

Halaman Beranda dan Portofolio bekerja persis dengan cara yang sama: mereka memuat file Markdown (home.md atau portfolio.md) dan merendernya menjadi HTML.

Keunikannya, mereka menggunakan skema sanitasi kustom yang mengizinkan atribut class dan style pada semua elemen HTML. Ini memungkinkanku menulis HTML bergaya langsung di Markdown, seperti galeri gambar misalnya.

Header disematkan di bagian atas halaman dengan position: fixed. Isinya:

  • Avatar GitHub-ku (dimuat langsung dari github.com/fox3000foxy.png)
  • Judul blog
  • Tautan navigasi: Beranda, Blog, Portofolio

Footer minimalis: hanya hak cipta dengan tahun berjalan yang dihitung secara dinamis.

Tema Gelap

Situs ini selalu dalam mode gelap -- tanpa toggle siang/malam. Ini adalah pilihan yang disengaja: color-scheme: dark ditentukan di gaya global, dengan latar belakang hitam #000 dan teks putih #fff. Tautan berwarna biru (#64b5f6) dan berubah menjadi hijau saat dihover (#81c784).

Bagaimana Aku Menulis Artikel

Mari kita praktik! Berikut alur kerjaku untuk menulis artikel baru:

Langkah 1: Membuat File Markdown

Aku membuka VS Code dan membuat file .md baru di public/articles/:

Langkah 2: Menulis Konten

Aku menulis konten artikel langsung dalam Markdown. VS Code memiliki pratinjau Markdown yang sangat baik:

Untuk gambar, aku meletakkannya di public/articles//articles/assets/ dan mereferensikannya dengan sintaks Markdown standar:

![description](/articles/assets/my-image.png)

Komponen Article.tsx secara otomatis menulis ulang jalur /articles/assets/ ke /articles//articles/assets/ agar gambar ditampilkan dengan benar.

Langkah 3: Mendaftarkan Artikel di index.json

Setelah artikel selesai, aku menambahkannya ke public/articles/index.json agar muncul di daftar blog:

Langkah 4: Uji Coba Lokal

Aku menjalankan server pengembangan Vite:

pnpm dev

Vite mulai dalam hitungan milidetik dan aku bisa melihat artikel secara real-time di localhost:5173:

Langkah 5: Publikasi

Cukup git push! Pipeline CI/CD menangani sisanya secara otomatis.

Pipeline Deployment CI/CD

Aku menyiapkan pipeline GitHub Actions lengkap yang mengotomatiskan lint, build, dan deployment situs setiap kali push ke main. Mari kita lihat secara detail.

Workflow-nya ada di .github/workflows/deploy.yml dan dibagi menjadi dua job: build dan deploy.

Pemicu

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

Pipeline berjalan setiap push ke main dan setiap pull request yang menuju main. Jadi PR diperiksa (lint + build) sebelum digabung, tetapi hanya push ke main yang memicu deployment.

Job 1: Build

Job build berjalan di ubuntu-latest dan mengikuti langkah-langkah berikut:

  1. Checkout -- Meng-clone repositori dengan seluruh riwayat (fetch-depth: 0)
  2. Setup pnpm -- Menginstal versi terbaru pnpm dengan pnpm/action-setup@v4
  3. Setup Node.js 20 -- Mengonfigurasi Node dengan cache pnpm yang diaktifkan untuk instalasi yang lebih cepat
  4. Install dependencies -- Menjalankan pnpm install --frozen-lockfile untuk menjamin build yang reprodusibel (tidak ada modifikasi lockfile yang diizinkan)
  5. Lint -- Menjalankan pnpm run lint (ESLint) untuk memeriksa kualitas kode sebelum build
  6. Build -- Menjalankan pnpm run build, yang pertama-tama memeriksa tipe TypeScript (tsc -b) lalu membundle semuanya dengan Vite
  7. Upload artifact -- Mengunggah folder dist/ sebagai artefak build untuk job deployment

Jika ada langkah yang gagal -- kesalahan lint, tipe, atau build -- seluruh pipeline berhenti dan tidak ada yang di-deploy. Ini melindungi situs produksi dari kode yang rusak.

Job 2: Deploy

Job deployment hanya berjalan jika:

  • Job build berhasil (needs: build)
  • Event-nya adalah push (bukan PR)
  • Branch-nya adalah main
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

Kemudian melanjutkan:

  1. Mengunduh artefak build -- Mengambil folder dist/ yang dihasilkan oleh job build
  2. Mengonfigurasi GitHub Pages -- Menyiapkan environment Pages
  3. Mengunggah ke Pages -- Menyiapkan folder dist/ untuk GitHub Pages
  4. Deploy -- Mempublikasikan situs dengan actions/deploy-pages@v4

Tabel Lengkap

Berikut yang terjadi dari penulisan hingga deployment:

Menulis artikel di VS Code
         ↓
   git add & commit
         ↓
      git push
         ↓
  GitHub Actions terpicu
         ↓
  ┌─────────────────┐
  │   BUILD JOB     │
  │  1. Checkout    │
  │  2. Setup pnpm  │
  │  3. Setup Node  │
  │  4. Install     │
  │  5. Lint ✓      │
  │  6. Build ✓     │
  │  7. Upload dist │
  └────────┬────────┘
           ↓
  ┌─────────────────┐
  │  DEPLOY JOB     │
  │  1. Download    │
  │  2. Configure   │
  │  3. Upload      │
  │  4. Deploy 🚀   │
  └─────────────────┘
           ↓
    Live di GitHub Pages!

Seluruh proses memakan waktu sekitar satu menit antara push dan go-live. Tanpa deployment manual, tanpa FTP, tanpa SSH -- cukup git push dan selesai.

Build Produksi

Di balik layar, perintah pnpm build menjalankan:

  1. tsc -b -- Memeriksa tipe TypeScript
  2. vite build -- Membundle dan mengoptimalkan semua kode

Vite menghasilkan file yang diminifikasi dan dioptimalkan dengan pemisahan kode otomatis. Hasilnya adalah situs statis yang sangat cepat.

Mengapa Arsitektur Ini?

Aku bisa saja menggunakan CMS, generator situs statis seperti Hugo atau Jekyll, atau bahkan Next.js. Tapi inilah alasan aku memilih pendekatan ini:

  • Kesederhanaan -- Tulis di Markdown, push ke GitHub, langsung online
  • Kontrol penuh -- Tanpa ketergantungan pada CMS atau database
  • Kinerja -- Vite + React = pemuatan cepat
  • Fleksibilitas -- Aku bisa mencampur Markdown dan HTML sesuka hati
  • Pembelajaran -- Proyek yang bagus untuk menguasai React dan TypeScript
  • CI/CD -- Pemeriksaan kualitas dan deployment otomatis dengan GitHub Actions

Kesimpulan

Blog ini adalah proyek yang sederhana namun dirancang dengan baik: Markdown untuk konten, React untuk rendering, Vite untuk kinerja, GitHub Actions untuk CI/CD, dan GitHub Pages untuk hosting. Tanpa database, tanpa server backend, hanya file statis yang dilayani secara efisien dengan pipeline otomatis yang menjamin kualitas setiap push.

Terima kasih telah membaca, sampai jumpa di artikel berikutnya! 🦊

✨ AI Generated Article

यह ब्लॉग कैसे काम करता है?

ब्लॉग के पर्दे के पीछे: React, Vite, Markdown, CI/CD पाइपलाइन और लेखन प्रक्रिया।

यह ब्लॉग कैसे काम करता है?

क्या आपने कभी सोचा है कि यह ब्लॉग अंदर से कैसे काम करता है? इस लेख में, मैं आपको एप्लिकेशन की पूरी आर्किटेक्चर के बारे में बताऊंगा, टेक्निकल स्टैक से लेकर लेख लिखने की प्रक्रिया तक। और हाँ, मैं आपको यह भी दिखाऊंगा कि मैं VS Code से अपने लेख कैसे लिखता हूँ!

टेक्निकल स्टैक

यह ब्लॉग आधुनिक वेब तकनीकों से बनाया गया है:

  • React 19 -- यूज़र इंटरफ़ेस के लिए
  • TypeScript -- टाइप किए गए और अधिक विश्वसनीय कोड के लिए
  • Vite -- अल्ट्रा-फ़ास्ट बिल्ड टूल के रूप में
  • React Router v7 -- पेजों के बीच नेविगेशन के लिए
  • react-markdown -- Markdown को HTML में बदलने के लिए
  • rehype-raw + rehype-sanitize -- Markdown में सुरक्षित रूप से रॉ HTML की अनुमति देने के लिए

प्रोजेक्ट स्ट्रक्चर

प्रोजेक्ट की डायरेक्टरी संरचना इस प्रकार है:

├── .github/
│   └── workflows/
│       └── deploy.yml              ← CI/CD पाइपलाइन
├── public/
│   ├── home.md                     ← होम पेज की सामग्री
│   ├── portfolio.md                ← पोर्टफोलियो की सामग्री
│   └── articles/
│       ├── index.json              ← सभी लेखों की सूची
│       ├── hello-world.md          ← एक लेख
│       ├── how-this-blog-works.md  ← यह लेख!
│       └── /articles/assets/                 ← लेखों की छवियाँ
├── src/
│   ├── main.tsx                    ← React एंट्री पॉइंट
│   ├── App.tsx                     ← मुख्य राउटर
│   ├── components/
│   │   ├── Header.tsx              ← नेविगेशन बार
│   │   └── Footer.tsx              ← पाद लेख
│   └── pages/
│       ├── Home.tsx                ← होम पेज
│       ├── BlogList.tsx            ← लेखों की सूची
│       ├── Article.tsx             ← लेख रीडर
│       ├── Portfolio.tsx           ← पोर्टफोलियो पेज
│       └── NotFound.tsx            ← 404 पेज
└── vite.config.ts                  ← Vite कॉन्फ़िगरेशन

मुख्य विचार सरल है: सामग्री को कोड से अलग रखा गया है। पेज public/ फ़ोल्डर में Markdown में लिखे गए हैं, और src/ में React कोड उन्हें प्रदर्शित करने का काम करता है।

रूटिंग सिस्टम

App.tsx फ़ाइल React Router के साथ एप्लिकेशन के सभी रूट्स को परिभाषित करती है:

Route Page Description
/ Home होम पेज, home.md लोड करता है
/blog BlogList सभी लेखों की सूची
/blog/:slug Article एक लेख, articles/{slug}.md लोड करता है
/portfolio Portfolio पोर्टफोलियो पेज, portfolio.md लोड करता है
* NotFound अज्ञात URLs के लिए 404 पेज

प्रत्येक पेज की एक स्पष्ट भूमिका होती है: वह एक Markdown फ़ाइल लाता है, react-markdown से उसे HTML में बदलता है, और स्क्रीन पर प्रदर्शित करता है।

एक लेख कैसे काम करता है?

यह सबसे दिलचस्प हिस्सा है! यहाँ एक लेख का जीवनचक्र है:

1. index.json फ़ाइल

सभी लेख public/articles/index.json में संदर्भित हैं। प्रत्येक प्रविष्टि में लेख के मेटाडेटा होते हैं:

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug -- अद्वितीय पहचानकर्ता, URL में उपयोग किया जाता है (/blog/hello-world)
  • title -- सूची में दिखाया गया शीर्षक
  • description -- एक संक्षिप्त सारांश
  • date -- प्रकाशन तिथि

2. Markdown फ़ाइल

लेख की सामग्री public/articles/ में एक साधारण .md फ़ाइल है। फ़ाइल का नाम index.json में परिभाषित slug से मेल खाता है।

आप इसमें जो चाहे डाल सकते हैं: शीर्षक, सूचियाँ, चित्र, तालिकाएँ, और यहाँ तक कि rehype-raw की बदौलत रॉ HTML भी!

3. React साइड पर रेंडरिंग

जब आप /blog/hello-world पर जाते हैं, तो यह होता है:

  1. React Router URL से slug पैरामीटर प्राप्त करता है
  2. Article.tsx कम्पोनेंट /articles/hello-world.md लोड करता है
  3. Markdown को react-markdown द्वारा HTML में बदला जाता है
  4. /articles/assets/ के लिंक स्वचालित रूप से /articles//articles/assets/ पर रीराइट हो जाते हैं
  5. समानांतर में, तिथि और विवरण प्रदर्शित करने के लिए index.json से मेटाडेटा लोड किया जाता है

इतना ही सरल है!

होम पेज और पोर्टफोलियो

होम और पोर्टफोलियो पेज बिल्कुल उसी तरह काम करते हैं: वे एक Markdown फ़ाइल (home.md या portfolio.md) लोड करते हैं और उसे HTML में रेंडर करते हैं।

खास बात यह है कि वे एक कस्टम सैनिटाइज़ेशन स्कीम का उपयोग करते हैं जो सभी HTML तत्वों पर class और style विशेषताओं की अनुमति देता है। इससे मैं Markdown में सीधे स्टाइल किया हुआ HTML लिख सकता हूँ, जैसे इमेज गैलरी।

हेडर और फ़ुटर

हेडर position: fixed के साथ पेज के ऊपर पिन किया गया है। इसमें शामिल है:

  • मेरा GitHub अवतार (सीधे github.com/fox3000foxy.png से लोड)
  • ब्लॉग का शीर्षक
  • नेविगेशन लिंक: होम, ब्लॉग, पोर्टफोलियो

फ़ुटर न्यूनतम है: बस डायनामिक रूप से गणना किए गए वर्तमान वर्ष के साथ एक कॉपीराइट।

डार्क थीम

साइट हमेशा डार्क मोड में है -- कोई दिन/रात टॉगल नहीं। यह एक जानबूझकर किया गया विकल्प है: ग्लोबल स्टाइल में color-scheme: dark सेट है, जिसमें काला बैकग्राउंड #000 और सफेद टेक्स्ट #fff है। लिंक नीले (#64b5f6) हैं और होवर पर हरे (#81c784) हो जाते हैं।

मैं एक लेख कैसे लिखता हूँ

अब व्यावहारिक हिस्से पर आते हैं! यहाँ एक नया लेख लिखने का मेरा वर्कफ़्लो है:

चरण 1: Markdown फ़ाइल बनाएँ

मैं VS Code खोलता हूँ और public/articles/ में एक नई .md फ़ाइल बनाता हूँ:

चरण 2: सामग्री लिखें

मैं लेख की सामग्री सीधे Markdown में लिखता हूँ। VS Code में एक उत्कृष्ट बिल्ट-इन Markdown प्रीव्यू है:

चित्रों के लिए, मैं उन्हें public/articles//articles/assets/ में रखता हूँ और मानक Markdown सिंटैक्स से संदर्भित करता हूँ:

![description](/articles/assets/my-image.png)

Article.tsx कम्पोनेंट स्वचालित रूप से /articles/assets/ पथ को /articles//articles/assets/ पर रीराइट करता है ताकि चित्र सही ढंग से प्रदर्शित हों।

चरण 3: index.json में लेख जोड़ें

लेख पूरा होने के बाद, मैं इसे public/articles/index.json में जोड़ता हूँ ताकि यह ब्लॉग सूची में दिखाई दे:

चरण 4: लोकल पर परीक्षण करें

मैं Vite डेवलपमेंट सर्वर चलाता हूँ:

pnpm dev

Vite मिलीसेकंड में शुरू होता है और मैं localhost:5173 पर अपना लेख रीयल-टाइम में देख सकता हूँ:

चरण 5: प्रकाशित करें

बस एक git push काफी है! CI/CD पाइपलाइन बाकी काम अपने आप कर लेती है।

CI/CD डिप्लॉयमेंट पाइपलाइन

मैंने एक पूर्ण GitHub Actions पाइपलाइन सेट अप की है जो main पर प्रत्येक push पर साइट के लिंट, बिल्ड और डिप्लॉयमेंट को स्वचालित करती है। आइए इसे विस्तार से देखें।

यह वर्कफ़्लो .github/workflows/deploy.yml में स्थित है और दो जॉब्स में विभाजित है: build और deploy।

ट्रिगर

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

पाइपलाइन main पर प्रत्येक push और main को लक्षित करने वाले प्रत्येक pull request पर चलती है। PRs को मर्ज करने से पहले जाँचा जाता है (लिंट + बिल्ड), लेकिन केवल main पर push डिप्लॉयमेंट को ट्रिगर करते हैं।

जॉब 1: Build

Build जॉब ubuntu-latest पर चलती है और इन चरणों का पालन करती है:

  1. Checkout -- पूरे इतिहास के साथ रिपॉज़िटरी को क्लोन करता है (fetch-depth: 0)
  2. Setup pnpm -- pnpm/action-setup@v4 के साथ pnpm का नवीनतम संस्करण स्थापित करता है
  3. Setup Node.js 20 -- तेज़ इंस्टॉलेशन के लिए pnpm कैश सक्षम के साथ Node सेट करता है
  4. Install dependencies -- पुनरुत्पादनीय बिल्ड सुनिश्चित करने के लिए pnpm install --frozen-lockfile चलाता है (लॉकफ़ाइल में कोई बदलाव की अनुमति नहीं)
  5. Lint -- बिल्ड से पहले कोड गुणवत्ता जाँचने के लिए pnpm run lint (ESLint) चलाता है
  6. Build -- pnpm run build चलाता है, जो पहले TypeScript प्रकार जाँचता है (tsc -b) फिर Vite के साथ सब कुछ बंडल करता है
  7. Upload artifact -- dist/ फ़ोल्डर को डिप्लॉय जॉब के लिए बिल्ड आर्टिफ़ैक्ट के रूप में अपलोड करता है

यदि कोई भी चरण विफल होता है -- लिंट त्रुटि, टाइप त्रुटि, या बिल्ड त्रुटि -- तो पूरी पाइपलाइन रुक जाती है और कुछ भी डिप्लॉय नहीं होता। यह प्रोडक्शन साइट को टूटे हुए कोड से बचाता है।

जॉब 2: Deploy

डिप्लॉय जॉब केवल तभी चलती है जब:

  • Build जॉब सफल हुई हो (needs: build)
  • ईवेंट push हो (PR नहीं)
  • ब्रांच main हो
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

फिर यह आगे बढ़ता है:

  1. बिल्ड आर्टिफ़ैक्ट डाउनलोड करता है -- build जॉब द्वारा उत्पादित dist/ फ़ोल्डर प्राप्त करता है
  2. GitHub Pages कॉन्फ़िगर करता है -- Pages वातावरण सेट करता है
  3. Pages पर अपलोड करता है -- GitHub Pages के लिए dist/ फ़ोल्डर तैयार करता है
  4. डिप्लॉय करता है -- actions/deploy-pages@v4 के साथ साइट प्रकाशित करता है

पूरी तालिका

यहाँ लेखन से डिप्लॉयमेंट तक क्या होता है:

लेख को VS Code में लिखें
         ↓
   git add & commit
         ↓
      git push
         ↓
 GitHub Actions शुरू होता है
         ↓
 ┌─────────────────┐
 │   BUILD JOB     │
 │  1. Checkout    │
 │  2. Setup pnpm  │
 │  3. Setup Node  │
 │  4. Install     │
 │  5. Lint ✓      │
 │  6. Build ✓     │
 │  7. Upload dist │
 └────────┬────────┘
          ↓
 ┌─────────────────┐
 │  DEPLOY JOB     │
 │  1. Download    │
 │  2. Configure   │
 │  3. Upload      │
 │  4. Deploy 🚀   │
 └─────────────────┘
          ↓
   GitHub Pages पर लाइव!

पूरी प्रक्रिया में push से लाइव होने तक लगभग एक मिनट लगता है। कोई मैन्युअल डिप्लॉयमेंट नहीं, कोई FTP नहीं, कोई SSH नहीं -- बस git push और यह हो गया।

प्रोडक्शन बिल्ड

परदे के पीछे, pnpm build कमांड निष्पादित करता है:

  1. tsc -b -- TypeScript प्रकार जाँचता है
  2. vite build -- सभी कोड को बंडल और ऑप्टिमाइज़ करता है

Vite स्वचालित कोड-स्प्लिटिंग के साथ मिनिफ़ाइड और ऑप्टिमाइज़्ड फ़ाइलें उत्पन्न करता है। परिणाम एक अल्ट्रा-फ़ास्ट स्टैटिक साइट है।

यह आर्किटेक्चर क्यों?

मैं CMS, Hugo या Jekyll जैसे स्टैटिक साइट जनरेटर, या यहाँ तक कि Next.js का उपयोग कर सकता था। लेकिन मैंने यह दृष्टिकोण क्यों चुना:

  • सरलता -- Markdown में लिखें, GitHub पर push करें, यह लाइव हो जाता है
  • पूर्ण नियंत्रण -- किसी CMS या डेटाबेस पर कोई निर्भरता नहीं
  • प्रदर्शन -- Vite + React = तेज़ लोडिंग
  • लचीलापन -- मैं Markdown और HTML को अपनी इच्छानुसार मिक्स कर सकता हूँ
  • सीखना -- React और TypeScript में महारत हासिल करने के लिए यह एक शानदार प्रोजेक्ट है
  • CI/CD -- GitHub Actions के साथ गुणवत्ता जाँच और स्वचालित डिप्लॉयमेंट

निष्कर्ष

यह ब्लॉग एक सरल लेकिन सुविचारित प्रोजेक्ट है: सामग्री के लिए Markdown, रेंडरिंग के लिए React, प्रदर्शन के लिए Vite, CI/CD के लिए GitHub Actions, और होस्टिंग के लिए GitHub Pages। कोई डेटाबेस नहीं, कोई बैकएंड सर्वर नहीं, बस कुशलता से परोसी गई स्टैटिक फ़ाइलें और एक स्वचालित पाइपलाइन जो हर push पर गुणवत्ता सुनिश्चित करती है।

पढ़ने के लिए धन्यवाद, और अगले लेख में मिलते हैं! 🦊

✨ AI Generated Article

كيف يعمل هذه المدونة؟

وراء كواليس المدونة: React، Vite، Markdown، خط أنابيب CI/CD

كيف يعمل هذه المدونة؟

هل تساءلت يوماً كيف تعمل هذه المدونة تحت الغطاء؟ في هذا المقال، سأشرح لك بالتفصيل بنية التطبيق بالكامل، بدءاً من التقنيات المستخدمة وصولاً إلى عملية كتابة مقال. ونعم، سأريك أيضاً كيف أكتب مقالاتي من VS Code!

التقنيات المستخدمة

هذه المدونة مبنية باستخدام تقنيات ويب حديثة:

  • React 19 -- لواجهة المستخدم
  • TypeScript -- لكود منسق وأكثر موثوقية
  • Vite -- كأداة بناء فائقة السرعة
  • React Router v7 -- للتنقل بين الصفحات
  • react-markdown -- لتحويل Markdown إلى HTML
  • rehype-raw + rehype-sanitize -- للسماح بـ HTML الخام داخل Markdown بأمان

هيكل المشروع

إليك شجرة المشروع:

├── .github/
│   └── workflows/
│       └── deploy.yml              ← خط أنابيب CI/CD
├── public/
│   ├── home.md                     ← محتوى الصفحة الرئيسية
│   ├── portfolio.md                ← محتوى صفحة الأعمال
│   └── articles/
│       ├── index.json              ← قائمة بجميع المقالات
│       ├── hello-world.md          ← مقال
│       ├── how-this-blog-works.md  ← هذا المقال!
│       └── /articles/assets/                 ← صور المقالات
├── src/
│   ├── main.tsx                    ← نقطة الدخول React
│   ├── App.tsx                     ← الموجه الرئيسي
│   ├── components/
│   │   ├── Header.tsx              ← شريط التنقل
│   │   └── Footer.tsx              ← تذييل الصفحة
│   └── pages/
│       ├── Home.tsx                ← الصفحة الرئيسية
│       ├── BlogList.tsx            ← قائمة المقالات
│       ├── Article.tsx             ← قارئ المقالات
│       ├── Portfolio.tsx           ← صفحة الأعمال
│       └── NotFound.tsx            ← صفحة 404
└── vite.config.ts                  ← إعدادات Vite

الفكرة الأساسية بسيطة: المحتوى منفصل عن الكود. الصفحات مكتوبة بـ Markdown في مجلد public/، وكود React في src/ يقوم بعرضها.

نظام التوجيه

الملف App.tsx يحدد جميع مسارات التطبيق باستخدام React Router:

المسار الصفحة الوصف
/ Home الصفحة الرئيسية، تحميل home.md
/blog BlogList قائمة بجميع المقالات
/blog/:slug Article مقال، تحميل articles/{slug}.md
/portfolio Portfolio صفحة الأعمال، تحميل portfolio.md
* NotFound صفحة 404 للعناوين غير المعروفة

كل صفحة لها دور محدد: تجلب ملف Markdown، تحوله إلى HTML باستخدام react-markdown، وتعرضه على الشاشة.

كيف يعمل المقال؟

هذا هو الجزء الأكثر إثارة للاهتمام! إليك دورة حياة المقال:

1. ملف index.json

جميع المقالات مُشار إليها في public/articles/index.json. كل مدخل يحتوي على بيانات المقال الوصفية:

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug -- المعرف الفريد، يُستخدم في الرابط (/blog/hello-world)
  • title -- العنوان المعروض في القائمة
  • description -- ملخص قصير
  • date -- تاريخ النشر

2. ملف Markdown

محتوى المقال هو مجرد ملف .md في public/articles/. اسم الملف يطابق slug المعرف في index.json.

يمكنك وضع ما تريد فيه: عناوين، قوائم، صور، جداول، وحتى HTML خام بفضل rehype-raw!

3. العرض عبر React

عندما تزور /blog/hello-world، إليك ما يحدث:

  1. React Router يستخرج معامل slug من الرابط
  2. المكون Article.tsx يحمل /articles/hello-world.md
  3. يتم تحويل Markdown إلى HTML بواسطة react-markdown
  4. الروابط إلى /articles/assets/ تُعاد كتابتها تلقائياً إلى /articles//articles/assets/
  5. بالتوازي، يتم تحميل البيانات الوصفية من index.json لعرض التاريخ والوصف

الأمر بهذه البساطة!

الصفحة الرئيسية وصفحة الأعمال

صفحتا الرئيسية والأعمال تعملان بنفس الطريقة تماماً: تحملان ملف Markdown (home.md أو portfolio.md) وتحولانه إلى HTML.

الخصوصية هي أنهما تستخدمان مخطط تنظيف مخصص يسمح بالخاصيتين class و style على جميع عناصر HTML. هذا يسمح لي بكتابة HTML منسق مباشرة في Markdown، مثل معارض الصور على سبيل المثال.

الرأس والتذييل

الرأس مثبت في أعلى الصفحة باستخدام position: fixed. يحتوي على:

  • صورتي الرمزية على GitHub (تُحمّل مباشرة من github.com/fox3000foxy.png)
  • عنوان المدونة
  • روابط التنقل: الرئيسية، المدونة، الأعمال

التذييل بسيط جداً: مجرد حقوق نشر مع السنة الحالية محسوبة ديناميكياً.

الوضع الداكن

الموقع دائماً في الوضع الداكن -- لا يوجد تبديل بين النهار والليل. هذا اختيار متعمد: color-scheme: dark محدد في الأنماط العامة، مع خلفية سوداء #000 ونص أبيض #fff. الروابط زرقاء (#64b5f6) وتصبح خضراء عند التمرير (#81c784).

كيف أكتب مقالاً

لننتقل إلى الجانب العملي! إليك سير عملي لكتابة مقال جديد:

الخطوة 1: إنشاء ملف Markdown

أفتح VS Code وأنشئ ملف .md جديد في public/articles/:

الخطوة 2: كتابة المحتوى

أكتب محتوى المقال مباشرة بـ Markdown. VS Code لديه معاينة Markdown ممتازة مدمجة:

للصور، أضعها في public/articles//articles/assets/ وأشير إليها باستخدام صيغة Markdown القياسية:

![description](/articles/assets/my-image.png)

المكون Article.tsx يعيد كتابة المسار /articles/assets/ تلقائياً إلى /articles//articles/assets/ لتظهر الصور بشكل صحيح.

الخطوة 3: تسجيل المقال في index.json

بمجرد الانتهاء من المقال، أضيفه إلى public/articles/index.json ليظهر في قائمة المدونة:

الخطوة 4: الاختبار محلياً

أشغل خادم التطوير Vite:

pnpm dev

Vite يبدأ في غضون ميلي ثوانٍ ويمكنني رؤية مقالي في الوقت الفعلي على localhost:5173:

الخطوة 5: النشر

مجرد git push يكفي! خط أنابيب CI/CD يتولى الباقي تلقائياً.

خط أنابيب النشر CI/CD

لقد أعددت خط أنابيب GitHub Actions كامل يؤتمت الفحص والبناء والنشر للموقع عند كل push على main. لنرَ ذلك بالتفصيل.

سير العمل موجود في .github/workflows/deploy.yml ومقسم إلى وظيفتين: build و deploy.

المشغلات

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

خط الأنابيب يعمل عند كل push على main وعند كل pull request يستهدف main. يتم فحص الطلبات (فحص + بناء) قبل دمجها، لكن فقط الـ pushes على main تشغل النشر.

الوظيفة 1: Build

وظيفة البناء تعمل على ubuntu-latest وتتبع هذه الخطوات:

  1. Checkout -- يستنسخ المستودع مع كل التاريخ (fetch-depth: 0)
  2. Setup pnpm -- يثبت أحدث إصدار من pnpm باستخدام pnpm/action-setup@v4
  3. Setup Node.js 20 -- يهيئ Node مع تفعيل cache pnpm لتركيبات أسرع
  4. Install dependencies -- ينفذ pnpm install --frozen-lockfile لضمان بناءات قابلة للتكرار (لا يُسمح بتعديل lockfile)
  5. Lint -- ينفذ pnpm run lint (ESLint) للتحقق من جودة الكود قبل البناء
  6. Build -- ينفذ pnpm run build، الذي يتحقق أولاً من أنواع TypeScript (tsc -b) ثم يحزم كل شيء مع Vite
  7. Upload artifact -- يرفع مجلد dist/ كقطعة بناء لوظيفة النشر

إذا فشلت أي خطوة -- خطأ في الفحص أو الأنواع أو البناء -- يتوقف خط الأنابيب بالكامل ولا يُنشر شيء. هذا يحمي الموقع في الإنتاج من الكود المعطل.

الوظيفة 2: Deploy

وظيفة النشر تُنفذ فقط إذا:

  • وظيفة البناء نجحت (needs: build)
  • الحدث هو push (ليس PR)
  • الفرع هو main
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

ثم تتابع:

  1. تنزيل قطعة البناء -- تسترجع مجلد dist/ المنتج بواسطة وظيفة البناء
  2. إعداد GitHub Pages -- تهيئة بيئة Pages
  3. رفع إلى Pages -- تحضير مجلد dist/ لـ GitHub Pages
  4. نشر -- تنشر الموقع باستخدام actions/deploy-pages@v4

الجدول الكامل

إليك ما يحدث من الكتابة إلى النشر:

كتابة المقال في VS Code
         ↓
   git add & commit
         ↓
      git push
         ↓
   GitHub Actions يبدأ
         ↓
   ┌─────────────────┐
   │  BUILD JOB      │
   │  1. Checkout    │
   │  2. Setup pnpm  │
   │  3. Setup Node  │
   │  4. Install     │
   │  5. Lint ✓      │
   │  6. Build ✓     │
   │  7. Upload dist │
   └────────┬────────┘
            ↓
   ┌─────────────────┐
   │  DEPLOY JOB     │
   │  1. Download    │
   │  2. Configure   │
   │  3. Upload      │
   │  4. Deploy 🚀   │
   └─────────────────┘
            ↓
    على الإنترنت على GitHub Pages!

العملية بأكملها تستغرق حوالي دقيقة بين push والنشر. لا نشر يدوي، لا FTP، لا SSH -- مجرد git push ويتم الأمر.

بناء الإنتاج

تحت الغطاء، الأمر pnpm build ينفذ:

  1. tsc -b -- يتحقق من أنواع TypeScript
  2. vite build -- يحزم ويحسن كل الكود

Vite ينتج ملفات مصغرة ومحسنة مع تقسيم تلقائي للكود. النتيجة هي موقع ثابت فائق السرعة.

لماذا هذه البنية؟

كان بإمكاني استخدام CMS، أو مولد موقع ثابت مثل Hugo أو Jekyll، أو حتى Next.js. لكن إليك لماذا اخترت هذا النهج:

  • البساطة -- اكتب بـ Markdown، ادفع إلى GitHub، يصبح على الإنترنت
  • تحكم كامل -- لا اعتماد على CMS أو قاعدة بيانات
  • الأداء -- Vite + React = تحميل سريع
  • المرونة -- يمكنني مزج Markdown و HTML كما أريد
  • التعلم -- إنه مشروع رائع لإتقان React و TypeScript
  • CI/CD -- فحوصات جودة ونشر آلي مع GitHub Actions

الخاتمة

هذه المدونة مشروع بسيط ولكنه مدروس جيداً: Markdown للمحتوى، React للعرض، Vite للأداء، GitHub Actions لـ CI/CD، و GitHub Pages للاستضافة. لا قاعدة بيانات، لا خادم طرف خلفي، مجرد ملفات ثابتة تُخدم بكفاءة مع خط أنابيب آلي يضمن الجودة عند كل push.

شكراً للقراءة، وإلى اللقاء في المقال القادم! 🦊

✨ AI Generated Article

Blog này hoạt động như thế nào ?

Hậu trường của blog: React, Vite, Markdown, pipeline CI/CD và quy trình viết bài.

Blog Này Hoạt Động Như Thế Nào ?

Bạn đã bao giờ tự hỏi blog này hoạt động ra sao dưới mui xe chưa ? Trong bài viết này, tôi sẽ trình bày chi tiết toàn bộ kiến trúc của ứng dụng, từ stack kỹ thuật cho đến quy trình viết một bài. Và vâng, tôi thậm chí sẽ chỉ cho bạn cách tôi viết bài ngay từ VS Code !

Stack Kỹ Thuật

Blog này được xây dựng với các công nghệ web hiện đại :

  • React 19 -- cho giao diện người dùng
  • TypeScript -- cho mã nguồn có kiểu và đáng tin cậy hơn
  • Vite -- công cụ build siêu nhanh
  • React Router v7 -- cho điều hướng giữa các trang
  • react-markdown -- để chuyển đổi Markdown thành HTML
  • rehype-raw + rehype-sanitize -- để cho phép HTML thô trong Markdown một cách an toàn

Cấu Trúc Dự Án

Đây là cây thư mục của dự án :

├── .github/
│   └── workflows/
│       └── deploy.yml              ← Pipeline CI/CD
├── public/
│   ├── home.md                     ← Nội dung trang chủ
│   ├── portfolio.md                ← Nội dung portfolio
│   └── articles/
│       ├── index.json              ← Danh sách tất cả bài viết
│       ├── hello-world.md          ← Một bài viết
│       ├── how-this-blog-works.md  ← Chính bài viết này !
│       └── /articles/assets/                 ← Hình ảnh của bài viết
├── src/
│   ├── main.tsx                    ← Điểm vào React
│   ├── App.tsx                     ← Router chính
│   ├── components/
│   │   ├── Header.tsx              ← Thanh điều hướng
│   │   └── Footer.tsx              ← Chân trang
│   └── pages/
│       ├── Home.tsx                ← Trang chủ
│       ├── BlogList.tsx            ← Danh sách bài viết
│       ├── Article.tsx             ← Trình đọc bài viết
│       ├── Portfolio.tsx           ← Trang portfolio
│       └── NotFound.tsx            ← Trang 404
└── vite.config.ts                  ← Cấu hình Vite

Ý tưởng trung tâm rất đơn giản : nội dung được tách biệt khỏi mã nguồn. Các trang được viết bằng Markdown trong thư mục public/, và mã React trong src/ đảm nhận việc hiển thị chúng.

Hệ Thống Định Tuyến

Tệp App.tsx định nghĩa tất cả các route của ứng dụng với React Router :

Route Trang Mô tả
/ Home Trang chủ, tải home.md
/blog BlogList Danh sách tất cả bài viết
/blog/:slug Article Một bài viết, tải articles/{slug}.md
/portfolio Portfolio Trang portfolio, tải portfolio.md
* NotFound Trang 404 cho các URL không xác định

Mỗi trang có một vai trò rõ ràng : nó lấy một tệp Markdown, chuyển đổi thành HTML bằng react-markdown, và hiển thị lên màn hình.

Một Bài Viết Hoạt Động Như Thế Nào ?

Đây là phần thú vị nhất ! Đây là vòng đời của một bài viết :

1. Tệp index.json

Tất cả bài viết được tham chiếu trong public/articles/index.json. Mỗi mục chứa siêu dữ liệu của bài viết :

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug -- định danh duy nhất, được dùng trong URL (/blog/hello-world)
  • title -- tiêu đề hiển thị trong danh sách
  • description -- một tóm tắt ngắn
  • date -- ngày xuất bản

2. Tệp Markdown

Nội dung của bài viết là một tệp .md đơn giản trong public/articles/. Tên tệp tương ứng với slug được định nghĩa trong index.json.

Bạn có thể đặt bất cứ thứ gì bạn muốn : tiêu đề, danh sách, hình ảnh, bảng biểu, và thậm chí cả HTML thô nhờ rehype-raw !

3. Render Phía React

Khi bạn truy cập /blog/hello-world, đây là những gì xảy ra :

  1. React Router lấy tham số slug từ URL
  2. Component Article.tsx tải /articles/hello-world.md
  3. Markdown được chuyển đổi thành HTML bởi react-markdown
  4. Các đường dẫn đến /articles/assets/ được tự động viết lại thành /articles//articles/assets/
  5. Song song đó, siêu dữ liệu được tải từ index.json để hiển thị ngày và mô tả

Đơn giản vậy thôi !

Trang Chủ và Portfolio

Trang Chủ và Portfolio hoạt động hoàn toàn giống nhau : chúng tải một tệp Markdown (home.md hoặc portfolio.md) và render thành HTML.

Điểm đặc biệt là chúng sử dụng một lược đồ sanitization tùy chỉnh cho phép các thuộc tính class và style trên tất cả các phần tử HTML. Điều này cho phép tôi viết HTML có kiểu dáng trực tiếp trong Markdown, chẳng hạn như thư viện ảnh.

Header được ghim ở đầu trang với position: fixed. Nó chứa :

  • Avatar GitHub của tôi (tải trực tiếp từ github.com/fox3000foxy.png)
  • Tiêu đề blog
  • Các liên kết điều hướng : Trang chủ, Blog, Portfolio

Footer rất tối giản : chỉ là bản quyền với năm hiện tại được tính động.

Chế Độ Tối

Trang web luôn ở chế độ tối -- không có nút chuyển ngày/đêm. Đó là một lựa chọn có chủ đích : color-scheme: dark được định nghĩa trong các style toàn cục, với nền đen #000 và chữ trắng #fff. Các liên kết có màu xanh lam (#64b5f6) và chuyển sang màu xanh lá khi di chuột (#81c784).

Cách Tôi Viết Một Bài Viết

Chuyển sang thực hành nào ! Đây là quy trình làm việc của tôi để viết một bài viết mới :

Bước 1 : Tạo Tệp Markdown

Tôi mở VS Code và tạo một tệp .md mới trong public/articles/ :

Bước 2 : Viết Nội Dung

Tôi viết nội dung bài viết trực tiếp bằng Markdown. VS Code có tính năng xem trước Markdown tích hợp rất tốt :

Đối với hình ảnh, tôi đặt chúng trong public/articles//articles/assets/ và tham chiếu bằng cú pháp Markdown tiêu chuẩn :

![description](/articles/assets/my-image.png)

Component Article.tsx tự động viết lại đường dẫn /articles/assets/ thành /articles//articles/assets/ để hình ảnh hiển thị chính xác.

Bước 3 : Đăng Ký Bài Viết trong index.json

Sau khi hoàn thành bài viết, tôi thêm nó vào public/articles/index.json để nó xuất hiện trong danh sách blog :

Bước 4 : Kiểm Tra Cục Bộ

Tôi chạy máy chủ phát triển Vite :

pnpm dev

Vite khởi động trong vài mili giây và tôi có thể thấy bài viết của mình trong thời gian thực tại localhost:5173 :

Bước 5 : Xuất Bản

Chỉ cần git push là đủ ! Pipeline CI/CD sẽ tự động xử lý phần còn lại.

Pipeline Triển Khai CI/CD

Tôi đã thiết lập một pipeline GitHub Actions hoàn chỉnh giúp tự động hóa việc lint, build và triển khai trang web mỗi khi push lên main. Hãy xem chi tiết nào.

Workflow nằm trong .github/workflows/deploy.yml và được chia thành hai job : build và deploy.

Bộ Kích Hoạt

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

Pipeline chạy mỗi khi push lên main và mỗi pull request nhắm vào main. Các PR được kiểm tra (lint + build) trước khi merge, nhưng chỉ các push lên main mới kích hoạt triển khai.

Job 1 : Build

Job build chạy trên ubuntu-latest và thực hiện các bước sau :

  1. Checkout -- Clone kho với toàn bộ lịch sử (fetch-depth: 0)
  2. Setup pnpm -- Cài đặt phiên bản pnpm mới nhất với pnpm/action-setup@v4
  3. Setup Node.js 20 -- Cấu hình Node với bộ nhớ đệm pnpm đã được kích hoạt để cài đặt nhanh hơn
  4. Install dependencies -- Chạy pnpm install --frozen-lockfile để đảm bảo các bản build có thể tái tạo (không cho phép sửa đổi lockfile)
  5. Lint -- Chạy pnpm run lint (ESLint) để kiểm tra chất lượng mã trước khi build
  6. Build -- Chạy pnpm run build, trước tiên kiểm tra kiểu TypeScript (tsc -b) sau đó bundle mọi thứ với Vite
  7. Upload artifact -- Tải lên thư mục dist/ như một artifact build cho job triển khai

Nếu bất kỳ bước nào thất bại -- lỗi lint, kiểu hay build -- toàn bộ pipeline dừng lại và không có gì được triển khai. Điều này bảo vệ trang web production khỏi mã hỏng.

Job 2 : Deploy

Job triển khai chỉ chạy nếu :

  • Job build đã thành công (needs: build)
  • Sự kiện là push (không phải PR)
  • Nhánh là main
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

Sau đó nó tiến hành :

  1. Tải artifact build -- Lấy thư mục dist/ được tạo bởi job build
  2. Cấu hình GitHub Pages -- Thiết lập môi trường Pages
  3. Tải lên Pages -- Chuẩn bị thư mục dist/ cho GitHub Pages
  4. Triển khai -- Xuất bản trang web với actions/deploy-pages@v4

Bảng Tổng Quan Đầy Đủ

Đây là những gì xảy ra từ lúc viết đến khi triển khai :

Viết bài trong VS Code
         ↓
   git add & commit
         ↓
      git push
         ↓
 GitHub Actions được kích hoạt
         ↓
 ┌─────────────────┐
 │   BUILD JOB     │
 │  1. Checkout    │
 │  2. Setup pnpm  │
 │  3. Setup Node  │
 │  4. Install     │
 │  5. Lint ✓      │
 │  6. Build ✓     │
 │  7. Upload dist │
 └────────┬────────┘
          ↓
 ┌─────────────────┐
 │  DEPLOY JOB     │
 │  1. Download    │
 │  2. Configure   │
 │  3. Upload      │
 │  4. Deploy 🚀   │
 └─────────────────┘
          ↓
   Trực tuyến trên GitHub Pages !

Toàn bộ quy trình mất khoảng một phút từ lúc push đến khi lên mạng. Không triển khai thủ công, không FTP, không SSH -- chỉ cần git push và xong.

Build cho Production

Dưới mui xe, lệnh pnpm build thực thi :

  1. tsc -b -- Kiểm tra kiểu TypeScript
  2. vite build -- Bundle và tối ưu hóa toàn bộ mã nguồn

Vite tạo ra các tệp đã được minify và tối ưu hóa với tính năng code-splitting tự động. Kết quả là một trang web tĩnh siêu nhanh.

Tại Sao Kiến Trúc Này ?

Tôi đã có thể dùng CMS, một trình tạo trang web tĩnh như Hugo hay Jekyll, hoặc thậm chí Next.js. Nhưng đây là lý do tôi chọn cách tiếp cận này :

  • Đơn giản -- Viết Markdown, push lên GitHub, là lên mạng
  • Kiểm soát hoàn toàn -- Không phụ thuộc vào CMS hay cơ sở dữ liệu
  • Hiệu năng -- Vite + React = tải nhanh
  • Linh hoạt -- Tôi có thể kết hợp Markdown và HTML tùy ý
  • Học tập -- Đây là một dự án tuyệt vời để làm chủ React và TypeScript
  • CI/CD -- Kiểm tra chất lượng và triển khai tự động với GitHub Actions

Kết Luận

Blog này là một dự án đơn giản nhưng được thiết kế tốt : Markdown cho nội dung, React cho render, Vite cho hiệu năng, GitHub Actions cho CI/CD, và GitHub Pages cho lưu trữ. Không cơ sở dữ liệu, không máy chủ backend, chỉ là các tệp tĩnh được phục vụ hiệu quả với một pipeline tự động đảm bảo chất lượng mỗi lần push.

Cảm ơn bạn đã đọc, và hẹn gặp lại trong bài viết tiếp theo ! 🦊

✨ AI Generated Article

บล็อกนี้ทำงานอย่างไร ?

เบื้องหลังของบล็อก: React, Vite, Markdown, CI/CD Pipeline

บล็อกนี้ทำงานอย่างไร ?

คุณเคยสงสัยไหมว่าบล็อกนี้ทำงานภายใต้ฝาครอบอย่างไร ? ในบทความนี้ ผมจะอธิบายรายละเอียดทั้งหมดเกี่ยวกับสถาปัตยกรรมของแอปพลิเคชัน ตั้งแต่เทคนิคสแตกไปจนถึงกระบวนการเขียนบทความ ใช่แล้ว ผมจะโชว์ให้คุณเห็นด้วยว่าผมเขียนบทความของผมจาก VS Code อย่างไร !

เทคนิคสแตก (Tech Stack)

บล็อกนี้สร้างขึ้นด้วยเทคโนโลยีเว็บสมัยใหม่:

  • React 19 -- สำหรับอินเทอร์เฟซผู้ใช้
  • TypeScript -- สำหรับโค้ดที่กำหนดชนิดและเชื่อถือได้มากขึ้น
  • Vite -- เป็นเครื่องมือ build ที่เร็วเป็นพิเศษ
  • React Router v7 -- สำหรับการนำทางระหว่างหน้า
  • react-markdown -- สำหรับแปลง Markdown เป็น HTML
  • rehype-raw + rehype-sanitize -- สำหรับอนุญาต HTML ดิบใน Markdown อย่างปลอดภัย

โครงสร้างโปรเจกต์

นี่คือลักษณะโครงสร้างของโปรเจกต์:

├── .github/
│   └── workflows/
│       └── deploy.yml              ← CI/CD Pipeline
├── public/
│   ├── home.md                     ← เนื้อหาหน้าแรก
│   ├── portfolio.md                ← เนื้อหาพอร์ตโฟลิโอ
│   └── articles/
│       ├── index.json              ← รายการบทความทั้งหมด
│       ├── hello-world.md          ← บทความหนึ่ง
│       ├── how-this-blog-works.md  ← บทความนี้ !
│       └── /articles/assets/                 ← รูปภาพของบทความ
├── src/
│   ├── main.tsx                    ← จุดเริ่มต้น React
│   ├── App.tsx                     ← เราเตอร์หลัก
│   ├── components/
│   │   ├── Header.tsx              ← แถบนำทาง
│   │   └── Footer.tsx              ← ส่วนท้าย
│   └── pages/
│       ├── Home.tsx                ← หน้าแรก
│       ├── BlogList.tsx            ← รายการบทความ
│       ├── Article.tsx             ← โปรแกรมอ่านบทความ
│       ├── Portfolio.tsx           ← หน้าพอร์ตโฟลิโอ
│       └── NotFound.tsx            ← หน้า 404
└── vite.config.ts                  ← การกำหนดค่า Vite

แนวคิดหลักนั้นเรียบง่าย: เนื้อหาถูกแยกออกจากโค้ด หน้าต่าง ๆ เขียนด้วย Markdown ในโฟลเดอร์ public/ และโค้ด React ใน src/ ทำหน้าที่แสดงผล

ระบบการกำหนดเส้นทาง (Routing System)

ไฟล์ App.tsx กำหนดเส้นทางทั้งหมดของแอปพลิเคชันด้วย React Router:

Route Page คำอธิบาย
/ Home หน้าแรก, โหลด home.md
/blog BlogList รายการบทความทั้งหมด
/blog/:slug Article บทความ, โหลด articles/{slug}.md
/portfolio Portfolio หน้าพอร์ตโฟลิโอ, โหลด portfolio.md
* NotFound หน้า 404 สำหรับ URL ที่ไม่รู้จัก

แต่ละหน้ามีบทบาทที่ชัดเจน: ดึงไฟล์ Markdown, แปลงเป็น HTML ด้วย react-markdown, และแสดงผลบนหน้าจอ

บทความทำงานอย่างไร ?

นี่คือส่วนที่น่าสนใจที่สุด ! นี่คือวงจรชีวิตของบทความ:

1. ไฟล์ index.json

บทความทั้งหมดถูกอ้างอิงใน public/articles/index.json แต่ละรายการประกอบด้วยข้อมูลเมตาของบทความ:

[
  {
    "slug": "hello-world",
    "title": "Hello World",
    "description": "A sample post for Fox's Blog.",
    "date": "2026-03-08"
  }
]
  • slug -- ตัวระบุเฉพาะ, ใช้ใน URL (/blog/hello-world)
  • title -- ชื่อเรื่องที่แสดงในรายการ
  • description -- สรุปสั้น ๆ
  • date -- วันที่เผยแพร่

2. ไฟล์ Markdown

เนื้อหาของบทความเป็นไฟล์ .md ธรรมดาใน public/articles/ ชื่อไฟล์ตรงกับ slug ที่กำหนดใน index.json

คุณสามารถใส่สิ่งที่ต้องการ: หัวข้อ, รายการ, รูปภาพ, ตาราง, และแม้แต่ HTML ดิบได้ด้วย rehype-raw !

3. การเรนเดอร์ฝั่ง React

เมื่อคุณเยี่ยมชม /blog/hello-world สิ่งนี้จะเกิดขึ้น:

  1. React Router ดึงพารามิเตอร์ slug จาก URL
  2. คอมโพเนนต์ Article.tsx โหลด /articles/hello-world.md
  3. Markdown ถูกแปลงเป็น HTML โดย react-markdown
  4. ลิงก์ไปยัง /articles/assets/ ถูกเขียนใหม่เป็น /articles//articles/assets/ โดยอัตโนมัติ
  5. ในเวลาเดียวกัน, ข้อมูลเมตาจะถูกโหลดจาก index.json เพื่อแสดงวันที่และคำอธิบาย

ง่ายแบบนั้นเลย !

หน้าแรกและพอร์ตโฟลิโอ

หน้าหลักและหน้าพอร์ตโฟลิโอทำงานในลักษณะเดียวกันทุกประการ: โหลดไฟล์ Markdown (home.md หรือ portfolio.md) และเรนเดอร์เป็น HTML

จุดพิเศษคือ พวกมันใช้ schema sanitization ที่กำหนดเองซึ่งอนุญาตให้ใช้แอตทริบิวต์ class และ style บนองค์ประกอบ HTML ทั้งหมด ซึ่งทำให้ผมสามารถเขียน HTML ที่มีสไตล์ได้โดยตรงใน Markdown เช่น แกลเลอรีรูปภาพ

Header ถูกปักหมุดไว้ด้านบนของหน้าด้วย position: fixed ประกอบด้วย:

  • Avatar GitHub ของผม (โหลดโดยตรงจาก github.com/fox3000foxy.png)
  • ชื่อบล็อก
  • ลิงก์นำทาง: หน้าแรก, บล็อก, พอร์ตโฟลิโอ

Footer เป็นแบบมินิมอล: แค่ลิขสิทธิ์พร้อมปีปัจจุบันที่คำนวณแบบไดนามิก

โหมดมืด (Dark Theme)

เว็บไซต์เป็น โหมดมืดเสมอ -- ไม่มีการสลับกลางวัน/กลางคืน นี่คือการตัดสินใจโดยเจตนา: color-scheme: dark ถูกกำหนดในสไตล์ส่วนกลาง, พร้อมพื้นหลังสีดำ #000 และข้อความสีขาว #fff ลิงก์เป็นสีน้ำเงิน (#64b5f6) และเปลี่ยนเป็นสีเขียวเมื่อชี้ (#81c784)

ผมเขียนบทความอย่างไร

มาถึงภาคปฏิบัติ ! นี่คือขั้นตอนการทำงานของผมในการเขียนบทความใหม่:

ขั้นตอนที่ 1: สร้างไฟล์ Markdown

ผมเปิด VS Code และสร้างไฟล์ .md ใหม่ใน public/articles/:

ขั้นตอนที่ 2: เขียนเนื้อหา

ผมเขียนเนื้อหาของบทความโดยตรงใน Markdown VS Code มีตัวอย่าง Markdown ในตัวที่ยอดเยี่ยม:

สำหรับรูปภาพ ผมวางไว้ใน public/articles//articles/assets/ และอ้างอิงด้วยไวยากรณ์ Markdown มาตรฐาน:

![description](/articles/assets/my-image.png)

คอมโพเนนต์ Article.tsx จะเขียนเส้นทาง /articles/assets/ เป็น /articles//articles/assets/ โดยอัตโนมัติเพื่อให้รูปภาพแสดงผลอย่างถูกต้อง

ขั้นตอนที่ 3: ลงทะเบียนบทความใน index.json

เมื่อบทความเสร็จสมบูรณ์ ผมเพิ่มมันใน public/articles/index.json เพื่อให้ปรากฏในรายการบล็อก:

ขั้นตอนที่ 4: ทดสอบในเครื่อง

ผมรันเซิร์ฟเวอร์พัฒนา Vite:

pnpm dev

Vite เริ่มทำงานในไม่กี่มิลลิวินาที และผมสามารถดูบทความแบบเรียลไทม์ที่ localhost:5173:

ขั้นตอนที่ 5: เผยแพร่

แค่ git push ก็พอ ! CI/CD Pipeline จัดการส่วนที่เหลือโดยอัตโนมัติ

Deployment Pipeline CI/CD

ผมตั้งค่า GitHub Actions pipeline ที่สมบูรณ์ซึ่งจะทำ lint, build และ deploy เว็บไซต์โดยอัตโนมัติทุกครั้งที่มีการ push ไปยัง main มาดูรายละเอียดกัน

Workflow อยู่ใน .github/workflows/deploy.yml และแบ่งออกเป็นสอง jobs: build และ deploy

ตัวกระตุ้น (Triggers)

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

Pipeline จะทำงานทุกครั้งที่มี push ไปยัง main และทุก pull request ที่มีเป้าหมายเป็น main ดังนั้น PRs จะถูกตรวจสอบ (lint + build) ก่อนที่จะถูก merge แต่เฉพาะ push ที่ main เท่านั้นที่จะกระตุ้นให้มีการ deploy

Job 1: Build

Job build ทำงานบน ubuntu-latest และทำตามขั้นตอนเหล่านี้:

  1. Checkout -- โคลน repository พร้อมประวัติทั้งหมด (fetch-depth: 0)
  2. Setup pnpm -- ติดตั้ง pnpm เวอร์ชันล่าสุดด้วย pnpm/action-setup@v4
  3. Setup Node.js 20 -- กำหนดค่า Node พร้อมเปิดใช้งานแคช pnpm เพื่อการติดตั้งที่เร็วขึ้น
  4. Install dependencies -- รัน pnpm install --frozen-lockfile เพื่อรับประกัน build ที่ reproducible (ไม่อนุญาตให้แก้ไข lockfile)
  5. Lint -- รัน pnpm run lint (ESLint) เพื่อตรวจสอบคุณภาพโค้ดก่อน build
  6. Build -- รัน pnpm run build ซึ่งตรวจสอบชนิด TypeScript ก่อน (tsc -b) จากนั้น bundle ทุกอย่างด้วย Vite
  7. Upload artifact -- อัปโหลดโฟลเดอร์ dist/ เป็น build artifact สำหรับ job deploy

หากขั้นตอนใดล้มเหลว -- ไม่ว่าจะเป็น lint error, type error หรือ build error -- ทั้ง pipeline จะหยุดและไม่มีอะไรถูก deploy ซึ่งช่วยปกป้องเว็บไซต์ใน production จากโค้ดที่เสียหาย

Job 2: Deploy

Job deploy จะทำงานก็ต่อเมื่อ:

  • Job build สำเร็จ (needs: build)
  • Event เป็น push (ไม่ใช่ PR)
  • Branch เป็น main
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

จากนั้นดำเนินการ:

  1. ดาวน์โหลด build artifact -- ดึงโฟลเดอร์ dist/ ที่ผลิตโดย job build
  2. กำหนดค่า GitHub Pages -- ตั้งค่าสภาพแวดล้อม Pages
  3. อัปโหลดไปยัง Pages -- เตรียมโฟลเดอร์ dist/ สำหรับ GitHub Pages
  4. Deploy -- เผยแพร่เว็บไซต์ด้วย actions/deploy-pages@v4

ตารางแบบครบถ้วน

นี่คือสิ่งที่เกิดขึ้นตั้งแต่การเขียนจนถึงการ deploy:

เขียนบทความใน VS Code
         ↓
   git add & commit
         ↓
      git push
         ↓
 GitHub Actions เริ่มทำงาน
         ↓
 ┌─────────────────┐
 │   BUILD JOB     │
 │  1. Checkout    │
 │  2. Setup pnpm  │
 │  3. Setup Node  │
 │  4. Install     │
 │  5. Lint ✓      │
 │  6. Build ✓     │
 │  7. Upload dist │
 └────────┬────────┘
          ↓
 ┌─────────────────┐
 │  DEPLOY JOB     │
 │  1. Download    │
 │  2. Configure   │
 │  3. Upload      │
 │  4. Deploy 🚀   │
 └─────────────────┘
          ↓
   ออนไลน์บน GitHub Pages !

กระบวนการทั้งหมดใช้เวลาประมาณหนึ่งนาทีตั้งแต่ push จนถึงออนไลน์ ไม่มีการ deploy ด้วยตนเอง, ไม่มี FTP, ไม่มี SSH -- แค่ git push เท่านั้น

Production Build

ภายใต้ฝาครอบ, คำสั่ง pnpm build จะทำงาน:

  1. tsc -b -- ตรวจสอบชนิด TypeScript
  2. vite build -- Bundle และปรับแต่งโค้ดทั้งหมด

Vite สร้างไฟล์ที่ minified และปรับแต่งแล้วพร้อม code-splitting อัตโนมัติ ผลลัพธ์คือเว็บไซต์แบบ static ที่เร็วเป็นพิเศษ

ทำไมถึงเลือกสถาปัตยกรรมนี้ ?

ผมอาจใช้ CMS, เครื่องมือสร้างเว็บไซต์ static อย่าง Hugo หรือ Jekyll, หรือแม้แต่ Next.js แต่ทำไมผมถึงเลือกแนวทางนี้:

  • ความเรียบง่าย -- เขียนใน Markdown, push ไปยัง GitHub, ก็ออนไลน์
  • ควบคุมได้ทั้งหมด -- ไม่พึ่งพา CMS หรือฐานข้อมูล
  • ประสิทธิภาพ -- Vite + React = โหลดเร็ว
  • ความยืดหยุ่น -- ผมสามารถผสม Markdown และ HTML ได้ตามต้องการ
  • การเรียนรู้ -- มันเป็นโปรเจกต์ที่ยอดเยี่ยมสำหรับการเรียนรู้ React และ TypeScript
  • CI/CD -- การตรวจสอบคุณภาพและการ deploy อัตโนมัติด้วย GitHub Actions

บทสรุป

บล็อกนี้เป็นโปรเจกต์ที่เรียบง่ายแต่ออกแบบมาอย่างดี: Markdown สำหรับเนื้อหา, React สำหรับการเรนเดอร์, Vite เพื่อประสิทธิภาพ, GitHub Actions สำหรับ CI/CD, และ GitHub Pages สำหรับโฮสต์ ไม่มีฐานข้อมูล, ไม่มีเซิร์ฟเวอร์ backend, แค่ไฟล์ static ที่ให้บริการอย่างมีประสิทธิภาพด้วย pipeline อัตโนมัติที่รับประกันคุณภาพทุกครั้งที่มีการ push

ขอบคุณที่อ่าน แล้วพบกันในบทความหน้าครับ ! 🦊

Related Articles