Os arquivos estáticos de aplicativos da web são todos folhas de estilo, javascripts, imagens, fontes, etc. Como esses arquivos são estáticos, eles podem ser servidos a partir de cache ou CDN (rede de distribuição de conteúdo) e acelerar o tempo de carregamento de uma página da web. No entanto, o armazenamento em cache de arquivos estáticos no lado do servidor ou do cliente tem um grande problema: a invalidação do cache.
Vida longa aos arquivos em cache
Cada navegador armazena em cache ativos estáticos da página da web. Usando o max-age
cabeçalho, podemos instruir os navegadores por quanto tempo eles devem manter nossos arquivos em seu cache. O melhor desempenho é alcançado quando o arquivo é armazenado em cache indefinidamente, exigindo apenas uma solicitação ao servidor da Web para cada arquivo.
Isso é ótimo, porque o usuário carrega todos os ativos estáticos de nossa página na primeira visita e cada solicitação subsequente é extremamente rápida. No entanto, quando mudamos nosso CSS ou JS, estamos perdidos, porque o navegador não fará o download do arquivo mais recente. Não podemos nem instruir os navegadores a invalidar seus caches, apenas os usuários podem.
A solução é simples: renomeie os arquivos sempre que eles forem alterados. Portanto, quando acrescentamos o hash MD5 ou SHA1 ao nome do arquivo original, ele será único depois de mudado. Agora, só precisamos atualizar as referências em HTML, referências em folhas de estilo (como propriedades de url) e talvez em algum outro lugar em nossa base de código … Vamos as máquinas fazer seu trabalho!
O jeito Django
Outros desenvolvedores do Django já inventaram essa roda. Desde Django 1.7, existe o ManifestStaticFilesStorage
, que renomeia arquivos e suas referências toda vez que é executado collectstatic
. O nome do arquivo estático mais recente é armazenado no staticfiles.json
manifesto, portanto, quando chamamos {% static 'my/file/latest.css'%}
, obtemos algo como: http://static.cdn/my/file/latest.a1b2c3.css
(que é uma combinação de um STATIC_URL
nome de arquivo com hash).
Esta solução é perfeita devido à sua simplicidade. A maioria dos projetos já usa static
tag para localizar nomes de arquivos estáticos, o que é mais flexível do que prefixar STATIC_URL
. ManifestStaticFilesStorage
lida com renomeações automaticamente quando executamos collectstatic
, então, na verdade, essa abordagem resolve nosso problema com o mínimo de esforço. Porém, há apenas uma pequena advertência: o que acontece durante a implantação de uma nova versão?
Frango ou ovo
O manifesto é sobrescrito quando chamamos collectstatic. Se coletarmos arquivos estáticos antes de o servidor ser reiniciado, nossa página da web antiga carregará novos arquivos estáticos e algumas coisas podem falhar. Se invertermos a ordem e coletarmos os arquivos estáticos depois que o servidor for recarregado, a nova página da Web carregará os arquivos estáticos antigos e, novamente, algumas coisas podem falhar.staticfiles.json
Uma vez que temos controle de versão para arquivos estáticos, também podemos adicionar controle de versão para manifesto. Podemos simplesmente anexar a versão do aplicativo que está sendo alterada durante cada lançamento. Assim, podemos coletar arquivos estáticos com segurança durante o processo de lançamento e novos arquivos estáticos serão usados logo após o processo ser recarregado.staticfiles.json
Se adicionarmos __version_static__
ao de nosso projeto, podemos usar o seguinte armazenamento de arquivos estáticos:__init__.py
from django.contrib.staticfiles.storage import ManifestFilesMixin
from storages.backends.s3boto import S3BotoStorage
import lingui
class StaticRootS3BotoStorage(ManifestFilesMixin, S3BotoStorage):
"""
Appends version of static files (git commit sha)
to static files manifest before uploading to S3.
"""
location = 'static'
@property
def manifest_name(self):
filename = 'staticfiles-{version}.json'
return filename.format(version=lingui.__version_static__)
O __version_static__
é atualizado automaticamente toda vez que os arquivos estáticos são lançados com o hash git commit curto. É diferente da versão do projeto, porque nem todos os lançamentos de código requerem também a liberação de estática. Uma vez que a execução collectstatic
pode demorar algum tempo, esta é uma pequena aceleração do processo de liberação.