4. 静态文件与模板:CSS/JS/图片的加载、模板中静态文件引用、蓝图(Blueprint)初识
好,咱们今天聊点实在的。一个后端项目,光有接口和数据是不够的。你想想看,用户打开一个页面,看到的是光秃秃的文字,连个样式都没有,那体验也太差了。所以,静态文件——CSS、JavaScript、图片——怎么加载,就成了绕不开的话题。
我个人习惯把静态文件和模板放在一起管理。Flask 在这方面做得挺顺手,但新手容易踩坑。今天我就把这块掰开揉碎了讲清楚。
4.1 静态文件的默认目录与配置
Flask 默认会在项目根目录下找 static 文件夹。你只要把 CSS、JS、图片丢进去,框架会自动处理。
项目结构大概长这样:
my_project/
├── app.py
├── static/
│ ├── css/
│ │ └── style.css
│ ├── js/
│ │ └── app.js
│ └── images/
│ └── logo.png
└── templates/
└── index.html
嗯,这里要注意:static 和 templates 这两个文件夹名字是约定俗成的。你当然可以改,但我建议别改。除非你有特殊需求,否则保持默认最省心。
Flask(__name__, static_folder='assets')。但相信我,你大概率用不上。
4.2 模板中引用静态文件
在 HTML 模板里引用静态文件,要用 Flask 提供的 url_for 函数。别直接写相对路径,那样容易出问题。
看个例子:
<!-- templates/index.html -->
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
<img src="{{ url_for('static', filename='images/logo.png') }}" alt="Logo">
<script src="{{ url_for('static', filename='js/app.js') }}"></script>
</body>
</html>
为什么非要用 url_for?说白了,Flask 会根据你的应用根路径自动生成正确的 URL。如果你把应用部署到子路径下(比如 /myapp/),硬编码的路径就全废了。我在项目中遇到过这种坑,后来老老实实全改成了 url_for。
url_for('static', filename='...')。别偷懒。
4.3 模板继承与静态文件
实际项目中,每个页面都要加载相同的 CSS 和 JS。你总不能每个模板都写一遍吧?这时候模板继承就派上用场了。
先写一个基础模板 base.html:
<!-- templates/base.html -->
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
{% block extra_css %}{% endblock %}
</head>
<body>
<nav>
<img src="{{ url_for('static', filename='images/logo.png') }}" alt="Logo">
</nav>
<div class="content">
{% block content %}{% endblock %}
</div>
<script src="{{ url_for('static', filename='js/app.js') }}"></script>
{% block extra_js %}{% endblock %}
</body>
</html>
然后子模板只需要填充内容块:
<!-- templates/index.html -->
{% extends "base.html" %}
{% block content %}
<h1>欢迎来到我的网站</h1>
<p>这是首页内容</p>
{% endblock %}
这样做的好处很明显:所有页面共享同一套 CSS 和 JS,改一处就全改了。我之前的项目有 50 多个页面,要是每个都单独写样式引用,维护起来简直要命。
4.4 蓝图(Blueprint)初识
项目一大了,所有路由都堆在 app.py 里,那画面太美我不敢看。蓝图就是用来解决这个问题的——它让你把相关路由和模板组织在一起。
举个例子,假设你的网站有用户模块和文章模块:
my_project/
├── app.py
├── blueprints/
│ ├── __init__.py
│ ├── user.py
│ └── article.py
├── static/
│ └── ...
└── templates/
├── user/
│ └── profile.html
└── article/
└── list.html
在 blueprints/user.py 中定义蓝图:
from flask import Blueprint, render_template
user_bp = Blueprint('user', __name__,
template_folder='../templates/user',
static_folder='../static')
@user_bp.route('/profile')
def profile():
return render_template('profile.html')
然后在 app.py 中注册:
from flask import Flask
from blueprints.user import user_bp
from blueprints.article import article_bp
app = Flask(__name__)
app.register_blueprint(user_bp, url_prefix='/user')
app.register_blueprint(article_bp, url_prefix='/article')
template_folder 和 static_folder 路径是相对于蓝图文件所在目录的。我曾经因为路径写错,折腾了半小时才发现是相对路径的问题。
注册蓝图时指定 url_prefix,所有路由都会自动加上前缀。比如 /user/profile 和 /article/list。这样模块之间互不干扰,代码结构清晰多了。
4.5 蓝图中静态文件的引用
在蓝图的模板里引用静态文件,写法稍微有点不同。你需要指定蓝图的名称:
<!-- templates/user/profile.html -->
<link rel="stylesheet" href="{{ url_for('user.static', filename='css/user.css') }}">
这里的 user 就是蓝图的名字。Flask 会自动找到该蓝图对应的静态文件目录。
我个人习惯把公共的 CSS/JS 放在全局 static 目录,把模块特有的样式放在蓝图自己的静态目录里。这样既共享了公共资源,又保持了模块的独立性。
4.6 避坑指南
最后分享几个我踩过的坑:
- 路径大小写问题: 有些服务器对大小写敏感。我曾经把
style.css写成了Style.css,在本地跑得好好的,部署到 Linux 服务器上就 404 了。 - 缓存问题: 浏览器会缓存静态文件。你改了 CSS,但用户看到的还是旧的。解决方案是在文件名后加版本号,比如
style.css?v=2,或者用 Flask 的static_url_path配置。 - 蓝图模板路径: 如果蓝图和主应用的模板文件夹有同名文件,蓝图会优先使用自己的模板。这一点容易让人困惑,建议命名时加上模块前缀。
好了,静态文件和蓝图这块就聊到这儿。下一章咱们会深入模板引擎 Jinja2 的高级用法,包括过滤器、宏和自定义标签。到时候你会发现,模板不只是简单的变量替换,它能做的事情远比你想的要多。