Jekyll 静态站点完全教程 / 第14章:最佳实践
第14章:最佳实践
14.1 性能优化
构建性能
# 查看构建性能分析
bundle exec jekyll build --profile
# 输出示例:
# Filename | Count | Bytes | Time
# -----------------------------------+-------+--------+------
# _layouts/default.html | 150 | 960000 | 2.1s
# _posts/2025-01-15-post.md | 1 | 5000 | 0.3s
# _includes/header.html | 150 | 120000 | 0.8s
增量构建
# 增量构建(只处理修改的文件)
bundle exec jekyll build --incremental
# 或在 _config.yml 中配置
incremental: true
注意事项:
- 增量构建可能不总是正确更新(特别是涉及依赖时)
- 生产构建建议使用全量构建
- 开发时使用增量构建加快反馈速度
内容优化
| 优化项 | 方法 | 效果 |
|---|---|---|
| 减少 Liquid 循环 | 缓存循环结果 | 减少模板处理时间 |
| 简化 include | 减少嵌套深度 | 降低渲染复杂度 |
| 精简数据文件 | 只加载需要的数据 | 减少内存占用 |
| 排除无用文件 | exclude: 配置 | 减少文件扫描 |
# _config.yml
exclude:
- Gemfile
- Gemfile.lock
- node_modules
- README.md
- vendor
- scripts
- "*.config.js"
- ".github"
14.2 SEO 优化
基础 SEO 配置
# _config.yml
title: "My Blog"
description: "A tech blog about web development"
url: "https://blog.example.com"
author: "Zhang San"
plugins:
- jekyll-seo-tag
- jekyll-sitemap
- jekyll-feed
<!-- _includes/head.html -->
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% seo %}
{% feed_meta %}
<link rel="canonical" href="{{ page.url | absolute_url }}">
</head>
文章 SEO 最佳实践
---
title: "Jekyll SEO 完全指南"
description: "详细介绍如何优化 Jekyll 站点的搜索引擎排名"
date: 2025-01-15
last_modified_at: 2025-02-01
image: /images/posts/jekyll-seo.jpg
keywords: [jekyll, seo, 静态站点]
canonical_url: "https://blog.example.com/jekyll-seo-guide/"
robots: "index, follow"
---
Open Graph 标签
<!-- _includes/og-tags.html -->
<meta property="og:title" content="{{ page.title | default: site.title }}">
<meta property="og:description" content="{{ page.description | default: site.description | strip_html | truncatewords: 30 }}">
<meta property="og:type" content="{% if page.layout == 'post' %}article{% else %}website{% endif %}">
<meta property="og:url" content="{{ page.url | absolute_url }}">
<meta property="og:site_name" content="{{ site.title }}">
{% if page.image %}
<meta property="og:image" content="{{ page.image | absolute_url }}">
{% else %}
<meta property="og:image" content="{{ '/images/default-og.png' | absolute_url }}">
{% endif %}
<meta property="og:locale" content="{{ site.lang | default: 'zh_CN' }}">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{{ page.title | default: site.title }}">
<meta name="twitter:description" content="{{ page.description | default: site.description }}">
{% if page.image %}
<meta name="twitter:image" content="{{ page.image | absolute_url }}">
{% endif %}
结构化数据(JSON-LD)
<!-- _includes/structured-data.html -->
{% if page.layout == 'post' %}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "{{ page.title }}",
"description": "{{ page.description }}",
"datePublished": "{{ page.date | date_to_xmlschema }}",
"dateModified": "{{ page.last_modified_at | default: page.date | date_to_xmlschema }}",
"author": {
"@type": "Person",
"name": "{{ page.author | default: site.author }}"
},
"publisher": {
"@type": "Organization",
"name": "{{ site.title }}",
"logo": {
"@type": "ImageObject",
"url": "{{ '/images/logo.png' | absolute_url }}"
}
},
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "{{ page.url | absolute_url }}"
}
{% if page.image %}
,"image": "{{ page.image | absolute_url }}"
{% endif %}
}
</script>
{% endif %}
sitemap.xml
---
layout: null
permalink: /sitemap.xml
---
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
{% for page in site.pages %}
{% unless page.sitemap == false %}
<url>
<loc>{{ page.url | absolute_url }}</loc>
<lastmod>{{ page.last_modified_at | default: page.date | date_to_xmlschema }}</lastmod>
<changefreq>{% if page.layout == 'post' %}monthly{% else %}weekly{% endif %}</changefreq>
<priority>{% if page.url == '/' %}1.0{% elsif page.layout == 'post' %}0.8{% else %}0.5{% endif %}</priority>
</url>
{% endunless %}
{% endfor %}
</urlset>
robots.txt
# static/robots.txt
User-agent: *
Allow: /
Sitemap: https://blog.example.com/sitemap.xml
Disallow: /assets/
Disallow: /_site/
14.3 图片优化
响应式图片
<!-- _includes/responsive-image.html -->
{% assign default_sizes = "320,640,960,1280" | split: "," %}
{% assign sizes = include.sizes | default: default_sizes %}
<picture>
{% for size in sizes %}
<source
media="(max-width: {{ size }}px)"
srcset="{{ include.src | replace: '.', '-' | append: '-' | append: size | append: '.webp' | relative_url }}">
{% endfor %}
<img
src="{{ include.src | relative_url }}"
alt="{{ include.alt }}"
loading="lazy"
width="{{ include.width }}"
height="{{ include.height }}">
</picture>
构建时图片处理插件
# Gemfile
gem "jekyll-responsive-image"
# _config.yml
plugins:
- jekyll-responsive-image
responsive_image:
template: _includes/responsive-image.html
default_quality: 80
sizes:
- width: 320
- width: 640
- width: 960
- width: 1280
output_path_format: assets/images/resized/%{width}/%{filename}
图片压缩工作流
# 使用 imagemin 批量压缩
npx imagemin static/images/**/* --out-dir=assets/images --plugin=pngquant --plugin=mozjpeg
# 使用 Squoosh CLI
npx @squoosh/cli --webp '{quality:80}' static/images/*.jpg
图片最佳实践
| 实践 | 说明 |
|---|---|
| 使用 WebP | 比 JPEG 小 25-35% |
| 懒加载 | loading="lazy" 属性 |
| 指定尺寸 | 避免布局偏移(CLS) |
| 响应式 | 使用 <picture> 或 srcset |
| CDN | 使用图片 CDN(如 Cloudinary) |
| 压缩 | 构建时压缩,保持质量 |
14.4 安全最佳实践
HTTP 安全头
# nginx.conf
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline';" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
依赖安全
# 检查 Gem 依赖漏洞
bundle audit check --update
# 定期更新依赖
bundle update
# 锁定版本
# Gemfile.lock 应该提交到 Git
14.5 工作流最佳实践
Git 分支策略
main ← 生产分支,自动部署
├── dev ← 开发分支
│ ├── feature/new-post ← 新文章
│ ├── feature/redesign ← 设计改版
│ └── fix/typo ← 修复
提交信息规范
feat: 添加新文章《Jekyll 最佳实践》
fix: 修复移动端导航菜单问题
style: 调整代码块配色
docs: 更新 README
chore: 升级 Jekyll 到 4.3.3
perf: 优化图片加载性能
自动化工作流
# .github/workflows/automation.yml
name: Site Automation
on:
schedule:
- cron: '0 0 * * 0' # 每周日
jobs:
update-deps:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
- run: bundle update
- name: Create PR
uses: peter-evans/create-pull-request@v5
with:
title: "chore: weekly dependency update"
branch: chore/dep-update
check-links:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true
- run: |
bundle exec jekyll build
bundle exec htmlproofer ./_site --allow-hash-href
14.6 从其他平台迁移
从 WordPress 迁移
# 1. 导出 WordPress 数据
# 安装 WordPress 导出插件
# Tools > Export > All content
# 2. 使用 Jekyll 导入工具
gem install jekyll-import
gem install hpricot
# 3. 执行导入
ruby -r rubygems -r hpricot -r jekyll-import -e '
JekyllImport::Importers::WordPress.run({
"dbname" => "wordpress_db",
"user" => "root",
"password" => "",
"host" => "localhost",
"prefix" => "wp_",
"clean_entities" => true
})
'
从 Hexo 迁移
# Hexo 和 Jekyll 结构相似,迁移较简单
# 1. 复制文章
cp -r hexo/source/_posts/ jekyll/_posts/
# 2. 调整 Front Matter
# Hexo 使用 <!-- more --> 作为摘要分隔
# Jekyll 默认使用 excerpt_separator
# 3. 修改模板语法
# Hexo: {% blockquote %} → Jekyll: <blockquote>
# Hexo: {% codeblock %} → Jekyll: ```代码块```
从 Hugo 迁移
# Hugo 和 Jekyll 的主要差异:
# 1. 模板语言(Go Template vs Liquid)
# 2. 目录结构
# 3. Front Matter 字段名
# 迁移步骤:
# 1. 复制内容
cp -r hugo/content/post/ jekyll/_posts/
# 2. 转换 Front Matter
# Hugo: title, date, draft, tags, categories
# Jekyll: title, date, published, tags, categories
# 3. 转换模板
# {{ .Title }} → {{ page.title }}
# {{ .Content }} → {{ content }}
# {{ range .Pages }} → {% for page in site.pages %}
迁移检查清单
## 迁移后检查
- [ ] 所有文章已正确导入
- [ ] Front Matter 格式正确
- [ ] 图片路径已更新
- [ ] 内部链接有效
- [ ] 分类和标签完整
- [ ] 永久链接与原站一致(或已配置重定向)
- [ ] SEO 元标签完整
- [ ] sitemap.xml 生成正确
- [ ] 旧 URL 重定向配置
- [ ] RSS feed 地址更新
- [ ] 评论系统迁移(如需要)
- [ ] 分析代码已添加
14.7 项目结构最佳实践
my-jekyll-site/
├── .github/
│ └── workflows/ # CI/CD 工作流
├── _config.yml # 主配置
├── _config_dev.yml # 开发环境覆盖
├── _data/ # 数据文件
│ ├── navigation.yml
│ └── authors.yml
├── _drafts/ # 草稿
├── _includes/ # 可复用片段
│ ├── head.html
│ ├── header.html
│ ├── footer.html
│ ├── seo.html
│ └── analytics.html
├── _layouts/ # 布局模板
│ ├── default.html
│ ├── post.html
│ ├── page.html
│ └── home.html
├── _plugins/ # 自定义插件
├── _posts/ # 文章
│ └── 2025/
├── _sass/ # Sass 源文件
│ ├── _variables.scss
│ ├── _mixins.scss
│ ├── _base.scss
│ └── _components.scss
├── assets/ # 静态资源
│ ├── css/
│ ├── js/
│ ├── images/
│ └── fonts/
├── pages/ # 独立页面
├── Gemfile
├── Gemfile.lock
├── .gitignore
├── .dockerignore
├── Dockerfile
├── docker-compose.yml
├── netlify.toml # 或 vercel.json
├── README.md
└── LICENSE
14.8 文章写作规范
Front Matter 模板
---
title: "文章标题"
date: 2025-01-15 10:30:00 +0800
last_modified_at: 2025-02-01
layout: post
permalink: /blog/article-slug/
categories: [技术, Jekyll]
tags: [tutorial, beginner]
description: "一句话描述文章内容"
author: "作者名"
image: /images/posts/article-slug.jpg
excerpt_separator:
comments: true
toc: true
---
Markdown 写作规范
# 一级标题(仅用于文章标题)
## 二级标题(主要章节)
### 三级标题(子章节)
正文内容,中文与英文之间加空格。
> 引用内容
**加粗** 和 *斜体*
- 列表项1
- 列表项2
```ruby
# 代码块标注语言
puts "Hello, World!"
| 表头1 | 表头2 |
|---|---|
| 内容1 | 内容2 |

---
## 14.9 备份与恢复
### Git 备份
```bash
# 定期推送到远程仓库
git add .
git commit -m "backup: $(date +%Y-%m-%d)"
git push origin main
完整备份脚本
#!/bin/bash
# backup.sh
BACKUP_DIR="/backups/jekyll"
DATE=$(date +%Y%m%d_%H%M%S)
SITE_DIR="/opt/jekyll-site"
# 创建备份
mkdir -p "$BACKUP_DIR"
tar -czf "$BACKUP_DIR/jekyll_backup_$DATE.tar.gz" \
--exclude='_site' \
--exclude='.jekyll-cache' \
--exclude='vendor' \
-C "$(dirname $SITE_DIR)" \
"$(basename $SITE_DIR)"
# 保留最近 30 天的备份
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +30 -delete
echo "Backup completed: jekyll_backup_$DATE.tar.gz"
14.10 监控与分析
Google Analytics 集成
<!-- _includes/analytics.html -->
{% if jekyll.environment == "production" and site.google_analytics %}
<script async src="https://www.googletagmanager.com/gtag/js?id={{ site.google_analytics }}"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '{{ site.google_analytics }}');
</script>
{% endif %}
性能监控
# _config.yml
google_analytics: "G-XXXXXXXXXX"
# 仅在生产环境启用
# 在 _layouts/default.html 中
{% include analytics.html %}
14.11 扩展阅读
本章小结
| 要点 | 说明 |
|---|---|
| 性能优化 | 增量构建、排除无用文件、优化模板 |
| SEO | jekyll-seo-tag、结构化数据、sitemap |
| 图片优化 | WebP、懒加载、响应式、压缩 |
| 安全 | HTTP 安全头、依赖审计 |
| 工作流 | Git 分支策略、提交规范、自动化 |
| 迁移 | WordPress/Hugo/Hexo 迁移工具和步骤 |
| 备份 | Git + 定期备份脚本 |
恭喜!你已完成 Jekyll 静态站点完全教程的全部 14 章内容。
回到目录:教程概览