最近在技术社区看到不少开发者想搭建个人博客却不知从何下手。作为一个用Flask+Vue组合开发过多个内容站点的老手,今天就来拆解下这种技术栈的完整实现路径。不同于现成的WordPress或Hexo,自主开发博客系统能获得完全可控的技术体验,特别适合想深入全栈开发的工程师。
这个方案的核心优势在于前后端彻底解耦。后端采用Python系的Flask轻量框架处理数据逻辑,前端用Vue构建动态交互界面,PyCharm作为主力开发工具提供全流程支持。Django虽然出现在标题中,但实际我们会用更灵活的Flask作为核心——两者都是Python生态的Web框架,但Flask的微框架特性更适合需要高度定制的博客场景。
现代Web开发早已不是当年PHP一把梭的时代。我们采用严格的前后端分离架构:
这种架构下,前后端可以并行开发。我曾在一个项目中让团队成员分别负责两端,通过Swagger文档定义接口规范,效率比传统模式提升40%以上。
博客系统的核心数据模型其实很简单,主要包含:
python复制class Article(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(80))
content = db.Column(db.Text)
created_at = db.Column(db.DateTime)
class Comment(db.Model):
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.Text)
article_id = db.Column(db.Integer, db.ForeignKey('article.id'))
建议使用Flask-SQLAlchemy扩展操作数据库,它比直接写SQL更安全高效。我在早期项目中曾因SQL拼接导致注入漏洞,这个教训让我坚持使用ORM至今。
现代技术博客离不开Markdown支持。推荐以下方案:
配置示例:
javascript复制// Vue组件
<editor
initialValue="## Hello World"
previewStyle="vertical"
height="500px"
/>
踩坑提示:记得在前端做XSS过滤,我曾因为直接渲染原始Markdown导致脚本注入漏洞。
采用JWT方案比Session更适合前后端分离架构:
python复制# Flask后端
@app.route('/login', methods=['POST'])
def login():
user = User.query.filter_by(username=request.json['username']).first()
if user.check_password(request.json['password']):
access_token = create_access_token(identity=user.id)
return {'access_token': access_token}
前端需要实现:
创建Flask后端项目:
配置Vue前端:
调试配置:
json复制{
"name": "复合调试",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/app.py",
"args": ["--port=5000"],
"preLaunchTask": "启动前端服务"
}
推荐Docker+GitHub Actions实现CI/CD:
dockerfile复制# 前端Dockerfile
FROM node:16 as build-stage
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# 生产阶段
FROM nginx:stable
COPY --from=build-stage /app/dist /usr/share/nginx/html
文章列表采用Redis缓存:
python复制@cache.cached(timeout=60, key_prefix='article_list')
def get_articles():
return Article.query.order_by(Article.created_at.desc()).all()
前端配置SW预缓存:
javascript复制// vite.config.js
import { VitePWA } from 'vite-plugin-pwa'
plugins: [
VitePWA({ registerType: 'autoUpdate' })
]
添加复合索引:
python复制class Article(db.Model):
__table_args__ = (
db.Index('idx_author_created', 'author_id', 'created_at'),
)
分页查询优化:
python复制Article.query.order_by(Article.created_at.desc()).paginate(
page=1, per_page=10, error_out=False)
CSRF防护(虽然JWT方案风险较低但仍建议):
python复制@app.after_request
def add_csrf_token(response):
response.set_cookie('csrf_token', generate_csrf())
return response
密码存储必须加盐哈希:
python复制from werkzeug.security import generate_password_hash
user.password = generate_password_hash(password, method='pbkdf2:sha256')
API速率限制:
python复制from flask_limiter import Limiter
limiter = Limiter(app, key_func=get_remote_address)
@app.route("/api/search")
@limiter.limit("10/minute")
def search():
return db.session.query(Article).filter(...)
轻量级方案用Whoosh:
python复制from flask_whooshee import Whooshee
whooshee = Whooshee(app)
@whooshee.register_model('title', 'content')
class Article(db.Model):
# 模型定义...
无需数据库的方案:
javascript复制// 前端页面埋点
import axios from 'axios'
axios.post('/api/track', {
path: location.pathname,
referrer: document.referrer
})
后端用内存缓存临时存储:
python复制from collections import defaultdict
visitor_stats = defaultdict(int)
@app.route('/api/track', methods=['POST'])
def track():
visitor_stats[request.json['path']] += 1
return ''
经过多个类似项目的实践,我总结出这样的目录结构最合理:
code复制/blog-frontend # Vue项目
/public
/src
/assets
/components
/views
/blog-backend # Flask项目
/migrations
/app
/static
/templates
__init__.py
models.py
routes.py
这种结构下,前后端可以独立版本控制,也便于部署时分离构建。有个反例:我曾将Vue项目放在Flask的static目录,导致热重载失效,不得不重构整个项目。