Collect Static 执行的原理
Django
默认使用staticfiles
这个app来将整个项目下的静态文件收集到一个目录。主要用到了两个参数:
项目相关的静态文件,按照默认的配置会收集到STATIC_ROOT
目录。
模板文件中,使用{% static "path/to/static/file "%}
方式引入的项目静态资源,会根据STATIC_URL
替换成目标值。
这一层需要注意的一个点是,全部独立APP下面的static
都对应到STATIC_ROOT
这一层。所以按照文档的建议,每个单独的APP下面的static目录最好也带上app名称。
直观的例子:
1 | mysite/ |
appa
和appb
下面有共同的a.jpg
,都会收集到STATIC_ROOT
下面。某一个项目的a.jpg
会覆盖掉另一个项目的,造成问题。
正确的做法是:
1 | mysite/ |
在每个app下的static目录下,相关资源放到以当前app命名的文件夹,能避免碰到同名文件覆盖的问题。
Hash 和自动上传云存储兼得
默认的方式存在两个问题
- 修改文件内容,链接不会变更,CDN/浏览器等各层会有文件缓存,新内容不会生效。
- 需要手动/写脚本把
STATIC_ROOT
目录的资源上传到CDN。
这两个问题,都有单独的解决方案。使用STATICFILES_STORAGE。
默认的STATICFILES_STORAGE
使用的系统的文件存储,会将文件放置到STATIC_ROOT
目录,使用不同的STATICFILES_STORAGE
可以实现更多丰富的功能。
Hash 静态文件
ManifestStaticFilesStorage是Django自带的hash静态资源的工具。
一个关联的静态资源,会由
1 | @import url("../admin/css/base.css"); |
会变成
1 | @import url("../admin/css/base.27e20196a850.css"); |
自动上传到CDN
自动上传到各种CDN的本质,就是将对象存储的Bucket作为静态文件的FileStorage。
Django框架下有django-storages和django-s3-storage。
这两个库做的事情都是将静态资源文件上传到对象存储的Bucket。现在国内的提供对象存储的云服务商的接口,大部分都兼容事实上的行业标准AWS S3的接口,修改endpoint_url
之后都能使用。
Hash 和自动上传兼得
两种FileStorage
各自只实现了一种功能,怎么同时实现两个需求呢。Hash这个功能比较麻烦,尽量复用现有的。那就需要将上传整合进现有的模块。
落到处理静态资源的命令collectstatic上。
处理完静态资源文件之后,会调用post_process
。
查看ManifestStaticFilesStorage对应的函数,涉及到两个函数read_manifest
和save_manifest
。
问题就简单了,利用Django自带的ManifestStaticFilesStorage
生成好带Hash的静态文件,再修改这个方法,自动根据文件列表上传到CDN。
Django 启动的时候会根据read_manifest
读取manifest.json
,根据他的结果渲染页面。这个函数使用到正确的manifest.json
文件就能保证链接的是正确的静态文件。
自定义的FileStorage代码如下:
1 | #!/usr/bin/env python3 |
我还是通过配置文件的STATICFILES_VERSION
来决定使用的静态资源的版本。但是其他的一系列操作(Hash、Upload)都自动完成。
以后的改造计划
- 压缩 js、css 文件
- 压缩图片
- 自动使用最新版本的
STATICFILES_VERSION
(比如通过一个接口获取、使用当前 git head 之类)