flask 看不到 .js 文件的变化

flask does not see change in .js file

我对我使用的 .js 文件之一进行了更改,无论我做什么,flask 都坚持从内存缓存中获取文件的最新版本,而没有更改。

为了澄清,我有以下结构。一切都以 foo.html

开头
return render_template foo.html

foo.html 内部有一个表单调用带有一些数据的烧瓶然后 returns 第二个模板 bar.html:

return render_template bar.html

第二个模板调用一些 .js 文件,放在 static 文件夹中,但它不会在代码更改时更新。

我提到上面的结构是因为如果 .js 文件放在 foo.html 而不是 bar.html 那么 Flask 拿起文件的新变化。但是在 bar.html Flask 中完全忽略了它们。

这是怎么回事?

唯一有效的方法是在浏览器上单击 "disable cache" 并重新加载。

最终这是一个令人沮丧的浏览器缓存问题,可以通过强制浏览器执行 "hard refresh" 来解决,这将是 browser/OS 相关的击键,但通常这是可行的:

  • Windows: Ctrl+F5
  • Mac: Cmd+Shift+R
  • Linux: Ctrl+Shift+R

还有其他文件名技巧可以用来避免这个问题(在 OP 的评论中提到)。这些在您无法控制浏览器行为的生产环境中尤为重要。

对于非静态 Flask 响应,您可以设置 cache_control.max_age 属性,如果响应被缓存,它应该告诉浏览器何时过期响应。例如,如果你有一个 returns JSON 数据的 Flask XHR 端点,你可以这样做:

@app.route('/_get_ajax_data/')
def get_ajax_data():
    data = {"hello": "world"}
    response = jsonify(data)
    response.cache_control.max_age = 60 * 60 * 24  # 1 day (in seconds)
    return response

您通常还可以在生产 Web 服务器配置中为特定资源类型设置默认值(例如 CSS/JS/HTML/JSON/etc)

编辑 4/1/2019(与愚人节无关)

  • Mac / Safari 击键现在显示为:Cmd+Opt+R(通过评论,谢谢!)。
  • 请参阅@MarredCheese 的新答案,以获得非常优雅的 "filename trick" 强制浏览器忽略更新文件的缓存副本。

如果您使用 Flask 提供静态资产(这通常是开发环境中的情况),那么您可能需要设置 SEND_FILE_MAX_AGE_DEFAULT configuration value:

Default cache control max age to use with send_static_file() (the default static file handler) and send_file(), as datetime.timedelta or as seconds. Override this value on a per-file basis using the get_send_file_max_age() hook on Flask or Blueprint, respectively. Defaults to 43200 (12 hours).

解决这个问题就像更新 app.config 字典一样简单,如下所示:

app = Flask(__name__)
...
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0

如果这样做,您的浏览器将不会缓存 Flask 提供的静态资产。

缓存通常很好,因此不建议完全消除它。使用 control + F5 或任何进行硬刷新显然不是可扩展的解决方案,因为您必须在每台计算机上的每个浏览器中执行此操作.

更好的办法是让浏览器在大部分时间缓存文件,但不是在文件更新后立即缓存。您可以通过附加时间来实现此目的源文件最后更新为它的路径参数 URL。为简单起见,您可以对静态文件夹(或任何子文件夹)中的任何文件使用最近的修改时间,而不是单独查看每个文件。

Python

def dir_last_updated(folder):
    return str(max(os.path.getmtime(os.path.join(root_path, f))
                   for root_path, dirs, files in os.walk(folder)
                   for f in files))

@app.route('/my-site')
def my_site():
    return render_template('my-site.html',
                           last_updated=dir_last_updated('mydir/static'))

Jinja 模板

<script type="text/javascript" src="/static/my-script.js?u={{ last_updated }}"></script>

HTML 结果

<script type="text/javascript" src="/static/my-script.js?u=1547330602.31"></script>

使用 CTRL+ f5 刷新浏览器

使用:

if __name__ == '__main__':
    app.config['TEMPLATES_AUTO_RELOAD'] = True
    app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0
    app.run(debug=True)

您可以使用 F5CTRL + R 进行更新。