Please enable Javascript to view the contents

Django 中对静态文件版本的控制

 ·  ☕ 3 分钟

为了快速地响应用户的需求、满足运营活动的需要,互联网产品通常有着非常高的发布频率。采用敏捷开发的方式,缩短了交付的周期,加快了产品的迭代,也给项目的文件管理带来了挑战。前端工程直接面向用户,首当其冲,最值得重视。频繁更新的图片、样式、交互,不同的版本文件,怎样保证用户获取一个可预期的结果呢?本文正是从这个问题出发,讨论相关的解决方案。

1. 缓存方式

前端缓存分为两种:

  • 强缓存:浏览器在加载静态资源时,检查 Http Response Header 的 Expires 和 Cache-Control 两个字段。如果在有效期,那么使用浏览器缓存。
  • 协商缓存:如果没有命中强缓存,浏览器会向服务器验证是否命中协商缓存。协商缓存实际上是在静态资源的 Header 上标记 Last-Modified、ETag,通过对比浏览器端、服务器端静态资源的这些值,判断是否是同一个文件。如果一致,则返回 304,Not Modified 表示命中协议缓存。否则,浏览器从服务器加载静态文件。

服务器缓存主要是 CDN 缓存:

CDN ,也就是内容分发网络,是在现有网络的基础上,新增一层架构,提前将静态文件,分发到用户访问最佳的网络节点。当用户使用浏览器访问静态资源时,静态资源域名通过 DNS 解析,根据用户的地理位置,返回一个最佳访问的节点 IP 地址。浏览器拿到 IP 地址之后,再请求静态资源。

2. 强制刷新缓存方式

强制刷新缓存是指:当缓存有效时,通过一定的技术手段,迫使浏览器不使用缓存,而从服务器请求静态资源。

主要有两种方式:

  • 重新命名静态资源,如: app.js 更新为 app.a232nas9.js
  • 静态资源链接添加变化的参数,如:app.js 更新为 app.js?v=20171017

在大型 Web 项目中,更倾向于使用第一种,重命名静态资源的方式实现静态资源的强制刷新。这是由于采用第二种方式,发布时,需要替换静态资源文件。文件替换时,CDN 不能即时完成更新。当仅部分静态资源完成更新时,如果有用户访问,会导致一些不可预期的错误。

下面介绍几种 Django 中实现的静态文件版本管理的方式:

2.1 通过 settings 中的变量管理版本

1,在 yourapp 下新建 context_processors.py 文件

context_processors.py

1
2
3
4
5
6
7
# -*- coding: utf-8 -*-
from django.conf import settings

def version(request):
    return {
        'VERSION': settings.VERSION
    }

2,修改 settings.py 文件,新增如下内容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
VERSION = '.1.0.0'
TEMPLATES = [
    {
        'OPTIONS': {
            'context_processors': [
                'yourapp.context_processors.version',
            ],
        },
    },
]

3、修改 html 中静态资源的引入方式

静态资源重新命名方式

1
2
<script src="${STATIC_URL}js/app${VERSION}.js"></script>
<link href="${STATIC_URL}css/app${VERSION}.css" rel="stylesheet">

静态资源链接添加变化的参数方式

1
2
<script src="${STATIC_URL}js/app.js?v=${VERSION}"></script>
<link href="${STATIC_URL}css/app.css?v=${VERSION}" rel="stylesheet">

每次发布之前,通过修改 settings.py 中 VERSION 变量的值,可以达到前端版本更新的目的。由于静态文件共用一个 VERSION 标识,如果仅仅更新了一个静态文件,其他带上 VERSION 标识的静态文件也将被更新。

2.2 使用 ManifestStaticFilesStorage

Django 中提供 ManifestStaticFilesStorage 类,允许 Django 从 staticfiles.json 文件中读取静态文件映射。staticfiles.json 中保存了类似访问 app.css 时,返回 app.s23324a.css 的对应关系。

1,settings.py 设置

1
2
3
STATIC_ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static')
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
DEBUG = False

通过对 ManifestStaticFilesStorage 类的继承,复写相应的函数,还可以定制化静态文件名。

2,修改 html 中静态资源的引入方式

1
2
{% load static %}
<link href="{% static "css/app.css" %}" rel="stylesheet">

3,收集静态文件

1
python manage.py collectstatic

这样在 static 目录下 就会生成一个 staticfiles.json 文件。

访问网页时,静态文件会被带上 hash 版本值。

1
2
<link rel="stylesheet" type="text/css" href="/static/v/css/base.f0d165989b77.css" />
<link rel="stylesheet" type="text/css" href="/static/v/css/dashboard.4898e2e9983d.css" />

2.3 使用 CachedStaticFilesStorage

Django 中提供 CachedStaticFilesStorage 类,允许 Django 从缓存中读取静态文件映射。与 ManifestStaticFilesStorage 不同的是 CachedStaticFilesStorage 不需要创建一个映射文件,而是通过缓存来保存映射关系。

1,settings.py 设置

1
2
3
STATIC_ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static')
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.CachedStaticFilesStorage'
DEBUG = False

通过对 ManifestStaticFilesStorage 类的继承,复写相应的函数,还可以定制化静态文件名。

2,设置缓存

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 设置 memcache 或者 redis 缓存
CACHES = {
    "default": {
        "BACKEND": "redis_cache.cache.RedisCache",
        "LOCATION": "127.0.0.1:6379:1",
        "OPTIONS": {
            "CLIENT_CLASS": "redis_cache.client.DefaultClient",
        }
    }
}

2,修改 html 中静态资源的引入方式

1
2
{% load static %}
<link href="{% static "css/app.css" %}" rel="stylesheet">

3,收集静态文件

1
python manage.py collectstatic

这样在 static 目录下 就会生成一个 staticfiles.json 文件。

3. 参考


微信公众号
作者
微信公众号