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

嗯,这里要注意:statictemplates 这两个文件夹名字是约定俗成的。你当然可以改,但我建议别改。除非你有特殊需求,否则保持默认最省心。

小提示: 如果你非要改静态文件目录,可以在创建 Flask 实例时指定: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_folderstatic_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 配置。
  • 蓝图模板路径: 如果蓝图和主应用的模板文件夹有同名文件,蓝图会优先使用自己的模板。这一点容易让人困惑,建议命名时加上模块前缀。
我的习惯: 开发时开启 Flask 的调试模式,它会自动禁用静态文件缓存。部署时用 Nginx 处理静态文件,性能更好,也方便做缓存策略。

好了,静态文件和蓝图这块就聊到这儿。下一章咱们会深入模板引擎 Jinja2 的高级用法,包括过滤器、宏和自定义标签。到时候你会发现,模板不只是简单的变量替换,它能做的事情远比你想的要多。