feat(blog): add blog posts and typography plugin

Add new blog posts covering React Hooks, Docker with Node.js, Tailwind CSS, and TypeScript best practices. Implement Tailwind Typography plugin for better blog post styling. Create blog post listing pages in both English and Chinese. Update BlogLayout to use prose styling for improved readability.

The changes include:
- Added 4 new blog posts in English and Chinese versions
- Integrated @tailwindcss/typography plugin
- Created blog post index pages
- Updated BlogLayout with typography-focused styling
- Added related dependencies to package.json
This commit is contained in:
joyzhao
2025-06-16 22:24:20 +08:00
parent b4b2153bde
commit 70b6a27021
14 changed files with 4699 additions and 1 deletions

View File

@@ -33,5 +33,8 @@
"tailwind-merge": "^3.0.2",
"tailwindcss": "^4.0.14",
"tw-animate-css": "^1.2.4"
},
"devDependencies": {
"@tailwindcss/typography": "^0.5.16"
}
}

46
pnpm-lock.yaml generated
View File

@@ -59,6 +59,10 @@ importers:
tw-animate-css:
specifier: ^1.2.4
version: 1.3.4
devDependencies:
'@tailwindcss/typography':
specifier: ^0.5.16
version: 0.5.16(tailwindcss@4.1.10)
packages:
@@ -720,6 +724,11 @@ packages:
resolution: {integrity: sha512-v0C43s7Pjw+B9w21htrQwuFObSkio2aV/qPx/mhrRldbqxbWJK6KizM+q7BF1/1CmuLqZqX3CeYF7s7P9fbA8Q==}
engines: {node: '>= 10'}
'@tailwindcss/typography@0.5.16':
resolution: {integrity: sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==}
peerDependencies:
tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1'
'@tailwindcss/vite@4.1.10':
resolution: {integrity: sha512-QWnD5HDY2IADv+vYR82lOhqOlS1jSCUUAmfem52cXAhRTKxpDh3ARX8TTXJTCCO7Rv7cD2Nlekabv02bwP3a2A==}
peerDependencies:
@@ -1431,9 +1440,18 @@ packages:
locale-emoji@0.3.0:
resolution: {integrity: sha512-JGm8+naU49CBDnH1jksS3LecPdfWQLxFgkLN6ZhYONKa850pJ0Xt8DPGJnYK0ZuJI8jTuiDDPCDtSL3nyacXwg==}
lodash.castarray@4.4.0:
resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==}
lodash.debounce@4.0.8:
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
lodash.isplainobject@4.0.6:
resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
lodash.merge@4.6.2:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
lodash.throttle@4.1.1:
resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==}
@@ -1696,6 +1714,10 @@ packages:
resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
engines: {node: '>=12'}
postcss-selector-parser@6.0.10:
resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==}
engines: {node: '>=4'}
postcss@8.5.5:
resolution: {integrity: sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==}
engines: {node: ^10 || ^12 || >=14}
@@ -2025,6 +2047,9 @@ packages:
peerDependencies:
browserslist: '>= 4.21.0'
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
vfile-location@5.0.3:
resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==}
@@ -2720,6 +2745,14 @@ snapshots:
'@tailwindcss/oxide-win32-arm64-msvc': 4.1.10
'@tailwindcss/oxide-win32-x64-msvc': 4.1.10
'@tailwindcss/typography@0.5.16(tailwindcss@4.1.10)':
dependencies:
lodash.castarray: 4.4.0
lodash.isplainobject: 4.0.6
lodash.merge: 4.6.2
postcss-selector-parser: 6.0.10
tailwindcss: 4.1.10
'@tailwindcss/vite@4.1.10(vite@6.3.5(@types/node@24.0.1)(jiti@2.4.2)(lightningcss@1.30.1))':
dependencies:
'@tailwindcss/node': 4.1.10
@@ -3499,8 +3532,14 @@ snapshots:
locale-emoji@0.3.0: {}
lodash.castarray@4.4.0: {}
lodash.debounce@4.0.8: {}
lodash.isplainobject@4.0.6: {}
lodash.merge@4.6.2: {}
lodash.throttle@4.1.1: {}
longest-streak@3.1.0: {}
@@ -3930,6 +3969,11 @@ snapshots:
picomatch@4.0.2: {}
postcss-selector-parser@6.0.10:
dependencies:
cssesc: 3.0.0
util-deprecate: 1.0.2
postcss@8.5.5:
dependencies:
nanoid: 3.3.11
@@ -4309,6 +4353,8 @@ snapshots:
escalade: 3.2.0
picocolors: 1.1.1
util-deprecate@1.0.2: {}
vfile-location@5.0.3:
dependencies:
'@types/unist': 3.0.3

View File

@@ -40,7 +40,11 @@ const lang = Astro.currentLocale as Lang || defaultLang;
<!-- Main content with proper spacing for fixed header -->
<div class="pt-16">
<main class="container mx-auto px-4 py-8 max-w-4xl">
<article class="prose prose-lg dark:prose-invert prose-headings:text-foreground prose-p:text-muted-foreground prose-strong:text-foreground prose-code:text-foreground prose-pre:bg-muted prose-blockquote:border-l-primary prose-blockquote:text-muted-foreground prose-a:text-primary hover:prose-a:text-primary/80 prose-li:text-muted-foreground">
<slot />
</article>
</main>
</div>
<!-- Footer -->

View File

@@ -0,0 +1,22 @@
---
import BlogLayout from '../../../layouts/BlogLayout.astro';
import type { Lang } from '../../../i18n/utils';
const lang = (Astro.currentLocale || 'en') as Lang;
---
<BlogLayout title="Blog Posts">
<div class="container mx-auto px-4 py-8">
<h1 class="text-4xl font-bold text-center mb-8">All Blog Posts</h1>
<p class="text-center text-muted-foreground mb-8">
Explore all our blog posts organized by categories and topics.
</p>
<!-- This page can be used for additional blog organization features -->
<div class="text-center">
<a href="/blog" class="text-primary hover:underline">
← Back to Blog Home
</a>
</div>
</div>
</BlogLayout>

View File

@@ -0,0 +1,230 @@
---
title: "Mastering React Hooks: A Deep Dive"
description: "Explore the power of React Hooks to manage state and side effects in functional components, with practical examples and best practices."
image: "https://images.unsplash.com/photo-1633356122544-f134324a6cee?w=400&h=250&fit=crop&crop=center"
date: "May 10, 2025"
readTime: "5 min read"
tags: ["React", "JavaScript", "Frontend"]
slug: "mastering-react-hooks"
layout: "../../../layouts/BlogLayout.astro"
---
# Mastering React Hooks: A Deep Dive
![React Hooks](https://images.unsplash.com/photo-1633356122544-f134324a6cee?w=800&h=400&fit=crop&crop=center)
React Hooks have revolutionized the way we write React components, allowing us to use state and other React features in functional components. In this comprehensive guide, we'll explore the most important hooks and learn how to use them effectively.
## What are React Hooks?
React Hooks are functions that let you "hook into" React state and lifecycle features from functional components. They were introduced in React 16.8 and have since become the preferred way to write React components.
### Key Benefits:
- **Simpler code**: No need for class components
- **Better reusability**: Custom hooks allow sharing stateful logic
- **Easier testing**: Functional components are easier to test
- **Better performance**: Optimizations are easier to implement
## Essential React Hooks
### 1. useState Hook
The `useState` hook allows you to add state to functional components:
```javascript
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
```
### 2. useEffect Hook
The `useEffect` hook lets you perform side effects in functional components:
```javascript
import React, { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchUser() {
setLoading(true);
try {
const response = await fetch(`/api/users/${userId}`);
const userData = await response.json();
setUser(userData);
} catch (error) {
console.error('Error fetching user:', error);
} finally {
setLoading(false);
}
}
fetchUser();
}, [userId]); // Dependency array
if (loading) return <div>Loading...</div>;
if (!user) return <div>User not found</div>;
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
```
### 3. useContext Hook
The `useContext` hook provides a way to pass data through the component tree without having to pass props down manually:
```javascript
import React, { useContext, createContext } from 'react';
const ThemeContext = createContext();
function App() {
return (
<ThemeContext.Provider value="dark">
<Header />
<Main />
</ThemeContext.Provider>
);
}
function Header() {
const theme = useContext(ThemeContext);
return (
<header className={`header-${theme}`}>
<h1>My App</h1>
</header>
);
}
```
## Custom Hooks
One of the most powerful features of React Hooks is the ability to create custom hooks that encapsulate and reuse stateful logic:
```javascript
// Custom hook for API calls
function useApi(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchData() {
try {
setLoading(true);
const response = await fetch(url);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const result = await response.json();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
fetchData();
}, [url]);
return { data, loading, error };
}
// Using the custom hook
function ProductList() {
const { data: products, loading, error } = useApi('/api/products');
if (loading) return <div>Loading products...</div>;
if (error) return <div>Error: {error}</div>;
return (
<ul>
{products.map(product => (
<li key={product.id}>{product.name}</li>
))}
</ul>
);
}
```
## Best Practices
### 1. Follow the Rules of Hooks
- Only call hooks at the top level of your React function
- Only call hooks from React functions (components or custom hooks)
### 2. Optimize Performance
Use `useMemo` and `useCallback` to prevent unnecessary re-renders:
```javascript
import React, { useMemo, useCallback } from 'react';
function ExpensiveComponent({ items, onItemClick }) {
// Memoize expensive calculations
const expensiveValue = useMemo(() => {
return items.reduce((sum, item) => sum + item.value, 0);
}, [items]);
// Memoize callback functions
const handleClick = useCallback((id) => {
onItemClick(id);
}, [onItemClick]);
return (
<div>
<p>Total: {expensiveValue}</p>
{items.map(item => (
<button key={item.id} onClick={() => handleClick(item.id)}>
{item.name}
</button>
))}
</div>
);
}
```
### 3. Clean Up Effects
Always clean up subscriptions and timers in useEffect:
```javascript
useEffect(() => {
const timer = setInterval(() => {
console.log('Timer tick');
}, 1000);
// Cleanup function
return () => {
clearInterval(timer);
};
}, []);
```
## Conclusion
React Hooks have fundamentally changed how we write React applications. They provide a more intuitive and powerful way to manage state and side effects in functional components. By mastering hooks like `useState`, `useEffect`, and `useContext`, and learning to create custom hooks, you'll be able to write more maintainable and reusable React code.
Remember to follow the rules of hooks, optimize for performance when necessary, and always clean up your effects. With these best practices in mind, you'll be well on your way to becoming a React Hooks expert!
---
*Want to learn more about React? Check out our other articles on advanced React patterns and performance optimization.*

View File

@@ -0,0 +1,420 @@
---
title: "Building Modern UIs with Tailwind CSS"
description: "Discover how to create beautiful, responsive user interfaces using Tailwind CSS utility classes and component patterns."
image: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=400&h=250&fit=crop&crop=center"
date: "April 15, 2025"
readTime: "6 min read"
tags: ["CSS", "Tailwind", "UI/UX"]
slug: "modern-ui-tailwind"
layout: "../../../layouts/BlogLayout.astro"
---
# Building Modern UIs with Tailwind CSS
![Modern UI Design](https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=800&h=400&fit=crop&crop=center)
Tailwind CSS has revolutionized the way developers approach styling web applications. By providing a comprehensive set of utility classes, it enables rapid development of beautiful, responsive user interfaces without writing custom CSS. Let's explore how to leverage Tailwind CSS to build modern UIs.
## Why Choose Tailwind CSS?
Tailwind CSS offers several advantages over traditional CSS frameworks:
- **Utility-first approach**: Build complex designs using simple utility classes
- **Highly customizable**: Configure every aspect of your design system
- **Responsive by default**: Built-in responsive design utilities
- **Performance optimized**: Only includes the CSS you actually use
- **Developer experience**: Excellent IntelliSense and tooling support
## Getting Started with Tailwind CSS
### Installation
Install Tailwind CSS in your project:
```bash
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
```
Configure your `tailwind.config.js`:
```javascript
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{html,js,ts,jsx,tsx}",
"./pages/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {
colors: {
primary: {
50: '#eff6ff',
500: '#3b82f6',
900: '#1e3a8a',
},
},
fontFamily: {
sans: ['Inter', 'sans-serif'],
},
},
},
plugins: [],
}
```
Add Tailwind directives to your CSS:
```css
@tailwind base;
@tailwind components;
@tailwind utilities;
```
## Building Common UI Components
### 1. Modern Card Component
```html
<div class="max-w-sm mx-auto bg-white rounded-xl shadow-lg overflow-hidden md:max-w-2xl">
<div class="md:flex">
<div class="md:shrink-0">
<img class="h-48 w-full object-cover md:h-full md:w-48"
src="/api/placeholder/300/200"
alt="Modern architecture">
</div>
<div class="p-8">
<div class="uppercase tracking-wide text-sm text-indigo-500 font-semibold">
Company retreats
</div>
<a href="#" class="block mt-1 text-lg leading-tight font-medium text-black hover:underline">
Incredible accommodation for your team
</a>
<p class="mt-2 text-slate-500">
Looking to take your team away on a retreat to enjoy awesome food and take in some sunshine?
We have a list of places to do just that.
</p>
</div>
</div>
</div>
```
### 2. Responsive Navigation Bar
```html
<nav class="bg-white shadow-lg">
<div class="max-w-7xl mx-auto px-4">
<div class="flex justify-between h-16">
<div class="flex items-center">
<div class="flex-shrink-0">
<img class="h-8 w-8" src="/logo.svg" alt="Logo">
</div>
<div class="hidden md:block">
<div class="ml-10 flex items-baseline space-x-4">
<a href="#" class="text-gray-900 hover:text-gray-700 px-3 py-2 rounded-md text-sm font-medium">
Home
</a>
<a href="#" class="text-gray-500 hover:text-gray-700 px-3 py-2 rounded-md text-sm font-medium">
About
</a>
<a href="#" class="text-gray-500 hover:text-gray-700 px-3 py-2 rounded-md text-sm font-medium">
Services
</a>
<a href="#" class="text-gray-500 hover:text-gray-700 px-3 py-2 rounded-md text-sm font-medium">
Contact
</a>
</div>
</div>
</div>
<div class="hidden md:block">
<div class="ml-4 flex items-center md:ml-6">
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Get Started
</button>
</div>
</div>
<div class="md:hidden">
<button class="text-gray-500 hover:text-gray-600 focus:outline-none focus:text-gray-600">
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
</div>
</div>
</nav>
```
### 3. Modern Button Variants
```html
<!-- Primary Button -->
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded transition duration-300 ease-in-out transform hover:scale-105">
Primary Action
</button>
<!-- Secondary Button -->
<button class="bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded transition duration-300">
Secondary Action
</button>
<!-- Gradient Button -->
<button class="bg-gradient-to-r from-purple-400 via-pink-500 to-red-500 hover:from-purple-600 hover:via-pink-600 hover:to-red-600 text-white font-bold py-3 px-6 rounded-full transition duration-300 ease-in-out transform hover:scale-105 shadow-lg">
Gradient Magic
</button>
<!-- Glass Morphism Button -->
<button class="backdrop-blur-sm bg-white/30 border border-white/20 rounded-xl px-6 py-3 text-white font-medium hover:bg-white/40 transition duration-300">
Glass Effect
</button>
```
## Advanced Layout Patterns
### 1. CSS Grid Dashboard
```html
<div class="min-h-screen bg-gray-100 p-4">
<div class="grid grid-cols-1 md:grid-cols-4 lg:grid-cols-6 gap-4 h-screen">
<!-- Sidebar -->
<aside class="bg-white rounded-lg shadow-md p-6 md:col-span-1 lg:col-span-1">
<h2 class="text-xl font-bold mb-4">Navigation</h2>
<nav class="space-y-2">
<a href="#" class="block p-2 rounded hover:bg-gray-100">Dashboard</a>
<a href="#" class="block p-2 rounded hover:bg-gray-100">Analytics</a>
<a href="#" class="block p-2 rounded hover:bg-gray-100">Settings</a>
</nav>
</aside>
<!-- Main Content -->
<main class="md:col-span-3 lg:col-span-4 space-y-4">
<!-- Header -->
<header class="bg-white rounded-lg shadow-md p-6">
<h1 class="text-2xl font-bold">Dashboard</h1>
</header>
<!-- Stats Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div class="bg-white rounded-lg shadow-md p-6">
<h3 class="text-lg font-semibold text-gray-700">Total Users</h3>
<p class="text-3xl font-bold text-blue-600">12,345</p>
</div>
<div class="bg-white rounded-lg shadow-md p-6">
<h3 class="text-lg font-semibold text-gray-700">Revenue</h3>
<p class="text-3xl font-bold text-green-600">$45,678</p>
</div>
<div class="bg-white rounded-lg shadow-md p-6">
<h3 class="text-lg font-semibold text-gray-700">Orders</h3>
<p class="text-3xl font-bold text-purple-600">1,234</p>
</div>
</div>
<!-- Chart Area -->
<div class="bg-white rounded-lg shadow-md p-6">
<h3 class="text-lg font-semibold mb-4">Analytics Chart</h3>
<div class="h-64 bg-gray-100 rounded flex items-center justify-center">
<p class="text-gray-500">Chart placeholder</p>
</div>
</div>
</main>
<!-- Right Sidebar -->
<aside class="bg-white rounded-lg shadow-md p-6 md:col-span-4 lg:col-span-1">
<h2 class="text-xl font-bold mb-4">Recent Activity</h2>
<div class="space-y-3">
<div class="flex items-center space-x-3">
<div class="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center">
<span class="text-white text-sm">JD</span>
</div>
<div>
<p class="text-sm font-medium">John Doe</p>
<p class="text-xs text-gray-500">2 minutes ago</p>
</div>
</div>
</div>
</aside>
</div>
</div>
```
### 2. Responsive Hero Section
```html
<section class="relative bg-gradient-to-r from-blue-600 to-purple-700 text-white">
<div class="absolute inset-0 bg-black opacity-50"></div>
<div class="relative max-w-7xl mx-auto px-4 py-24 sm:py-32">
<div class="text-center">
<h1 class="text-4xl sm:text-5xl md:text-6xl font-bold leading-tight mb-6">
Build Amazing
<span class="block text-transparent bg-clip-text bg-gradient-to-r from-yellow-400 to-pink-500">
User Experiences
</span>
</h1>
<p class="text-xl sm:text-2xl mb-8 max-w-3xl mx-auto leading-relaxed">
Create stunning, responsive websites with Tailwind CSS.
Fast, flexible, and beautifully designed.
</p>
<div class="flex flex-col sm:flex-row gap-4 justify-center">
<button class="bg-white text-blue-600 font-bold py-3 px-8 rounded-full hover:bg-gray-100 transition duration-300 transform hover:scale-105">
Get Started
</button>
<button class="border-2 border-white text-white font-bold py-3 px-8 rounded-full hover:bg-white hover:text-blue-600 transition duration-300">
Learn More
</button>
</div>
</div>
</div>
</section>
```
## Dark Mode Implementation
Tailwind CSS makes implementing dark mode straightforward:
```javascript
// tailwind.config.js
module.exports = {
darkMode: 'class', // or 'media'
// ... rest of config
}
```
```html
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-white min-h-screen">
<header class="bg-gray-100 dark:bg-gray-800 p-4">
<h1 class="text-2xl font-bold">My App</h1>
<button id="theme-toggle" class="mt-2 p-2 rounded bg-gray-200 dark:bg-gray-700">
Toggle Theme
</button>
</header>
<main class="p-4">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6">
<h2 class="text-xl font-semibold mb-4">Content Card</h2>
<p class="text-gray-600 dark:text-gray-300">
This content adapts to both light and dark themes automatically.
</p>
</div>
</main>
</div>
```
```javascript
// Dark mode toggle script
const themeToggle = document.getElementById('theme-toggle');
const html = document.documentElement;
themeToggle.addEventListener('click', () => {
html.classList.toggle('dark');
localStorage.setItem('theme', html.classList.contains('dark') ? 'dark' : 'light');
});
// Load saved theme
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'dark') {
html.classList.add('dark');
}
```
## Performance Optimization
### 1. Purge Unused CSS
Tailwind automatically purges unused styles in production:
```javascript
// tailwind.config.js
module.exports = {
content: [
'./src/**/*.{html,js,ts,jsx,tsx}',
'./pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
],
// This ensures only used classes are included
}
```
### 2. Custom Component Classes
For frequently used patterns, create component classes:
```css
@layer components {
.btn-primary {
@apply bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded transition duration-300;
}
.card {
@apply bg-white rounded-lg shadow-md p-6;
}
.input-field {
@apply w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500;
}
}
```
## Best Practices
### 1. Consistent Spacing Scale
Use Tailwind's spacing scale consistently:
```html
<!-- Good: Consistent spacing -->
<div class="p-4 mb-4">
<h2 class="text-xl mb-2">Title</h2>
<p class="mb-4">Content</p>
<button class="px-4 py-2">Action</button>
</div>
```
### 2. Responsive Design First
Always consider mobile-first design:
```html
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<!-- Mobile: 1 column, Tablet: 2 columns, Desktop: 3 columns -->
</div>
```
### 3. Use Semantic Color Names
Define semantic colors in your config:
```javascript
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
primary: '#3b82f6',
secondary: '#64748b',
success: '#10b981',
warning: '#f59e0b',
danger: '#ef4444',
},
},
},
}
```
## Conclusion
Tailwind CSS empowers developers to build modern, responsive UIs quickly and efficiently. By leveraging its utility-first approach, you can:
- Rapidly prototype and iterate on designs
- Maintain consistent design systems
- Build responsive layouts with ease
- Implement dark mode effortlessly
- Optimize for performance automatically
The key to mastering Tailwind CSS is understanding its utility classes and learning to compose them effectively. Start with simple components and gradually build more complex layouts as you become comfortable with the framework.
Remember to customize your Tailwind configuration to match your design system and always consider performance implications when building for production.
---
*Want to dive deeper? Explore our advanced guide on building design systems with Tailwind CSS and component libraries.*

View File

@@ -0,0 +1,400 @@
---
title: "Scaling Node.js Apps with Docker"
description: "Learn how to containerize Node.js applications using Docker for seamless deployment and scalability in production environments."
image: "https://images.unsplash.com/photo-1605745341112-85968b19335b?w=400&h=250&fit=crop&crop=center"
date: "April 25, 2025"
readTime: "7 min read"
tags: ["Node.js", "Docker", "DevOps"]
slug: "scaling-nodejs-docker"
layout: "../../../layouts/BlogLayout.astro"
---
# Scaling Node.js Apps with Docker
![Docker and Node.js](https://images.unsplash.com/photo-1605745341112-85968b19335b?w=800&h=400&fit=crop&crop=center)
Docker has revolutionized how we deploy and scale applications. When combined with Node.js, it provides a powerful platform for building scalable, maintainable applications. In this guide, we'll explore how to containerize Node.js applications and scale them effectively.
## Why Docker for Node.js?
Docker offers several advantages for Node.js applications:
- **Consistency**: Same environment across development, testing, and production
- **Isolation**: Applications run in isolated containers
- **Scalability**: Easy horizontal scaling with container orchestration
- **Portability**: Run anywhere Docker is supported
- **Resource efficiency**: Lightweight compared to virtual machines
## Creating a Dockerfile for Node.js
Let's start with a basic Node.js application and create a Dockerfile:
```dockerfile
# Use the official Node.js runtime as the base image
FROM node:18-alpine
# Set the working directory inside the container
WORKDIR /usr/src/app
# Copy package.json and package-lock.json (if available)
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy the rest of the application code
COPY . .
# Create a non-root user to run the application
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
# Change ownership of the app directory to the nodejs user
RUN chown -R nextjs:nodejs /usr/src/app
USER nextjs
# Expose the port the app runs on
EXPOSE 3000
# Define the command to run the application
CMD ["node", "server.js"]
```
## Multi-Stage Builds for Optimization
For production applications, use multi-stage builds to reduce image size:
```dockerfile
# Build stage
FROM node:18-alpine AS builder
WORKDIR /usr/src/app
# Copy package files
COPY package*.json ./
# Install all dependencies (including devDependencies)
RUN npm ci
# Copy source code
COPY . .
# Build the application (if you have a build step)
RUN npm run build
# Production stage
FROM node:18-alpine AS production
WORKDIR /usr/src/app
# Copy package files
COPY package*.json ./
# Install only production dependencies
RUN npm ci --only=production && npm cache clean --force
# Copy built application from builder stage
COPY --from=builder /usr/src/app/dist ./dist
COPY --from=builder /usr/src/app/server.js ./
# Create non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
RUN chown -R nextjs:nodejs /usr/src/app
USER nextjs
EXPOSE 3000
CMD ["node", "server.js"]
```
## Docker Compose for Development
Use Docker Compose to manage your development environment:
```yaml
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules
environment:
- NODE_ENV=development
- DATABASE_URL=mongodb://mongo:27017/myapp
depends_on:
- mongo
- redis
command: npm run dev
mongo:
image: mongo:5.0
ports:
- "27017:27017"
volumes:
- mongo_data:/data/db
environment:
- MONGO_INITDB_ROOT_USERNAME=admin
- MONGO_INITDB_ROOT_PASSWORD=password
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
volumes:
mongo_data:
redis_data:
```
## Production Deployment with Docker Swarm
For production scaling, use Docker Swarm or Kubernetes. Here's a Docker Swarm example:
```yaml
# docker-compose.prod.yml
version: '3.8'
services:
app:
image: myapp:latest
deploy:
replicas: 3
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
update_config:
parallelism: 1
delay: 10s
failure_action: rollback
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=mongodb://mongo:27017/myapp
networks:
- app-network
depends_on:
- mongo
mongo:
image: mongo:5.0
deploy:
replicas: 1
restart_policy:
condition: on-failure
volumes:
- mongo_data:/data/db
networks:
- app-network
environment:
- MONGO_INITDB_ROOT_USERNAME=admin
- MONGO_INITDB_ROOT_PASSWORD=password
nginx:
image: nginx:alpine
deploy:
replicas: 1
restart_policy:
condition: on-failure
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
networks:
- app-network
depends_on:
- app
volumes:
mongo_data:
external: true
networks:
app-network:
driver: overlay
```
## Health Checks and Monitoring
Add health checks to your Dockerfile:
```dockerfile
# Add to your Dockerfile
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node healthcheck.js
```
Create a simple health check script:
```javascript
// healthcheck.js
const http = require('http');
const options = {
host: 'localhost',
port: 3000,
path: '/health',
timeout: 2000
};
const request = http.request(options, (res) => {
console.log(`STATUS: ${res.statusCode}`);
if (res.statusCode === 200) {
process.exit(0);
} else {
process.exit(1);
}
});
request.on('error', (err) => {
console.log('ERROR:', err);
process.exit(1);
});
request.end();
```
## Performance Optimization Tips
### 1. Use .dockerignore
Create a `.dockerignore` file to exclude unnecessary files:
```
node_modules
npm-debug.log
.git
.gitignore
README.md
.env
.nyc_output
coverage
.cache
```
### 2. Optimize Layer Caching
Order your Dockerfile commands to maximize cache efficiency:
```dockerfile
# Copy package files first (changes less frequently)
COPY package*.json ./
RUN npm ci --only=production
# Copy source code last (changes more frequently)
COPY . .
```
### 3. Use Alpine Images
Alpine Linux images are much smaller:
```dockerfile
FROM node:18-alpine # ~40MB
# vs
FROM node:18 # ~350MB
```
### 4. Implement Graceful Shutdown
```javascript
// server.js
const express = require('express');
const app = express();
const server = require('http').createServer(app);
// Your app routes here
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.get('/health', (req, res) => {
res.status(200).send('OK');
});
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
// Graceful shutdown
process.on('SIGTERM', () => {
console.log('SIGTERM received, shutting down gracefully');
server.close(() => {
console.log('Process terminated');
process.exit(0);
});
});
process.on('SIGINT', () => {
console.log('SIGINT received, shutting down gracefully');
server.close(() => {
console.log('Process terminated');
process.exit(0);
});
});
```
## Monitoring and Logging
Use structured logging and monitoring:
```javascript
// logger.js
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
transports: [
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.simple()
)
})
]
});
module.exports = logger;
```
## Conclusion
Docker provides a powerful platform for scaling Node.js applications. By following these best practices:
- Use multi-stage builds for optimized production images
- Implement proper health checks and graceful shutdown
- Use Docker Compose for development environments
- Leverage orchestration tools like Docker Swarm or Kubernetes for production
- Monitor and log your applications properly
You'll be able to build robust, scalable Node.js applications that can handle production workloads efficiently.
Remember that containerization is just one part of a scalable architecture. Consider implementing load balancing, caching strategies, and database optimization for complete scalability.
---
*Ready to deploy? Check out our guide on Kubernetes deployment strategies for even more advanced scaling techniques.*

View File

@@ -0,0 +1,577 @@
---
title: "TypeScript Best Practices for Large Projects"
description: "Essential TypeScript patterns and practices for maintaining code quality and developer productivity in enterprise applications."
image: "https://images.unsplash.com/photo-1516321318423-f06f85e504b3?w=400&h=250&fit=crop&crop=center"
date: "March 30, 2025"
readTime: "8 min read"
tags: ["TypeScript", "JavaScript", "Architecture"]
slug: "typescript-best-practices"
layout: "../../../layouts/BlogLayout.astro"
---
# TypeScript Best Practices for Large Projects
![TypeScript Development](https://images.unsplash.com/photo-1516321318423-f06f85e504b3?w=800&h=400&fit=crop&crop=center)
TypeScript has become the de facto standard for building large-scale JavaScript applications. Its static type system helps catch errors early, improves code maintainability, and enhances developer productivity. In this comprehensive guide, we'll explore essential TypeScript best practices for enterprise-level projects.
## Why TypeScript for Large Projects?
TypeScript offers significant advantages for large codebases:
- **Static Type Checking**: Catch errors at compile time rather than runtime
- **Enhanced IDE Support**: Better autocomplete, refactoring, and navigation
- **Self-Documenting Code**: Types serve as inline documentation
- **Safer Refactoring**: Confidence when making changes across large codebases
- **Team Collaboration**: Clear contracts between different parts of the application
## Project Structure and Configuration
### 1. TypeScript Configuration
Start with a robust `tsconfig.json`:
```json
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "node",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"allowJs": false,
"checkJs": false,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src",
"removeComments": true,
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"noImplicitOverride": true,
"allowUnusedLabels": false,
"allowUnreachableCode": false,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@/components/*": ["src/components/*"],
"@/utils/*": ["src/utils/*"],
"@/types/*": ["src/types/*"]
}
},
"include": [
"src/**/*",
"tests/**/*"
],
"exclude": [
"node_modules",
"dist",
"**/*.js"
]
}
```
### 2. Project Structure
Organize your project for scalability:
```
src/
├── components/ # Reusable UI components
│ ├── common/ # Shared components
│ └── feature/ # Feature-specific components
├── hooks/ # Custom React hooks
├── services/ # API and external service integrations
├── stores/ # State management
├── types/ # Type definitions
│ ├── api.ts # API response types
│ ├── common.ts # Common utility types
│ └── domain.ts # Business domain types
├── utils/ # Utility functions
├── constants/ # Application constants
└── __tests__/ # Test files
```
## Type Definition Best Practices
### 1. Use Interfaces for Object Shapes
```typescript
// Good: Use interfaces for object contracts
interface User {
readonly id: string;
name: string;
email: string;
createdAt: Date;
updatedAt: Date;
preferences?: UserPreferences;
}
interface UserPreferences {
theme: 'light' | 'dark';
notifications: boolean;
language: string;
}
// Good: Extend interfaces for related types
interface AdminUser extends User {
permissions: Permission[];
lastLogin: Date;
}
```
### 2. Use Type Aliases for Unions and Complex Types
```typescript
// Good: Use type aliases for unions
type Status = 'pending' | 'approved' | 'rejected' | 'cancelled';
type Theme = 'light' | 'dark' | 'auto';
// Good: Use type aliases for complex computed types
type ApiResponse<T> = {
data: T;
status: number;
message: string;
timestamp: string;
};
// Good: Use mapped types for transformations
type PartialUser = Partial<User>;
type UserKeys = keyof User;
type RequiredUserFields = Required<Pick<User, 'id' | 'name' | 'email'>>;
```
### 3. Create Discriminated Unions for State Management
```typescript
// Good: Discriminated unions for different states
type LoadingState = {
status: 'loading';
progress?: number;
};
type SuccessState = {
status: 'success';
data: User[];
};
type ErrorState = {
status: 'error';
error: string;
retryCount: number;
};
type AsyncState = LoadingState | SuccessState | ErrorState;
// Usage with type guards
function handleState(state: AsyncState) {
switch (state.status) {
case 'loading':
return `Loading... ${state.progress || 0}%`;
case 'success':
return `Loaded ${state.data.length} users`;
case 'error':
return `Error: ${state.error} (Retry: ${state.retryCount})`;
}
}
```
## Advanced Type Patterns
### 1. Generic Constraints and Conditional Types
```typescript
// Generic constraints
interface Identifiable {
id: string;
}
function updateEntity<T extends Identifiable>(
entity: T,
updates: Partial<Omit<T, 'id'>>
): T {
return { ...entity, ...updates };
}
// Conditional types
type ApiEndpoint<T> = T extends 'users'
? '/api/users'
: T extends 'posts'
? '/api/posts'
: never;
// Utility type for API responses
type InferApiResponse<T> = T extends (args: any) => Promise<infer R>
? R
: never;
```
### 2. Template Literal Types
```typescript
// Template literal types for type-safe routing
type Route = '/users' | '/posts' | '/settings';
type DynamicRoute = `/users/${string}` | `/posts/${string}`;
type AllRoutes = Route | DynamicRoute;
// Type-safe event handling
type EventType = 'click' | 'hover' | 'focus';
type ElementType = 'button' | 'input' | 'div';
type EventHandler = `on${Capitalize<EventType>}${Capitalize<ElementType>}`;
// Usage
const handlers: Record<EventHandler, () => void> = {
onClickButton: () => console.log('Button clicked'),
onHoverDiv: () => console.log('Div hovered'),
// TypeScript will enforce all combinations exist
};
```
### 3. Branded Types for Type Safety
```typescript
// Branded types to prevent mixing similar types
type UserId = string & { readonly brand: unique symbol };
type PostId = string & { readonly brand: unique symbol };
type Email = string & { readonly brand: unique symbol };
// Factory functions
function createUserId(id: string): UserId {
// Add validation logic here
if (!id || id.length < 3) {
throw new Error('Invalid user ID');
}
return id as UserId;
}
function createEmail(email: string): Email {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
throw new Error('Invalid email format');
}
return email as Email;
}
// Usage - prevents accidental mixing
function getUser(id: UserId): Promise<User> {
// Implementation
}
function getPost(id: PostId): Promise<Post> {
// Implementation
}
// This would cause a TypeScript error:
// getUser(postId); // Error: PostId is not assignable to UserId
```
## Error Handling Patterns
### 1. Result Type Pattern
```typescript
// Result type for better error handling
type Result<T, E = Error> =
| { success: true; data: T }
| { success: false; error: E };
// Utility functions
function success<T>(data: T): Result<T> {
return { success: true, data };
}
function failure<E>(error: E): Result<never, E> {
return { success: false, error };
}
// Usage in async functions
async function fetchUser(id: UserId): Promise<Result<User, string>> {
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
return failure(`HTTP ${response.status}: ${response.statusText}`);
}
const user = await response.json();
return success(user);
} catch (error) {
return failure(error instanceof Error ? error.message : 'Unknown error');
}
}
// Type-safe error handling
async function handleUserFetch(id: UserId) {
const result = await fetchUser(id);
if (result.success) {
console.log('User:', result.data.name); // TypeScript knows data exists
} else {
console.error('Error:', result.error); // TypeScript knows error exists
}
}
```
### 2. Custom Error Classes
```typescript
// Base error class
abstract class AppError extends Error {
abstract readonly code: string;
abstract readonly statusCode: number;
constructor(message: string, public readonly context?: Record<string, unknown>) {
super(message);
this.name = this.constructor.name;
}
}
// Specific error types
class ValidationError extends AppError {
readonly code = 'VALIDATION_ERROR';
readonly statusCode = 400;
}
class NotFoundError extends AppError {
readonly code = 'NOT_FOUND';
readonly statusCode = 404;
}
class DatabaseError extends AppError {
readonly code = 'DATABASE_ERROR';
readonly statusCode = 500;
}
// Type guard for error handling
function isAppError(error: unknown): error is AppError {
return error instanceof AppError;
}
// Usage
try {
// Some operation
} catch (error) {
if (isAppError(error)) {
console.error(`${error.code}: ${error.message}`, error.context);
} else {
console.error('Unexpected error:', error);
}
}
```
## API Integration Patterns
### 1. Type-Safe API Client
```typescript
// API endpoint definitions
interface ApiEndpoints {
'GET /users': {
response: User[];
};
'GET /users/:id': {
params: { id: UserId };
response: User;
};
'POST /users': {
body: CreateUserRequest;
response: User;
};
'PUT /users/:id': {
params: { id: UserId };
body: UpdateUserRequest;
response: User;
};
}
// Generic API client
class ApiClient {
private baseUrl: string;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
async request<K extends keyof ApiEndpoints>(
endpoint: K,
options?: {
params?: ApiEndpoints[K] extends { params: infer P } ? P : never;
body?: ApiEndpoints[K] extends { body: infer B } ? B : never;
}
): Promise<ApiEndpoints[K] extends { response: infer R } ? R : never> {
// Implementation details
const url = this.buildUrl(endpoint, options?.params);
const response = await fetch(url, {
method: this.getMethod(endpoint),
headers: { 'Content-Type': 'application/json' },
body: options?.body ? JSON.stringify(options.body) : undefined,
});
if (!response.ok) {
throw new Error(`API Error: ${response.status}`);
}
return response.json();
}
private buildUrl(endpoint: string, params?: Record<string, string>): string {
let url = endpoint;
if (params) {
Object.entries(params).forEach(([key, value]) => {
url = url.replace(`:${key}`, value);
});
}
return `${this.baseUrl}${url}`;
}
private getMethod(endpoint: string): string {
return endpoint.split(' ')[0];
}
}
// Usage
const api = new ApiClient('https://api.example.com');
// Type-safe API calls
const users = await api.request('GET /users');
const user = await api.request('GET /users/:id', { params: { id: userId } });
const newUser = await api.request('POST /users', { body: createUserData });
```
## Testing Patterns
### 1. Type-Safe Test Utilities
```typescript
// Test data factories
interface TestDataFactory<T> {
build(overrides?: Partial<T>): T;
buildList(count: number, overrides?: Partial<T>): T[];
}
function createFactory<T>(defaultData: T): TestDataFactory<T> {
return {
build: (overrides = {}) => ({ ...defaultData, ...overrides }),
buildList: (count, overrides = {}) =>
Array.from({ length: count }, () => ({ ...defaultData, ...overrides }))
};
}
// Usage
const userFactory = createFactory<User>({
id: createUserId('test-user-1'),
name: 'Test User',
email: createEmail('test@example.com'),
createdAt: new Date(),
updatedAt: new Date(),
});
// In tests
const testUser = userFactory.build({ name: 'Custom Name' });
const testUsers = userFactory.buildList(5);
```
### 2. Mock Type Utilities
```typescript
// Utility types for mocking
type MockedFunction<T extends (...args: any[]) => any> = jest.MockedFunction<T>;
type MockedClass<T> = jest.Mocked<T>;
// Partial mocking utility
type PartialMock<T> = {
[K in keyof T]?: T[K] extends (...args: any[]) => any
? MockedFunction<T[K]>
: T[K];
};
// Create typed mocks
function createMock<T>(overrides: PartialMock<T> = {}): jest.Mocked<T> {
return overrides as jest.Mocked<T>;
}
// Usage
const mockUserService = createMock<UserService>({
getUser: jest.fn().mockResolvedValue(testUser),
createUser: jest.fn().mockResolvedValue(testUser),
});
```
## Performance Optimization
### 1. Lazy Loading with Types
```typescript
// Lazy component loading
const LazyUserProfile = React.lazy(() =>
import('./UserProfile').then(module => ({ default: module.UserProfile }))
);
// Type-safe dynamic imports
type ComponentModule<T> = {
default: React.ComponentType<T>;
};
async function loadComponent<T>(
importFn: () => Promise<ComponentModule<T>>
): Promise<React.ComponentType<T>> {
const module = await importFn();
return module.default;
}
```
### 2. Optimized Bundle Splitting
```typescript
// Feature-based code splitting
export const UserFeature = {
UserList: React.lazy(() => import('./UserList')),
UserProfile: React.lazy(() => import('./UserProfile')),
UserSettings: React.lazy(() => import('./UserSettings')),
};
// Route-based splitting
export const Routes = {
Home: React.lazy(() => import('../pages/Home')),
Users: React.lazy(() => import('../pages/Users')),
Settings: React.lazy(() => import('../pages/Settings')),
} as const;
```
## Conclusion
Implementing these TypeScript best practices in large projects will:
- **Improve Code Quality**: Catch errors early and enforce consistent patterns
- **Enhance Developer Experience**: Better IDE support and refactoring capabilities
- **Increase Maintainability**: Self-documenting code and clear contracts
- **Boost Team Productivity**: Shared understanding through types
- **Reduce Runtime Errors**: Comprehensive compile-time checking
### Key Takeaways:
1. **Start with strict TypeScript configuration** for maximum type safety
2. **Use discriminated unions** for complex state management
3. **Implement branded types** for domain-specific type safety
4. **Create type-safe API clients** to prevent integration errors
5. **Establish consistent error handling patterns** with Result types
6. **Write comprehensive type definitions** for better code documentation
7. **Use advanced type features** like conditional and template literal types
8. **Implement type-safe testing utilities** for reliable test suites
Remember that TypeScript is a tool to help you write better JavaScript. The goal is not to use every advanced feature, but to use the right features that solve real problems in your codebase.
---
*Ready to level up your TypeScript skills? Explore our advanced guide on building type-safe GraphQL APIs with TypeScript.*

View File

@@ -0,0 +1,22 @@
---
import BlogLayout from '../../../../layouts/BlogLayout.astro';
import type { Lang } from '../../../../i18n/utils';
const lang = (Astro.currentLocale || 'zh') as Lang;
---
<BlogLayout title="博客文章">
<div class="container mx-auto px-4 py-8">
<h1 class="text-4xl font-bold text-center mb-8">所有博客文章</h1>
<p class="text-center text-muted-foreground mb-8">
浏览按分类和主题组织的所有博客文章。
</p>
<!-- 此页面可用于其他博客组织功能 -->
<div class="text-center">
<a href="/zh/blog" class="text-primary hover:underline">
← 返回博客首页
</a>
</div>
</div>
</BlogLayout>

View File

@@ -0,0 +1,476 @@
---
title: "精通 React Hooks深入探索"
description: "探索 React Hooks 在函数组件中管理状态和副作用的强大功能,包含实用示例和最佳实践。"
image: "https://images.unsplash.com/photo-1633356122544-f134324a6cee?w=400&h=250&fit=crop&crop=center"
date: "2025年5月10日"
readTime: "5分钟阅读"
tags: ["React", "JavaScript", "前端"]
slug: "mastering-react-hooks"
layout: "../../../../layouts/BlogLayout.astro"
---
# 精通 React Hooks深入探索
![React Hooks](https://images.unsplash.com/photo-1633356122544-f134324a6cee?w=800&h=400&fit=crop&crop=center)
React Hooks 彻底改变了我们编写 React 组件的方式,让我们能够在函数组件中使用状态和其他 React 特性。在这个全面的指南中,我们将探索最重要的 hooks 并学习如何有效地使用它们。
## 什么是 React Hooks
React Hooks 是让你能够从函数组件中"钩入" React 状态和生命周期特性的函数。它们在 React 16.8 中引入,现在已经成为编写 React 组件的首选方式。
### 主要优势:
- **更简洁的代码**:无需类组件
- **更好的可复用性**:自定义 hooks 允许共享有状态的逻辑
- **更容易测试**:函数组件更容易测试
- **更好的性能**:优化更容易实现
## 基础 React Hooks
### 1. useState Hook
`useState` hook 允许你向函数组件添加状态:
```javascript
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>你点击了 {count} </p>
<button onClick={() => setCount(count + 1)}>
点击我
</button>
</div>
);
}
```
### 2. useEffect Hook
`useEffect` hook 让你在函数组件中执行副作用:
```javascript
import React, { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchUser() {
setLoading(true);
try {
const response = await fetch(`/api/users/${userId}`);
const userData = await response.json();
setUser(userData);
} catch (error) {
console.error('获取用户信息失败:', error);
} finally {
setLoading(false);
}
}
fetchUser();
}, [userId]); // 依赖数组
if (loading) return <div>加载中...</div>;
if (!user) return <div>用户未找到</div>;
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
```
### 3. useContext Hook
`useContext` hook 提供了一种在组件树中传递数据的方式,无需手动逐层传递 props
```javascript
import React, { useContext, createContext } from 'react';
const ThemeContext = createContext();
function App() {
return (
<ThemeContext.Provider value="dark">
<Header />
<Main />
</ThemeContext.Provider>
);
}
function Header() {
const theme = useContext(ThemeContext);
return (
<header className={`header-${theme}`}>
<h1>我的应用</h1>
</header>
);
}
```
## 自定义 Hooks
React Hooks 最强大的特性之一是能够创建自定义 hooks 来封装和复用有状态的逻辑:
```javascript
// 用于 API 调用的自定义 hook
function useApi(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchData() {
try {
setLoading(true);
const response = await fetch(url);
if (!response.ok) {
throw new Error('网络响应不正常');
}
const result = await response.json();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
fetchData();
}, [url]);
return { data, loading, error };
}
// 使用自定义 hook
function ProductList() {
const { data: products, loading, error } = useApi('/api/products');
if (loading) return <div>加载产品中...</div>;
if (error) return <div>错误: {error}</div>;
return (
<ul>
{products.map(product => (
<li key={product.id}>{product.name}</li>
))}
</ul>
);
}
```
## 最佳实践
### 1. 遵循 Hooks 规则
- 只在 React 函数的顶层调用 hooks
- 只在 React 函数(组件或自定义 hooks中调用 hooks
### 2. 性能优化
使用 `useMemo``useCallback` 防止不必要的重新渲染:
```javascript
import React, { useMemo, useCallback } from 'react';
function ExpensiveComponent({ items, onItemClick }) {
// 记忆化昂贵的计算
const expensiveValue = useMemo(() => {
return items.reduce((sum, item) => sum + item.value, 0);
}, [items]);
// 记忆化回调函数
const handleClick = useCallback((id) => {
onItemClick(id);
}, [onItemClick]);
return (
<div>
<p>总计: {expensiveValue}</p>
{items.map(item => (
<button key={item.id} onClick={() => handleClick(item.id)}>
{item.name}
</button>
))}
</div>
);
}
```
### 3. 清理副作用
在 useEffect 中始终清理订阅和定时器:
```javascript
useEffect(() => {
const timer = setInterval(() => {
console.log('定时器触发');
}, 1000);
// 清理函数
return () => {
clearInterval(timer);
};
}, []);
```
## 高级 Hooks 模式
### 1. useReducer 用于复杂状态管理
```javascript
import React, { useReducer } from 'react';
// 定义状态和动作类型
const initialState = {
count: 0,
step: 1,
};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + state.step };
case 'decrement':
return { ...state, count: state.count - state.step };
case 'setStep':
return { ...state, step: action.payload };
case 'reset':
return initialState;
default:
throw new Error('未知的动作类型');
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>计数: {state.count}</p>
<p>步长: {state.step}</p>
<button onClick={() => dispatch({ type: 'increment' })}>
增加
</button>
<button onClick={() => dispatch({ type: 'decrement' })}>
减少
</button>
<input
type="number"
value={state.step}
onChange={(e) => dispatch({
type: 'setStep',
payload: parseInt(e.target.value) || 1
})}
/>
<button onClick={() => dispatch({ type: 'reset' })}>
重置
</button>
</div>
);
}
```
### 2. 自定义 Hook 用于表单处理
```javascript
// 表单处理的自定义 hook
function useForm(initialValues, validationRules) {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState({});
const [touched, setTouched] = useState({});
const handleChange = useCallback((name, value) => {
setValues(prev => ({ ...prev, [name]: value }));
// 实时验证
if (validationRules[name]) {
const error = validationRules[name](value);
setErrors(prev => ({ ...prev, [name]: error }));
}
}, [validationRules]);
const handleBlur = useCallback((name) => {
setTouched(prev => ({ ...prev, [name]: true }));
}, []);
const isValid = useMemo(() => {
return Object.values(errors).every(error => !error);
}, [errors]);
const reset = useCallback(() => {
setValues(initialValues);
setErrors({});
setTouched({});
}, [initialValues]);
return {
values,
errors,
touched,
isValid,
handleChange,
handleBlur,
reset,
};
}
// 使用表单 hook
function LoginForm() {
const validationRules = {
email: (value) => {
if (!value) return '邮箱是必填的';
if (!/\S+@\S+\.\S+/.test(value)) return '邮箱格式不正确';
return null;
},
password: (value) => {
if (!value) return '密码是必填的';
if (value.length < 6) return '密码至少需要6个字符';
return null;
},
};
const {
values,
errors,
touched,
isValid,
handleChange,
handleBlur,
reset,
} = useForm({ email: '', password: '' }, validationRules);
const handleSubmit = (e) => {
e.preventDefault();
if (isValid) {
console.log('提交表单:', values);
reset();
}
};
return (
<form onSubmit={handleSubmit}>
<div>
<input
type="email"
placeholder="邮箱"
value={values.email}
onChange={(e) => handleChange('email', e.target.value)}
onBlur={() => handleBlur('email')}
/>
{touched.email && errors.email && (
<span className="error">{errors.email}</span>
)}
</div>
<div>
<input
type="password"
placeholder="密码"
value={values.password}
onChange={(e) => handleChange('password', e.target.value)}
onBlur={() => handleBlur('password')}
/>
{touched.password && errors.password && (
<span className="error">{errors.password}</span>
)}
</div>
<button type="submit" disabled={!isValid}>
登录
</button>
</form>
);
}
```
## 常见陷阱和解决方案
### 1. 避免无限循环
```javascript
// 错误:会导致无限循环
function BadComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
setCount(count + 1); // 每次渲染都会触发
}); // 缺少依赖数组
return <div>{count}</div>;
}
// 正确:使用依赖数组
function GoodComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setTimeout(() => {
setCount(c => c + 1); // 使用函数式更新
}, 1000);
return () => clearTimeout(timer);
}, []); // 空依赖数组,只在挂载时运行
return <div>{count}</div>;
}
```
### 2. 正确处理异步操作
```javascript
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
let cancelled = false;
async function fetchUser() {
setLoading(true);
try {
const response = await fetch(`/api/users/${userId}`);
const userData = await response.json();
// 检查组件是否仍然挂载
if (!cancelled) {
setUser(userData);
}
} catch (error) {
if (!cancelled) {
console.error('获取用户失败:', error);
}
} finally {
if (!cancelled) {
setLoading(false);
}
}
}
fetchUser();
// 清理函数
return () => {
cancelled = true;
};
}, [userId]);
if (loading) return <div>加载中...</div>;
return user ? <div>{user.name}</div> : <div>用户未找到</div>;
}
```
## 结论
React Hooks 从根本上改变了我们编写 React 应用的方式。它们提供了一种更直观、更强大的方式来管理函数组件中的状态和副作用。通过掌握 `useState``useEffect``useContext` 等 hooks以及学习创建自定义 hooks你将能够编写更可维护和可复用的 React 代码。
记住要遵循 hooks 的规则,在必要时进行性能优化,并始终清理你的副作用。有了这些最佳实践,你将很快成为 React Hooks 专家!
---
*想了解更多关于 React 的内容?查看我们关于高级 React 模式和性能优化的其他文章。*

View File

@@ -0,0 +1,796 @@
---
title: "使用 Tailwind CSS 构建现代 UI"
description: "探索如何使用 Tailwind CSS 创建美观、响应式的用户界面,提升开发效率和设计一致性。"
image: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=400&h=250&fit=crop&crop=center"
date: "2025年4月20日"
readTime: "6分钟阅读"
tags: ["CSS", "Tailwind", "UI/UX"]
slug: "modern-ui-tailwind"
layout: "../../../../layouts/BlogLayout.astro"
---
# 使用 Tailwind CSS 构建现代 UI
![Tailwind CSS 设计](https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=800&h=400&fit=crop&crop=center)
Tailwind CSS 已经彻底改变了我们构建用户界面的方式。作为一个实用优先的 CSS 框架,它提供了低级实用类,让你可以直接在标记中构建完全自定义的设计。在本指南中,我们将探索如何使用 Tailwind CSS 创建现代、响应式的 UI 组件。
## 为什么选择 Tailwind CSS
Tailwind CSS 提供了几个相比传统 CSS 框架的优势:
- **实用优先**:直接在 HTML 中应用样式
- **高度可定制**:通过配置文件完全控制设计系统
- **响应式设计**:内置响应式修饰符
- **性能优化**:自动清除未使用的 CSS
- **开发者体验**:优秀的 IDE 支持和文档
## 安装和设置
让我们从在项目中设置 Tailwind CSS 开始:
```bash
# 使用 npm 安装 Tailwind CSS
npm install -D tailwindcss postcss autoprefixer
# 生成配置文件
npx tailwindcss init -p
```
配置你的 `tailwind.config.js`
```javascript
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{html,js,ts,jsx,tsx,astro}",
"./pages/**/*.{html,js,ts,jsx,tsx,astro}",
"./components/**/*.{html,js,ts,jsx,tsx,astro}",
],
theme: {
extend: {
colors: {
primary: {
50: '#eff6ff',
500: '#3b82f6',
900: '#1e3a8a',
},
secondary: {
50: '#f0fdf4',
500: '#22c55e',
900: '#14532d',
}
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
mono: ['Fira Code', 'monospace'],
},
spacing: {
'18': '4.5rem',
'88': '22rem',
}
},
},
plugins: [
require('@tailwindcss/forms'),
require('@tailwindcss/typography'),
require('@tailwindcss/aspect-ratio'),
],
}
```
在你的主 CSS 文件中添加 Tailwind 指令:
```css
/* src/styles/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* 自定义基础样式 */
@layer base {
html {
font-family: 'Inter', system-ui, sans-serif;
}
h1, h2, h3, h4, h5, h6 {
@apply font-semibold text-gray-900;
}
}
/* 自定义组件样式 */
@layer components {
.btn-primary {
@apply bg-primary-500 hover:bg-primary-600 text-white font-medium py-2 px-4 rounded-lg transition-colors duration-200;
}
.card {
@apply bg-white rounded-xl shadow-lg border border-gray-200 overflow-hidden;
}
}
```
## 构建常见 UI 组件
### 1. 现代卡片组件
```html
<!-- 产品卡片 -->
<div class="group relative bg-white rounded-2xl shadow-lg hover:shadow-xl transition-all duration-300 overflow-hidden border border-gray-100">
<!-- 图片容器 -->
<div class="aspect-w-16 aspect-h-9 bg-gray-200 overflow-hidden">
<img
src="https://images.unsplash.com/photo-1555041469-a586c61ea9bc?w=400&h=250&fit=crop"
alt="产品图片"
class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
>
<!-- 悬浮标签 -->
<div class="absolute top-4 left-4">
<span class="bg-primary-500 text-white text-xs font-medium px-2 py-1 rounded-full">
新品
</span>
</div>
</div>
<!-- 内容区域 -->
<div class="p-6">
<div class="flex items-center justify-between mb-2">
<span class="text-sm text-gray-500 font-medium">科技产品</span>
<div class="flex items-center space-x-1">
<svg class="w-4 h-4 text-yellow-400 fill-current" viewBox="0 0 20 20">
<path d="M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z"/>
</svg>
<span class="text-sm text-gray-600">4.8</span>
</div>
</div>
<h3 class="text-xl font-semibold text-gray-900 mb-2 group-hover:text-primary-600 transition-colors">
智能无线耳机
</h3>
<p class="text-gray-600 text-sm mb-4 line-clamp-2">
体验前所未有的音质和舒适度配备主动降噪技术和长达30小时的电池续航。
</p>
<div class="flex items-center justify-between">
<div class="flex items-baseline space-x-2">
<span class="text-2xl font-bold text-gray-900">¥299</span>
<span class="text-sm text-gray-500 line-through">¥399</span>
</div>
<button class="bg-primary-500 hover:bg-primary-600 text-white font-medium px-4 py-2 rounded-lg transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2">
加入购物车
</button>
</div>
</div>
</div>
```
### 2. 响应式导航栏
```html
<!-- 导航栏 -->
<nav class="bg-white/80 backdrop-blur-md border-b border-gray-200 sticky top-0 z-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center h-16">
<!-- Logo -->
<div class="flex items-center">
<div class="flex-shrink-0">
<img class="h-8 w-auto" src="/logo.svg" alt="Logo">
</div>
<div class="hidden md:block ml-10">
<div class="flex items-baseline space-x-8">
<a href="#" class="text-gray-900 hover:text-primary-600 px-3 py-2 text-sm font-medium transition-colors">
首页
</a>
<a href="#" class="text-gray-600 hover:text-primary-600 px-3 py-2 text-sm font-medium transition-colors">
产品
</a>
<a href="#" class="text-gray-600 hover:text-primary-600 px-3 py-2 text-sm font-medium transition-colors">
解决方案
</a>
<a href="#" class="text-gray-600 hover:text-primary-600 px-3 py-2 text-sm font-medium transition-colors">
关于我们
</a>
</div>
</div>
</div>
<!-- 右侧按钮 -->
<div class="hidden md:block">
<div class="flex items-center space-x-4">
<button class="text-gray-600 hover:text-gray-900 p-2 rounded-lg hover:bg-gray-100 transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
</svg>
</button>
<button class="text-gray-600 hover:text-primary-600 font-medium transition-colors">
登录
</button>
<button class="bg-primary-500 hover:bg-primary-600 text-white font-medium px-4 py-2 rounded-lg transition-colors duration-200">
开始使用
</button>
</div>
</div>
<!-- 移动端菜单按钮 -->
<div class="md:hidden">
<button class="text-gray-600 hover:text-gray-900 p-2 rounded-lg hover:bg-gray-100 transition-colors">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
</button>
</div>
</div>
</div>
<!-- 移动端菜单 -->
<div class="md:hidden border-t border-gray-200 bg-white">
<div class="px-2 pt-2 pb-3 space-y-1">
<a href="#" class="block px-3 py-2 text-base font-medium text-gray-900 hover:text-primary-600 hover:bg-gray-50 rounded-lg transition-colors">
首页
</a>
<a href="#" class="block px-3 py-2 text-base font-medium text-gray-600 hover:text-primary-600 hover:bg-gray-50 rounded-lg transition-colors">
产品
</a>
<a href="#" class="block px-3 py-2 text-base font-medium text-gray-600 hover:text-primary-600 hover:bg-gray-50 rounded-lg transition-colors">
解决方案
</a>
<a href="#" class="block px-3 py-2 text-base font-medium text-gray-600 hover:text-primary-600 hover:bg-gray-50 rounded-lg transition-colors">
关于我们
</a>
</div>
<div class="pt-4 pb-3 border-t border-gray-200">
<div class="px-2 space-y-1">
<button class="block w-full text-left px-3 py-2 text-base font-medium text-gray-600 hover:text-primary-600 hover:bg-gray-50 rounded-lg transition-colors">
登录
</button>
<button class="block w-full bg-primary-500 hover:bg-primary-600 text-white font-medium px-3 py-2 rounded-lg transition-colors duration-200">
开始使用
</button>
</div>
</div>
</div>
</nav>
```
### 3. 现代按钮变体
```html
<!-- 按钮变体集合 -->
<div class="space-y-4">
<!-- 主要按钮 -->
<button class="bg-primary-500 hover:bg-primary-600 active:bg-primary-700 text-white font-medium px-6 py-3 rounded-lg transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 shadow-lg hover:shadow-xl">
主要操作
</button>
<!-- 次要按钮 -->
<button class="bg-white hover:bg-gray-50 active:bg-gray-100 text-gray-900 font-medium px-6 py-3 rounded-lg border border-gray-300 transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 shadow-sm hover:shadow-md">
次要操作
</button>
<!-- 幽灵按钮 -->
<button class="text-primary-600 hover:text-primary-700 hover:bg-primary-50 active:bg-primary-100 font-medium px-6 py-3 rounded-lg transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2">
幽灵按钮
</button>
<!-- 危险按钮 -->
<button class="bg-red-500 hover:bg-red-600 active:bg-red-700 text-white font-medium px-6 py-3 rounded-lg transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 shadow-lg hover:shadow-xl">
删除操作
</button>
<!-- 加载状态按钮 -->
<button class="bg-primary-500 text-white font-medium px-6 py-3 rounded-lg opacity-75 cursor-not-allowed flex items-center space-x-2" disabled>
<svg class="animate-spin h-4 w-4" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<span>处理中...</span>
</button>
</div>
```
## 高级布局模式
### 1. CSS Grid 仪表盘布局
```html
<!-- 仪表盘网格布局 -->
<div class="min-h-screen bg-gray-50 p-6">
<div class="max-w-7xl mx-auto">
<!-- 页面标题 -->
<div class="mb-8">
<h1 class="text-3xl font-bold text-gray-900 mb-2">仪表盘</h1>
<p class="text-gray-600">欢迎回来,这是您的数据概览</p>
</div>
<!-- 网格布局 -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<!-- 统计卡片 -->
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-sm font-medium text-gray-600 mb-1">总用户数</p>
<p class="text-3xl font-bold text-gray-900">12,345</p>
<p class="text-sm text-green-600 mt-1">
<span class="inline-flex items-center">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M5.293 9.707a1 1 0 010-1.414l4-4a1 1 0 011.414 0l4 4a1 1 0 01-1.414 1.414L11 7.414V15a1 1 0 11-2 0V7.414L6.707 9.707a1 1 0 01-1.414 0z" clip-rule="evenodd"></path>
</svg>
+12%
</span>
</p>
</div>
<div class="bg-blue-100 p-3 rounded-lg">
<svg class="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197m13.5-9a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z"></path>
</svg>
</div>
</div>
</div>
<!-- 重复其他统计卡片... -->
</div>
<!-- 主要内容区域 -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- 图表区域 -->
<div class="lg:col-span-2 bg-white rounded-xl shadow-sm border border-gray-200 p-6">
<h2 class="text-lg font-semibold text-gray-900 mb-4">销售趋势</h2>
<div class="h-80 bg-gray-100 rounded-lg flex items-center justify-center">
<p class="text-gray-500">图表占位符</p>
</div>
</div>
<!-- 侧边栏 -->
<div class="space-y-6">
<!-- 最近活动 -->
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
<h3 class="text-lg font-semibold text-gray-900 mb-4">最近活动</h3>
<div class="space-y-4">
<div class="flex items-start space-x-3">
<div class="bg-green-100 p-1 rounded-full">
<div class="w-2 h-2 bg-green-600 rounded-full"></div>
</div>
<div class="flex-1 min-w-0">
<p class="text-sm font-medium text-gray-900">新用户注册</p>
<p class="text-sm text-gray-500">2分钟前</p>
</div>
</div>
<!-- 更多活动项... -->
</div>
</div>
<!-- 快速操作 -->
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
<h3 class="text-lg font-semibold text-gray-900 mb-4">快速操作</h3>
<div class="space-y-3">
<button class="w-full text-left p-3 rounded-lg hover:bg-gray-50 transition-colors">
<div class="flex items-center space-x-3">
<svg class="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
</svg>
<span class="text-sm font-medium text-gray-900">添加新项目</span>
</div>
</button>
<!-- 更多快速操作... -->
</div>
</div>
</div>
</div>
</div>
</div>
```
### 2. 响应式 Hero 部分
```html
<!-- Hero 部分 -->
<section class="relative bg-gradient-to-br from-primary-50 via-white to-secondary-50 overflow-hidden">
<!-- 背景装饰 -->
<div class="absolute inset-0">
<div class="absolute top-0 left-0 w-40 h-40 bg-primary-200 rounded-full mix-blend-multiply filter blur-xl opacity-70 animate-blob"></div>
<div class="absolute top-0 right-0 w-40 h-40 bg-secondary-200 rounded-full mix-blend-multiply filter blur-xl opacity-70 animate-blob animation-delay-2000"></div>
<div class="absolute -bottom-8 left-20 w-40 h-40 bg-pink-200 rounded-full mix-blend-multiply filter blur-xl opacity-70 animate-blob animation-delay-4000"></div>
</div>
<div class="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-24 lg:py-32">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
<!-- 文本内容 -->
<div class="text-center lg:text-left">
<h1 class="text-4xl sm:text-5xl lg:text-6xl font-bold text-gray-900 leading-tight mb-6">
构建
<span class="bg-gradient-to-r from-primary-600 to-secondary-600 bg-clip-text text-transparent">
未来
</span>
的应用
</h1>
<p class="text-xl text-gray-600 mb-8 max-w-2xl mx-auto lg:mx-0">
使用我们强大的工具和服务,快速构建、部署和扩展您的应用程序。体验前所未有的开发效率。
</p>
<div class="flex flex-col sm:flex-row gap-4 justify-center lg:justify-start">
<button class="bg-primary-500 hover:bg-primary-600 text-white font-medium px-8 py-4 rounded-xl transition-all duration-200 shadow-lg hover:shadow-xl transform hover:-translate-y-1">
开始免费试用
</button>
<button class="bg-white hover:bg-gray-50 text-gray-900 font-medium px-8 py-4 rounded-xl border border-gray-300 transition-all duration-200 shadow-sm hover:shadow-md flex items-center justify-center space-x-2">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.828 14.828a4 4 0 01-5.656 0M9 10h1m4 0h1m-6 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<span>观看演示</span>
</button>
</div>
<!-- 社会证明 -->
<div class="mt-12">
<p class="text-sm text-gray-500 mb-4">受到全球 10,000+ 开发者信赖</p>
<div class="flex items-center justify-center lg:justify-start space-x-8 opacity-60">
<!-- 公司 Logo 占位符 -->
<div class="w-20 h-8 bg-gray-300 rounded"></div>
<div class="w-20 h-8 bg-gray-300 rounded"></div>
<div class="w-20 h-8 bg-gray-300 rounded"></div>
<div class="w-20 h-8 bg-gray-300 rounded"></div>
</div>
</div>
</div>
<!-- 图片/插图 -->
<div class="relative">
<div class="relative z-10">
<img
src="https://images.unsplash.com/photo-1551434678-e076c223a692?w=600&h=400&fit=crop"
alt="应用界面"
class="w-full h-auto rounded-2xl shadow-2xl"
>
</div>
<!-- 浮动元素 -->
<div class="absolute -top-4 -right-4 w-24 h-24 bg-primary-500 rounded-2xl shadow-lg transform rotate-12 opacity-80"></div>
<div class="absolute -bottom-4 -left-4 w-32 h-32 bg-secondary-500 rounded-full shadow-lg opacity-60"></div>
</div>
</div>
</div>
</section>
```
## 实现暗模式
Tailwind CSS 使实现暗模式变得简单:
```html
<!-- 暗模式切换器 -->
<button
id="theme-toggle"
class="p-2 rounded-lg bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"
>
<!-- 太阳图标(暗模式时显示) -->
<svg class="w-5 h-5 hidden dark:block" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"></path>
</svg>
<!-- 月亮图标(亮模式时显示) -->
<svg class="w-5 h-5 block dark:hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"></path>
</svg>
</button>
<!-- 暗模式样式示例 -->
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-white p-6 rounded-lg border border-gray-200 dark:border-gray-700">
<h2 class="text-2xl font-bold mb-4">暗模式支持</h2>
<p class="text-gray-600 dark:text-gray-300 mb-4">
这个组件在亮模式和暗模式下都能完美工作。
</p>
<button class="bg-primary-500 hover:bg-primary-600 dark:bg-primary-600 dark:hover:bg-primary-700 text-white font-medium px-4 py-2 rounded-lg transition-colors">
操作按钮
</button>
</div>
```
配置暗模式的 JavaScript
```javascript
// 暗模式切换逻辑
const themeToggle = document.getElementById('theme-toggle');
const html = document.documentElement;
// 检查本地存储或系统偏好
const currentTheme = localStorage.getItem('theme') ||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
// 应用主题
if (currentTheme === 'dark') {
html.classList.add('dark');
}
// 切换主题
themeToggle.addEventListener('click', () => {
html.classList.toggle('dark');
const newTheme = html.classList.contains('dark') ? 'dark' : 'light';
localStorage.setItem('theme', newTheme);
});
```
## 性能优化
### 1. 清除未使用的 CSS
Tailwind 自动清除未使用的样式,但你可以优化配置:
```javascript
// tailwind.config.js
module.exports = {
content: [
'./src/**/*.{html,js,ts,jsx,tsx,astro}',
// 确保包含所有可能使用 Tailwind 类的文件
],
// 保护动态生成的类名
safelist: [
'bg-red-500',
'bg-green-500',
'bg-blue-500',
// 或使用模式
{
pattern: /bg-(red|green|blue)-(100|200|300|400|500|600|700|800|900)/,
},
],
}
```
### 2. 创建自定义组件类
```css
/* 在 @layer components 中定义可重用的组件 */
@layer components {
.btn {
@apply font-medium py-2 px-4 rounded-lg transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2;
}
.btn-primary {
@apply btn bg-primary-500 hover:bg-primary-600 text-white focus:ring-primary-500;
}
.btn-secondary {
@apply btn bg-white hover:bg-gray-50 text-gray-900 border border-gray-300 focus:ring-primary-500;
}
.card {
@apply bg-white dark:bg-gray-800 rounded-xl shadow-lg border border-gray-200 dark:border-gray-700 overflow-hidden;
}
.input {
@apply w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent bg-white dark:bg-gray-700 text-gray-900 dark:text-white;
}
}
```
## 最佳实践
### 1. 一致的间距系统
```html
<!-- 使用一致的间距比例 -->
<div class="space-y-4"> <!-- 4 = 1rem -->
<div class="p-6"> <!-- 6 = 1.5rem -->
<h2 class="mb-4"> <!-- 4 = 1rem -->
<p class="mb-8"> <!-- 8 = 2rem -->
</div>
</div>
<!-- 推荐的间距比例2, 4, 6, 8, 12, 16, 20, 24 -->
```
### 2. 响应式优先设计
```html
<!-- 从移动端开始,然后添加更大屏幕的样式 -->
<div class="
grid
grid-cols-1
sm:grid-cols-2
lg:grid-cols-3
xl:grid-cols-4
gap-4
sm:gap-6
lg:gap-8
">
<!-- 内容 -->
</div>
<!-- 文本大小响应式 -->
<h1 class="text-2xl sm:text-3xl lg:text-4xl xl:text-5xl font-bold">
响应式标题
</h1>
```
### 3. 语义化颜色使用
```html
<!-- 使用语义化的颜色名称 -->
<div class="bg-white text-gray-900"> <!-- 主要内容 -->
<div class="bg-gray-50 text-gray-600"> <!-- 次要内容 -->
<div class="bg-primary-500 text-white"> <!-- 主要操作 -->
<div class="bg-red-500 text-white"> <!-- 危险操作 -->
<div class="bg-green-500 text-white"> <!-- 成功状态 -->
<div class="bg-yellow-500 text-white"> <!-- 警告状态 -->
```
### 4. 可访问性考虑
```html
<!-- 确保足够的颜色对比度 -->
<button class="bg-primary-500 text-white hover:bg-primary-600 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2">
可访问的按钮
</button>
<!-- 为屏幕阅读器提供文本 -->
<button class="p-2 rounded-lg hover:bg-gray-100" aria-label="关闭对话框">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
<!-- 使用适当的焦点样式 -->
<input class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent" type="text">
```
## 与 JavaScript 框架集成
### React 组件示例
```jsx
// Button.jsx
import React from 'react';
import { clsx } from 'clsx';
const Button = ({
children,
variant = 'primary',
size = 'md',
disabled = false,
className = '',
...props
}) => {
const baseClasses = 'font-medium rounded-lg transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2';
const variants = {
primary: 'bg-primary-500 hover:bg-primary-600 text-white focus:ring-primary-500',
secondary: 'bg-white hover:bg-gray-50 text-gray-900 border border-gray-300 focus:ring-primary-500',
danger: 'bg-red-500 hover:bg-red-600 text-white focus:ring-red-500'
};
const sizes = {
sm: 'px-3 py-1.5 text-sm',
md: 'px-4 py-2',
lg: 'px-6 py-3 text-lg'
};
const disabledClasses = disabled ? 'opacity-50 cursor-not-allowed' : '';
return (
<button
className={clsx(
baseClasses,
variants[variant],
sizes[size],
disabledClasses,
className
)}
disabled={disabled}
{...props}
>
{children}
</button>
);
};
export default Button;
```
### Vue 组件示例
```vue
<!-- Card.vue -->
<template>
<div :class="cardClasses">
<div v-if="$slots.header" class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
<slot name="header" />
</div>
<div class="p-6">
<slot />
</div>
<div v-if="$slots.footer" class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800">
<slot name="footer" />
</div>
</div>
</template>
<script>
export default {
name: 'Card',
props: {
shadow: {
type: String,
default: 'md',
validator: value => ['sm', 'md', 'lg', 'xl'].includes(value)
},
rounded: {
type: String,
default: 'lg',
validator: value => ['md', 'lg', 'xl', '2xl'].includes(value)
}
},
computed: {
cardClasses() {
return [
'bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 overflow-hidden',
`shadow-${this.shadow}`,
`rounded-${this.rounded}`
];
}
}
};
</script>
```
## 调试和开发工具
### 1. Tailwind CSS IntelliSense
在 VS Code 中安装 Tailwind CSS IntelliSense 扩展以获得:
- 自动完成
- 语法高亮
- 悬停预览
- CSS 类验证
### 2. 浏览器开发工具
```html
<!-- 添加调试类来可视化布局 -->
<div class="debug-screens"> <!-- 显示当前断点 -->
<div class="border border-red-500"> <!-- 临时边框 -->
<div class="bg-red-100"> <!-- 临时背景色 -->
```
### 3. 配置开发环境
```javascript
// tailwind.config.js - 开发配置
module.exports = {
// 开发模式下禁用清除以便调试
content: process.env.NODE_ENV === 'production'
? ['./src/**/*.{html,js,ts,jsx,tsx,astro}']
: [],
theme: {
extend: {
// 开发时添加调试断点
screens: {
'debug': '1px',
}
}
}
}
```
## 结论
Tailwind CSS 提供了一个强大而灵活的方式来构建现代用户界面。通过遵循这些最佳实践:
- 使用实用优先的方法进行快速原型设计
- 实现一致的设计系统
- 优化性能和可访问性
- 利用响应式设计原则
- 正确实现暗模式支持
你将能够创建美观、高性能且易于维护的用户界面。记住Tailwind CSS 的真正力量在于它的可定制性和与现代开发工作流的无缝集成。
---
*准备深入了解更多?查看我们关于高级 CSS 动画和微交互的指南。*

View File

@@ -0,0 +1,559 @@
---
title: "使用 Docker 扩展 Node.js 应用"
description: "学习如何使用 Docker 容器化 Node.js 应用程序,实现生产环境中的无缝部署和可扩展性。"
image: "https://images.unsplash.com/photo-1605745341112-85968b19335b?w=400&h=250&fit=crop&crop=center"
date: "2025年4月25日"
readTime: "7分钟阅读"
tags: ["Node.js", "Docker", "DevOps"]
slug: "scaling-nodejs-docker"
layout: "../../../../layouts/BlogLayout.astro"
---
# 使用 Docker 扩展 Node.js 应用
![Docker 和 Node.js](https://images.unsplash.com/photo-1605745341112-85968b19335b?w=800&h=400&fit=crop&crop=center)
Docker 彻底改变了我们部署和扩展应用程序的方式。当与 Node.js 结合使用时,它提供了一个强大的平台来构建可扩展、可维护的应用程序。在本指南中,我们将探索如何容器化 Node.js 应用程序并有效地扩展它们。
## 为什么为 Node.js 选择 Docker
Docker 为 Node.js 应用程序提供了几个优势:
- **一致性**:开发、测试和生产环境保持一致
- **隔离性**:应用程序在隔离的容器中运行
- **可扩展性**:通过容器编排轻松实现水平扩展
- **可移植性**:在任何支持 Docker 的地方运行
- **资源效率**:相比虚拟机更轻量级
## 为 Node.js 创建 Dockerfile
让我们从一个基本的 Node.js 应用程序开始,创建一个 Dockerfile
```dockerfile
# 使用官方 Node.js 运行时作为基础镜像
FROM node:18-alpine
# 设置容器内的工作目录
WORKDIR /usr/src/app
# 复制 package.json 和 package-lock.json如果可用
COPY package*.json ./
# 安装依赖
RUN npm ci --only=production
# 复制应用程序代码的其余部分
COPY . .
# 创建非 root 用户来运行应用程序
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
# 将应用目录的所有权更改为 nodejs 用户
RUN chown -R nextjs:nodejs /usr/src/app
USER nextjs
# 暴露应用程序运行的端口
EXPOSE 3000
# 定义运行应用程序的命令
CMD ["node", "server.js"]
```
## 多阶段构建优化
对于生产应用程序,使用多阶段构建来减少镜像大小:
```dockerfile
# 构建阶段
FROM node:18-alpine AS builder
WORKDIR /usr/src/app
# 复制包文件
COPY package*.json ./
# 安装所有依赖(包括 devDependencies
RUN npm ci
# 复制源代码
COPY . .
# 构建应用程序(如果有构建步骤)
RUN npm run build
# 生产阶段
FROM node:18-alpine AS production
WORKDIR /usr/src/app
# 复制包文件
COPY package*.json ./
# 只安装生产依赖
RUN npm ci --only=production && npm cache clean --force
# 从构建阶段复制构建的应用程序
COPY --from=builder /usr/src/app/dist ./dist
COPY --from=builder /usr/src/app/server.js ./
# 创建非 root 用户
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
RUN chown -R nextjs:nodejs /usr/src/app
USER nextjs
EXPOSE 3000
CMD ["node", "server.js"]
```
## 开发环境的 Docker Compose
使用 Docker Compose 管理你的开发环境:
```yaml
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules
environment:
- NODE_ENV=development
- DATABASE_URL=mongodb://mongo:27017/myapp
depends_on:
- mongo
- redis
command: npm run dev
mongo:
image: mongo:5.0
ports:
- "27017:27017"
volumes:
- mongo_data:/data/db
environment:
- MONGO_INITDB_ROOT_USERNAME=admin
- MONGO_INITDB_ROOT_PASSWORD=password
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
volumes:
mongo_data:
redis_data:
```
## 使用 Docker Swarm 进行生产部署
对于生产扩展,使用 Docker Swarm 或 Kubernetes。这里是一个 Docker Swarm 示例:
```yaml
# docker-compose.prod.yml
version: '3.8'
services:
app:
image: myapp:latest
deploy:
replicas: 3
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
update_config:
parallelism: 1
delay: 10s
failure_action: rollback
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=mongodb://mongo:27017/myapp
networks:
- app-network
depends_on:
- mongo
mongo:
image: mongo:5.0
deploy:
replicas: 1
restart_policy:
condition: on-failure
volumes:
- mongo_data:/data/db
networks:
- app-network
environment:
- MONGO_INITDB_ROOT_USERNAME=admin
- MONGO_INITDB_ROOT_PASSWORD=password
nginx:
image: nginx:alpine
deploy:
replicas: 1
restart_policy:
condition: on-failure
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
networks:
- app-network
depends_on:
- app
volumes:
mongo_data:
external: true
networks:
app-network:
driver: overlay
```
## 健康检查和监控
在你的 Dockerfile 中添加健康检查:
```dockerfile
# 添加到你的 Dockerfile
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node healthcheck.js
```
创建一个简单的健康检查脚本:
```javascript
// healthcheck.js
const http = require('http');
const options = {
host: 'localhost',
port: 3000,
path: '/health',
timeout: 2000
};
const request = http.request(options, (res) => {
console.log(`状态: ${res.statusCode}`);
if (res.statusCode === 200) {
process.exit(0);
} else {
process.exit(1);
}
});
request.on('error', (err) => {
console.log('错误:', err);
process.exit(1);
});
request.end();
```
## 性能优化技巧
### 1. 使用 .dockerignore
创建一个 `.dockerignore` 文件来排除不必要的文件:
```
node_modules
npm-debug.log
.git
.gitignore
README.md
.env
.nyc_output
coverage
.cache
```
### 2. 优化层缓存
在你的 Dockerfile 中排序命令以最大化缓存效率:
```dockerfile
# 首先复制包文件(变化频率较低)
COPY package*.json ./
RUN npm ci --only=production
# 最后复制源代码(变化频率较高)
COPY . .
```
### 3. 使用 Alpine 镜像
Alpine Linux 镜像要小得多:
```dockerfile
FROM node:18-alpine # ~40MB
# vs
FROM node:18 # ~350MB
```
### 4. 实现优雅关闭
```javascript
// server.js
const express = require('express');
const app = express();
const server = require('http').createServer(app);
// 你的应用路由
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.get('/health', (req, res) => {
res.status(200).send('OK');
});
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
console.log(`服务器运行在端口 ${PORT}`);
});
// 优雅关闭
process.on('SIGTERM', () => {
console.log('收到 SIGTERM正在优雅关闭');
server.close(() => {
console.log('进程已终止');
process.exit(0);
});
});
process.on('SIGINT', () => {
console.log('收到 SIGINT正在优雅关闭');
server.close(() => {
console.log('进程已终止');
process.exit(0);
});
});
```
## 监控和日志记录
使用结构化日志记录和监控:
```javascript
// logger.js
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
transports: [
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.simple()
)
})
]
});
module.exports = logger;
```
## 安全最佳实践
### 1. 使用非 root 用户
```dockerfile
# 创建专用用户
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
# 切换到非 root 用户
USER nextjs
```
### 2. 扫描漏洞
```bash
# 使用 Docker 安全扫描
docker scan myapp:latest
# 使用 Snyk 扫描
npx snyk test --docker myapp:latest
```
### 3. 使用多阶段构建移除开发依赖
```dockerfile
# 确保生产镜像中没有开发依赖
RUN npm ci --only=production
```
## 容器编排策略
### 1. 负载均衡配置
```nginx
# nginx.conf
upstream app {
server app:3000;
}
server {
listen 80;
location / {
proxy_pass http://app;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /health {
access_log off;
proxy_pass http://app/health;
}
}
```
### 2. 环境变量管理
```yaml
# docker-compose.yml
services:
app:
environment:
- NODE_ENV=${NODE_ENV:-production}
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
- JWT_SECRET=${JWT_SECRET}
env_file:
- .env.production
```
### 3. 数据持久化
```yaml
services:
mongo:
volumes:
- mongo_data:/data/db
- ./mongo-init:/docker-entrypoint-initdb.d
volumes:
mongo_data:
driver: local
driver_opts:
type: none
o: bind
device: /opt/myapp/data
```
## 部署和 CI/CD 集成
### 1. GitHub Actions 工作流
```yaml
# .github/workflows/deploy.yml
name: Build and Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build Docker image
run: docker build -t myapp:${{ github.sha }} .
- name: Run tests
run: docker run --rm myapp:${{ github.sha }} npm test
- name: Push to registry
run: |
echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
docker push myapp:${{ github.sha }}
- name: Deploy to production
run: |
docker service update --image myapp:${{ github.sha }} production_app
```
## 故障排除和调试
### 1. 容器日志
```bash
# 查看容器日志
docker logs -f container_name
# 查看服务日志Swarm
docker service logs -f service_name
```
### 2. 进入运行中的容器
```bash
# 进入容器进行调试
docker exec -it container_name sh
# 检查容器资源使用
docker stats container_name
```
### 3. 网络调试
```bash
# 检查网络连接
docker network ls
docker network inspect network_name
# 测试容器间连接
docker exec container1 ping container2
```
## 结论
Docker 为扩展 Node.js 应用程序提供了一个强大的平台。通过遵循这些最佳实践:
- 使用多阶段构建优化生产镜像
- 实现适当的健康检查和优雅关闭
- 使用 Docker Compose 进行开发环境
- 利用 Docker Swarm 或 Kubernetes 等编排工具进行生产
- 正确监控和记录你的应用程序
你将能够构建强大、可扩展的 Node.js 应用程序,能够高效地处理生产工作负载。
记住,容器化只是可扩展架构的一部分。考虑实现负载均衡、缓存策略和数据库优化以实现完整的可扩展性。
---
*准备部署了吗?查看我们关于 Kubernetes 部署策略的指南,了解更高级的扩展技术。*

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
@import "tailwindcss";
@import "tw-animate-css";
@plugin "@tailwindcss/typography";
/* Blog List Component Styles */
.line-clamp-3 {