Flask无法看到.js文件的更改

72

我在我使用的一个 .js 文件上做了更改,但无论我怎么做,flask 始终会从内存缓存中选取文件的最新版本(而不是我的更改版本)。

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

return render_template foo.html

foo.html 里有一个表单,它会向 Flask 传递一些数据,然后返回第二个模板 bar.html

return render_template bar.html

这个第二个模板调用了一些 .js 文件,这些文件放置在 static 文件夹中,但是当代码发生变化时它不会更新。

我提到了上面的结构,因为如果将 .js 文件放在 foo.html 而不是 bar.html 上,那么 Flask 捕捉到文件的新更改。但在 bar.html 中,Flask 完全忽略了它们。

发生了什么?

唯一有效的方法是在浏览器上点击 "禁用缓存" ,然后重新加载。


1
这不是 Flask 的问题,而是浏览器为了更快地工作而保留了旧版本的缓存。通常情况下,您将使用静态 .js 文件,这不会有任何问题。一些服务器使用动态 URL 来强制浏览器加载新版本的脚本 - 例如 "script.js?some_variable=dynamic_value"dynamic_value 可以是版本号或文件日期。 - furas
我正在开发过程中,不断修改.js文件,显然不能期望每次更改时都重命名文件。 - elelias
1
是的,在开发中我只是进行硬刷新。在Mac上使用Chrome浏览器,这是CMD-R。 - abigperson
2
如果 Ctrl+F5Ctrl+R 无法正常工作,则在创建最终版本之前请在浏览器中禁用缓存,或者使用 "script.js?some_variable=file_timestamp" 并添加创建函数以生成带参数的 URL。 - furas
1
所以Ctrl+R不起作用,但cmnd + R在Mac上可以工作。@PJSantoro,如果您想发布解决方案,我会接受。 - elelias
显示剩余4条评论
5个回答

105

最终,这是一个令人沮丧的浏览器缓存问题,可以通过强制浏览器进行“硬刷新”来解决,这将是一个与浏览器/操作系统相关的按键组合,但通常这有效:

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

还有其他文件名技巧可用于避免此问题(在OP的评论中提到)。在生产环境中特别重要,因为您无法控制浏览器行为。

对于非静态Flask响应,您可以设置cache_control.max_age属性,如果已缓存,则应告诉浏览器何时过期响应。例如,如果您有一个返回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 等)设置默认值。

编辑于 2019 年 4 月 1 日(与愚人节无关)

  • Mac/Safari 的按键组合现在似乎是:Cmd+Opt+R(感谢评论提供的信息!)。
  • 请查看 @MarredCheese 的新答案,其中提供了一种非常简洁的“文件名技巧”,可强制浏览器忽略已缓存的副本以更新文件。

2019年更改Mac • Cmd+Opt+R - Carlos Galeano
1
感谢@CarlosGaleano。从我所了解的情况来看,这似乎是针对Safari的特定问题,但我在这里添加了一个编辑来提及这一点。 - abigperson
他们在美国一月份就开始庆祝愚人节了。 - Michael Scheper

41

缓存通常是有益的,因此不建议完全消除它。使用Ctrl+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>

1
这是针对生产环境中用户拥有缓存文件版本的网站的最佳解决方案。 - dannypernik

25

如果你正在使用Flask提供静态资源(在开发环境中通常是这样),那么你可能需要设置 SEND_FILE_MAX_AGE_DEFAULT 配置值:

send_static_file()(默认的静态文件处理程序)和 send_file() 使用的默认缓存控制最大年龄,可以用 datetime.timedelta 或秒数表示。在 Flask 或 Blueprint 中分别使用 get_send_file_max_age() 钩子来覆盖每个文件的此值。默认为43200(12小时)。

解决此问题可能只需简单地更新 app.config 字典,如下所示:

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

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


2

使用:

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

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


-3
使用CTRL + F5刷新浏览器

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接