前言
在當今數字時代,擁有個人博客和高效的任務管理工具已成為開發者展示自我和提升生產力的標配。本文將帶你從零開始,通過純前端技術實現一個兼具個人博客靜態頁面和TodoList任務管理功能的綜合應用。無論你是前端新手還是希望鞏固基礎的中級開發者,這個項目都將為你提供寶貴的實戰經驗。
一、項目概述與設計
1.1 為什么選擇這個組合項目?
博客頁面和TodoList看似是兩個獨立的功能,但它們的組合能帶來以下優勢:
-
展示與實用結合:博客展示你的技術思考,TodoList管理你的創作任務
-
技術覆蓋面廣:涵蓋HTML結構設計、CSS布局美化、JavaScript交互邏輯
-
可擴展性強:為后續添加后端功能(如用戶認證、數據持久化)奠定基礎
1.2 技術選型
我們選擇純前端實現方案,確保項目輕量且易于部署:
-
核心三件套:HTML5 + CSS3 + JavaScript (ES6+)
-
CSS框架:使用Tailwind CSS實現快速樣式開發(可選)
-
圖標庫:Font Awesome或Remix Icon
-
部署方案:GitHub Pages/Vercel/Netlify
二、博客靜態頁面開發
2.1 HTML結構設計
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>技術博客 | 開發者姓名</title><link rel="stylesheet" href="styles.css">
</head>
<body><header class="blog-header"><nav><div class="logo">我的技術博客</div><ul class="nav-links"><li><a href="#home">首頁</a></li><li><a href="#articles">文章</a></li><li><a href="#projects">項目</a></li><li><a href="#about">關于我</a></li><li><a href="#todo">TodoList</a></li></ul></nav></header><main class="blog-container"><section id="home" class="hero-section"><h1>歡迎來到我的技術博客</h1><p>分享前端開發、算法設計與技術思考</p></section><section id="articles" class="articles-section"><h2>最新文章</h2><div class="article-card"><h3>React Hooks深度解析</h3><p class="meta">發布于2023年5月15日 · 8分鐘閱讀</p><p>本文將深入探討React Hooks的工作原理和最佳實踐...</p><a href="#" class="read-more">閱讀全文</a></div><!-- 更多文章卡片 --></section><section id="projects" class="projects-section"><!-- 項目展示區 --></section></main><footer class="blog-footer"><p>? 2023 我的技術博客. 保留所有權利.</p></footer><script src="script.js"></script>
</body>
</html>
2.2 CSS樣式美化
/* 基礎樣式 */
:root {--primary-color: #3498db;--secondary-color: #2ecc71;--dark-color: #2c3e50;--light-color: #ecf0f1;--danger-color: #e74c3c;
}body {font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;line-height: 1.6;color: #333;margin: 0;padding: 0;
}.blog-header {background-color: var(--dark-color);color: white;padding: 1rem 2rem;position: sticky;top: 0;z-index: 100;
}nav {display: flex;justify-content: space-between;align-items: center;
}.logo {font-size: 1.5rem;font-weight: bold;
}.nav-links {display: flex;list-style: none;gap: 2rem;
}.nav-links a {color: white;text-decoration: none;transition: color 0.3s;
}.nav-links a:hover {color: var(--primary-color);
}/* 響應式設計 */
@media (max-width: 768px) {nav {flex-direction: column;}.nav-links {margin-top: 1rem;gap: 1rem;}
}/* 文章卡片樣式 */
.article-card {background: white;border-radius: 8px;box-shadow: 0 2px 5px rgba(0,0,0,0.1);padding: 1.5rem;margin-bottom: 1.5rem;transition: transform 0.3s, box-shadow 0.3s;
}.article-card:hover {transform: translateY(-5px);box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}.meta {color: #7f8c8d;font-size: 0.9rem;
}.read-more {display: inline-block;color: var(--primary-color);text-decoration: none;font-weight: bold;margin-top: 0.5rem;
}.read-more:hover {text-decoration: underline;
}
三、TodoList應用實現
3.1 HTML結構
在博客頁面中添加TodoList部分:
<section id="todo" class="todo-section"><h2>任務管理</h2><div class="todo-container"><div class="todo-input"><input type="text" id="todoInput" placeholder="添加新任務..."><button id="addTodoBtn">添加</button></div><div class="todo-filters"><button class="filter-btn active" data-filter="all">全部</button><button class="filter-btn" data-filter="active">待完成</button><button class="filter-btn" data-filter="completed">已完成</button></div><ul id="todoList" class="todo-list"><!-- 任務將通過JavaScript動態添加 --></ul><div class="todo-stats"><span id="remainingCount">0</span> 個任務待完成<button id="clearCompleted" class="clear-btn">清除已完成</button></div></div>
</section>
3.2 JavaScript功能實現
document.addEventListener('DOMContentLoaded', function() {// DOM元素const todoInput = document.getElementById('todoInput');const addTodoBtn = document.getElementById('addTodoBtn');const todoList = document.getElementById('todoList');const filterBtns = document.querySelectorAll('.filter-btn');const remainingCount = document.getElementById('remainingCount');const clearCompletedBtn = document.getElementById('clearCompleted');// 狀態管理let todos = JSON.parse(localStorage.getItem('todos')) || [];let currentFilter = 'all';// 初始化renderTodoList();updateRemainingCount();// 事件監聽addTodoBtn.addEventListener('click', addTodo);todoInput.addEventListener('keypress', function(e) {if (e.key === 'Enter') addTodo();});clearCompletedBtn.addEventListener('click', clearCompleted);filterBtns.forEach(btn => {btn.addEventListener('click', function() {filterBtns.forEach(b => b.classList.remove('active'));this.classList.add('active');currentFilter = this.dataset.filter;renderTodoList();});});// 功能函數function addTodo() {const text = todoInput.value.trim();if (text) {const newTodo = {id: Date.now(),text,completed: false,createdAt: new Date().toISOString()};todos.unshift(newTodo);saveTodos();renderTodoList();updateRemainingCount();todoInput.value = '';}}function renderTodoList() {todoList.innerHTML = '';const filteredTodos = todos.filter(todo => {if (currentFilter === 'all') return true;if (currentFilter === 'active') return !todo.completed;if (currentFilter === 'completed') return todo.completed;return true;});if (filteredTodos.length === 0) {todoList.innerHTML = '<li class="empty-message">暫無任務</li>';return;}filteredTodos.forEach(todo => {const li = document.createElement('li');li.className = `todo-item ${todo.completed ? 'completed' : ''}`;li.dataset.id = todo.id;li.innerHTML = `<input type="checkbox" ${todo.completed ? 'checked' : ''}><span class="todo-text">${todo.text}</span><button class="delete-btn">×</button><small class="todo-date">${formatDate(todo.createdAt)}</small>`;const checkbox = li.querySelector('input[type="checkbox"]');const deleteBtn = li.querySelector('.delete-btn');checkbox.addEventListener('change', function() {toggleTodoComplete(todo.id);});deleteBtn.addEventListener('click', function() {deleteTodo(todo.id);});todoList.appendChild(li);});}function toggleTodoComplete(id) {todos = todos.map(todo => todo.id === id ? {...todo, completed: !todo.completed} : todo);saveTodos();renderTodoList();updateRemainingCount();}function deleteTodo(id) {todos = todos.filter(todo => todo.id !== id);saveTodos();renderTodoList();updateRemainingCount();}function clearCompleted() {todos = todos.filter(todo => !todo.completed);saveTodos();renderTodoList();}function updateRemainingCount() {const count = todos.filter(todo => !todo.completed).length;remainingCount.textContent = count;}function saveTodos() {localStorage.setItem('todos', JSON.stringify(todos));}function formatDate(dateString) {const options = { year: 'numeric', month: 'short', day: 'numeric' };return new Date(dateString).toLocaleDateString('zh-CN', options);}
});
3.3 TodoList樣式補充
/* TodoList 樣式 */
.todo-section {background-color: #f8f9fa;padding: 2rem;border-radius: 8px;margin-top: 2rem;
}.todo-container {max-width: 600px;margin: 0 auto;background: white;padding: 1.5rem;border-radius: 8px;box-shadow: 0 2px 10px rgba(0,0,0,0.05);
}.todo-input {display: flex;margin-bottom: 1rem;gap: 0.5rem;
}.todo-input input {flex: 1;padding: 0.75rem;border: 1px solid #ddd;border-radius: 4px;font-size: 1rem;
}.todo-input button {padding: 0.75rem 1.5rem;background-color: var(--primary-color);color: white;border: none;border-radius: 4px;cursor: pointer;transition: background-color 0.3s;
}.todo-input button:hover {background-color: #2980b9;
}.todo-filters {display: flex;gap: 0.5rem;margin-bottom: 1rem;
}.filter-btn {padding: 0.5rem 1rem;background: none;border: 1px solid #ddd;border-radius: 4px;cursor: pointer;transition: all 0.3s;
}.filter-btn.active {background-color: var(--primary-color);color: white;border-color: var(--primary-color);
}.todo-list {list-style: none;padding: 0;margin: 0;
}.todo-item {display: flex;align-items: center;padding: 0.75rem;border-bottom: 1px solid #eee;gap: 0.75rem;
}.todo-item.completed .todo-text {text-decoration: line-through;color: #95a5a6;
}.todo-item input[type="checkbox"] {cursor: pointer;
}.todo-text {flex: 1;
}.delete-btn {background: none;border: none;color: var(--danger-color);font-size: 1.25rem;cursor: pointer;opacity: 0.7;transition: opacity 0.3s;
}.delete-btn:hover {opacity: 1;
}.todo-date {color: #95a5a6;font-size: 0.8rem;
}.empty-message {text-align: center;color: #95a5a6;padding: 1rem;
}.todo-stats {display: flex;justify-content: space-between;align-items: center;margin-top: 1rem;color: #7f8c8d;font-size: 0.9rem;
}.clear-btn {background: none;border: none;color: var(--danger-color);cursor: pointer;font-size: 0.9rem;
}.clear-btn:hover {text-decoration: underline;
}
四、項目優化與高級功能
4.1 性能優化建議
-
圖片懶加載:對博客中的圖片實現懶加載
<img src="placeholder.jpg" data-src="actual-image.jpg" class="lazy-load">
// 實現懶加載
const lazyImages = document.querySelectorAll('.lazy-load');const imageObserver = new IntersectionObserver((entries, observer) => {entries.forEach(entry => {if (entry.isIntersecting) {const img = entry.target;img.src = img.dataset.src;img.classList.remove('lazy-load');observer.unobserve(img);}});
});lazyImages.forEach(img => imageObserver.observe(img));
? ? 2. 防抖處理:對搜索功能或頻繁觸發的事件添加防抖
function debounce(func, delay) {let timeoutId;return function(...args) {clearTimeout(timeoutId);timeoutId = setTimeout(() => {func.apply(this, args);}, delay);};
}// 使用示例
searchInput.addEventListener('input', debounce(function() {// 搜索邏輯
}, 300));
4.2 可添加的高級功能
-
Markdown支持:讓博客支持Markdown格式
// 使用marked.js庫
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>// 轉換Markdown為HTML
document.getElementById('markdown-content').innerHTML = marked.parse(markdownText);
? ?2. 主題切換:實現暗黑/明亮模式切換
/* 在:root中添加CSS變量 */
:root {--bg-color: #ffffff;--text-color: #333333;/* 其他變量 */
}/* 暗黑模式 */
[data-theme="dark"] {--bg-color: #1a1a1a;--text-color: #f0f0f0;/* 其他變量 */
}
// 切換主題
function toggleTheme() {const currentTheme = document.documentElement.getAttribute('data-theme');const newTheme = currentTheme === 'dark' ? 'light' : 'dark';document.documentElement.setAttribute('data-theme', newTheme);localStorage.setItem('theme', newTheme);
}// 初始化主題
const savedTheme = localStorage.getItem('theme') || 'light';
document.documentElement.setAttribute('data-theme', savedTheme);
? 3. PWA支持:讓應用可離線使用
-
創建manifest.json文件
-
注冊Service Worker
五、項目部署指南
5.1 GitHub Pages部署
-
在GitHub創建新倉庫
-
將項目代碼推送到倉庫
-
進入倉庫Settings > Pages
-
選擇部署分支(通常是main或master)
-
等待幾分鐘,訪問提供的URL
5.2 Vercel部署(更推薦)
-
注冊Vercel賬號(可使用GitHub賬號登錄)
-
點擊"New Project"
-
導入你的GitHub倉庫
-
配置項目(保持默認即可)
-
點擊"Deploy"
-
部署完成后會自動獲得一個vercel.app的域名
六、總結與擴展方向
通過本項目,你已經掌握了:
-
響應式博客頁面的設計與實現
-
功能完整的TodoList應用開發
-
本地存儲(localStorage)的使用
-
前端狀態管理的基本概念
-
項目部署的基本流程
擴展方向建議:
-
添加后端支持:使用Node.js + Express或Python Flask為項目添加后端API
-
數據庫集成:使用MongoDB或Firebase存儲博客文章和任務數據
-
用戶認證:實現登錄注冊功能,讓TodoList可以多用戶使用
-
博客管理系統:開發一個簡易的CMS用于管理博客內容
-
評論系統:為博客添加