博客 V2.0 更新说明 - 从 MDL 模板到完整博客系统

Kernel 2026-06-07
bookmark bookmark
share share

版本概述

经过多个迭代周期的开发,本博客完成了从基础 MDL 模板到完整博客系统的全面重构。本文记录了所有新增功能与修复的问题,并附带关键代码片段和界面截图。

1. CDN 迁移到本地资源

原博客依赖 code.getmdl.io CDN 加载 MDL 框架资源,但该 CDN 已停止服务,导致页面无法正常渲染。我们将所有 MDL 资源本地化到 lib/mdl/ 目录,彻底解决了外部依赖不可控的问题。

HTML: CDN vs 本地引用对比
<!-- 旧:CDN 引用(已失效) -->
<link rel="stylesheet" href="https://code.getmdl.io/1.3.0/material.indigo-pink.min.css">
<script defer src="https://code.getmdl.io/1.3.0/material.min.js"></script>

<!-- 新:本地引用(稳定可靠) -->
<link rel="stylesheet" href="lib/mdl/material-themes-grey-orange.css">
<script src="lib/mdl/material.min.js"></script>

同时将 MDL 默认的 Indigo-Pink 主题替换为 Grey-Orange 主题,视觉上更专业沉稳。

2. 博客管理后台

从零构建了完整的 Flask 后端 + 管理界面,支持文章的增删改查、置顶、封面图片上传、背景图管理等功能。管理后台遵循 Google Material Design 1 规范设计。

管理后台界面

Python: Flask 文章创建 API
@app.route('/api/posts', methods=['POST'])
def api_create_post():
    data = load_data()
    body = request.json

    post_id = body.get('id') or body.get('title', '').lower().strip()
    post_id = re.sub(r'[^a-z0-9\u4e00-\u9fff]+', '-', post_id).strip('-')

    post = {
        'id': post_id,
        'title': body.get('title', 'No Title'),
        'date': body.get('date', datetime.now().strftime('%Y-%m-%d')),
        'summary': body.get('summary', ''),
        'content': body.get('content', ''),
        'cardFormat': body.get('cardFormat', 'standard'),
        'coverImage': body.get('coverImage', ''),
    }
    data['posts'].append(post)
    save_data(data)
    generate_all()
    return jsonify({'ok': True, 'post': post})

3. 代码高亮系统(Prism + click-to-copy)

集成了 Prism.js 代码高亮引擎,支持 17 种编程语言。自定义了 code-with-text 格式:灰色圆角容器 + 语言标签 + 语法高亮 + 点击复制功能。同时支持两种代码块语法:

  • [code:lang|label]...[/code] — 旧格式,向后兼容
  • ```lang ... ``` — Markdown fenced code blocks,新推荐格式
Python: fenced code block 自定义处理
def markdown_to_html(content):
    def replace_fenced_code(match):
        lang = match.group(1) or ''
        code = match.group(2)
        code_escaped = (code
            .replace('&', '&amp;')
            .replace('<', '&lt;')
            .replace('>', '&gt;')
            .replace('"', '&quot;'))
        prism_lang = lang_map.get(lang.lower(), lang.lower()) if lang else 'markup'
        label = lang if lang else 'code'
        return f'<div class="code-with-text">{label}\n' \
               f'<pre class="language-{prism_lang}">' \
               f'<code class="language-{prism_lang}">{code_escaped}</code></pre>\n' \
               f'</div>'

    fenced_pattern = r'```(\w*)\r?\n([\s\S]*?)\r?\n```'
    content = re.sub(fenced_pattern, replace_fenced_code, content)

    md = markdown.Markdown(extensions=['tables', 'nl2br'], output_format='html5')
    return md.convert(content)

代码高亮效果

4. 标准卡片 vs 引用卡片

区分了标准文章卡片引用卡片两种格式。引用卡片用于展示名言警句,不会生成详情页,不参与文章导航,仅在首页展示。

Python: 生成主页时过滤引用卡片
# 只为标准格式文章生成详情页
nav_posts = [p for p in sorted_posts if p.get('cardFormat', 'standard') != 'quote']

# 引用卡片不生成链接
if post.get('cardFormat', 'standard') == 'quote':
    card_class = 'card-quote'
    # 不添加 <a> 链接
else:
    card_class = 'card-article'
    # 添加文章详情页链接

5. 动态文章导航

文章详情页底部增加了"前一篇/后一篇"导航,自动按照发布日期排序。最新文章左侧显示"返回首页",最旧文章右侧显示"返回首页"。鼠标悬停时 tooltip 显示相邻文章标题和日期。

Python: 导航逻辑
# all_posts sorted by date descending: [newest, ..., current, ..., oldest]
if post_index == 0:
    newer_link = '../../index.html'
    newer_text = '返回首页'
    newer_title = '返回博客首页'
else:
    newer_post = all_posts[post_index - 1]
    newer_link = f"{newer_post['id']}.html"
    newer_text = '前一篇'
    newer_title = f"{newer_post['title']} ({newer_post['date']})"

6. Markdown 编辑器 + 草稿系统

集成了 EasyMDE Markdown 编辑器,支持侧边实时预览、工具栏快捷操作。新增草稿系统,可以保存未完成的文章,稍后继续编辑。

Python: 草稿 CRUD API
@app.route('/api/drafts', methods=['POST'])
def api_save_draft():
    data = load_data()
    body = request.json
    draft = {
        'id': body.get('id', str(uuid.uuid4())[:8]),
        'title': body.get('title', ''),
        'content': body.get('content', ''),
        'savedAt': datetime.now().isoformat()
    }
    if 'drafts' not in data:
        data['drafts'] = []
    data['drafts'].append(draft)
    save_data(data)
    return jsonify({'ok': True, 'draft': draft})

7. 服务端预览(1:1 匹配发布页面)

预览功能通过服务端渲染,使用与实际发布页面完全相同的模板和 Markdown 处理流程,确保"所见即所得"。关键修复:预览页面的 URL 为 /api/preview,所有静态资源路径必须使用绝对路径(以 / 开头),否则浏览器会将相对路径错误解析为 /api/lib/mdl/...

预览效果

HTML: 绝对路径 vs 相对路径
<!-- Wrong: relative path resolved as /api/styles.css -->
<link rel="stylesheet" href="styles.css">

<!-- Correct: absolute path always resolves correctly -->
<link rel="stylesheet" href="/styles.css">

8. CSS 类名重构

将 MDL 模板原始的 demo-* 类名和随意命名(如 on-the-road-againamazingchroma)统一为语义化名称:

  • demo-blogblog
  • demo-blog--blogpostblog--article
  • demo-backback-btn
  • demo-nav / demo-nav__buttonarticle-nav / article-nav__btn
  • 封面类名 → card-article / card-quote / sidebar-card

同时从数据模型中移除了 coverClass 字段,简化为 cardFormat(standard/quote)+ coverImage 两个字段。

9. 其他修复

返回按钮白色修复:MDL 的 mdl-button--colored 会应用 accent 色而非白色,改用 mdl-color--white + mdl-color-text--grey-900 组合实现白底灰字的返回按钮。

无封面卡片背景色:移除默认封面图片(images/road.jpg),未上传封面的卡片显示 #37474f(blue-grey 800)实色背景,而非 MDL 主题的 mdl-color--primary-dark(灰色,不美观)。

文件上传按钮修复:将 <button onclick="document.getElementById().click()"> 改为 <label for="fileInput">,解决跨浏览器兼容性问题,同时修复了 flex 容器中 label 按钮文字不居中的 bug。

编辑提示栏优化:解决长标题文章导致"取消"按钮换行的问题,使用 text-overflow: ellipsis 截断标题文本,"取消"按钮设置 white-space: nowrap; flex-shrink: 0; 确保始终单行显示。

CSS: edit-notice layout fix
/* Before: justify-content causes button to wrap */
.edit-notice {
    display: flex;
    justify-content: space-between;
}

/* After: title truncates, button never shrinks */
.edit-notice {
    display: flex;
    align-items: center;
    gap: 12px;
}
.edit-notice span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
    flex: 1;
}
.edit-notice .md-btn {
    white-space: nowrap;
    flex-shrink: 0;
}

10. 博客背景图管理

管理后台新增背景图上传/删除功能,自定义背景图存储在 blog/backgrounds/ 目录,通过 CSS body::before 伪元素覆盖默认山景图。

博客首页(自定义背景图)

总结

本次重构涉及以下核心改动:

  • CDN → 本地资源迁移 + 主题切换
  • Flask 管理后端(文章 CRUD、封面上传、背景图管理、草稿系统)
  • Prism 代码高亮 + code-with-text 格式 + click-to-copy
  • 标准/引用卡片分离(引用不生成详情页)
  • 动态文章导航(前一篇/后一篇 + tooltip)
  • EasyMDE Markdown 编辑器 + 服务端 1:1 预览
  • CSS 类名全面语义化重构
  • 多项 UI bug 修复(按钮居中、换行、路径、颜色)

如果你也在用 MDL 搭建博客,希望这些实践能给你一些参考。有问题欢迎留言讨论!