-
Notifications
You must be signed in to change notification settings - Fork 10
Pliki statyczne w Systemie Zapisów
W Zapisach korzystamy z TypeScriptu i sass + frameworków jak vue.js czy React. Kompilacja assetów do formatu nadającego się dla przeglądarki odbywa się przy użyciu Webpacka. Webpack uruchamiany jest w ramach node.js. Dzięki temu procesowi:
- Możemy korzystać z technologii przyjemniejszych niż te natywnie dostępne w przeglądarkach (czysty ES6/CSS)
- Nie trzeba się nadmiernie przejmować przy używaniu nowych technologii; wszystkie cztery nowoczesne przeglądarki (Edge, Safari, Chrome, FF) powinny sobie radzić z naszym frontowym kodem spokojnie do kilku wersji wstecz.
System staticfiles w Django jest dziś naszym zdaniem niewystarczający do komfortowej pracy nad aplikacjami działającymi w przeglądarkach, ponieważ w Django te pliki faktycznie są statyczne. Istnieje projekt pipeline, ale jest to dość niszowa opcja: brak dobrze działającego wsparcia dla TypeScriptu (trzeba to robić samemu), korzystanie z vue.js prawdopodobnie w ogóle nie byłoby możliwe. Webpack jest znacznie bardziej popularnym i w związku z tym kompletnym rozwiązaniem.
Nazwa pliki statyczne, choć rozpowszechniona w Django, wydaje się być o tyle nietrafna, że, jak wspomniano wyżej, większość tych plików nie jest statyczna i wymaga procesu kompilacji. W związku z tym będą one tutaj nazywane zbiorczo assetami.
Dawny system plików statycznych został przebudowany, dlatego zachęcamy do przeczytania poniższego krótkiego opisu.
-
Pliki statyczne (rawfiles) - Mowa tu o wszelkich plikach niewymagających kompilacji, jak np. obrazki. Do ich serwowania korzystamy z systemu staticfiles. Przechowujemy je w podkatalogu
static/<nazwa aplikacji>/
w folderze aplikcji która z danego pliku korzysta. Aby móc zagnieździć taki plik w Djangowym templatcie, korzystamy z wbudowanego tagu{% static "nazwa_aplikacji/plik" %}
. -
Pliki kompilowane - Wszelkie pliki Javascript, Typescript, Vue, CSS, SCSS itp. Do ich kompilacji wykorzystujemy Webpacka, a integrację zapewnia paczka
django-webpack-loader
. Korzystanie z niej jest bardzo proste:
-
W folderze danej aplikacji tworzymy folder
assets
. To tutaj będziemy przechowywać wszelkie kompilowane pliki danej aplikacji. -
W pliku
webpack_resources/asset-defs.js
, w dziale aplikacji oznaczonym odpowiednim komentarzem, umieszczamy definicję naszego bundle'a. Z zasady oznaczamy go wedle następującej zasady:nazwa_aplikacji-nazwa_bundle'a
. -
W pliku HTML ładujemy zdefiniowany przez
django-webpack-loader
template tag:{% load render_bundle from webpack_loader %}
. -
Przy pomocy taga wczytujemy bundle w następujący sposób:
{% render_bundle "nazwa_aplikacji-nazwa_bundlea" %}
. Wygenerowane zostaną wówczas tagi<script>
i<style>
, które wczytają nasze skompilowane assety.
Webpack jest dość niskopoziomowym systemem i sam nie narzuca żadnej konwencji; w związku z tym system opisany poniżej jest zaprojektowany przez nas. Jeżeli pojawi się jakaś propozycja ulepszenia go, można to bez problemu zrobić.
Po uruchomieniu kompilacji Webpack sprawdzi zawartość pliku webpack_resources/asset-defs.js
. Format tego pliku jest następujący:
const path = require("path");
const AssetDefs = {
// Nazwa_aplikacji app
"nazwa_aplikacji-nazwa_bundlea1": [
path.resolve("apps/nazwa_aplikacji/assets/example1.ts"),
path.resolve("apps/nazwa_aplikacji/assets/example2.js")
],
"nazwa_aplikacji-nazwa_bundlea2": [
path.resolve("apps/nazwa_aplikacji/assets/example3.scss")
]
// ...
}
module.exports = AssetDefs;
Eksportujemy obiekt zawierający klucze będące nazwami bundle'i. Podane ścieżki muszą być relatywne względem folderu zapisy
.
Poniżej podano objaśnienie obu sekcji.
Bundle to nic innego jak zbiór plików .ts, .js, .scss czy .css. Definicja każdego z nich składa się z tablicy ze ścieżkami do plików z których się składa. Do tablicy wystarczy wpisać tylko entry points (punkty wejściowe); wszystkie pliki importowane pośrednio lub bezpośrednio przez jakikolwiek punkt wejściowy zostaną automatycznie dodane do bundle. Do nazwy bundle wedle zasady dodajemy z przodu nazwę aplikacji Django, do której ten bundle należy, oraz myślnik (-
).
Bundle można potem wczytać w szablonie Django w następujący sposób:
- Należy załadować (oczywiście tylko raz w danym pliku) odpowiedni tag:
{% load render_bundle from webpack_loader %}
- W odpowiednim miejscu w kodzie należy wczytać bundle:
{% render_bundle "nazwa_aplikacji-nazwa_bundlea" %}
. W wyniku tego wywołania zostaną wygenerowane tagi HTML<script>
oraz<link>
wczytujące odpowiednio skompilowany kod JS oraz CSS. Uwaga: są to standarowe tagi HTML i w związku z tym należy przestrzegać normalnych zasad ich umieszczania w kodzie (wzorcowo powinno się to robić w sekcji<head>
pliku HTML).
Dla przykładu, w aplikacji news, chcemy zadeklarować bundle o nazwie test-bundle
. Robimy to w pliku webpack_resources/asset-defs.js
w następujący sposób:
const path = require("path");
const AssetDefs = {
// ...
// news app
"news-test-bundle": [
path.resolve("apps/news/assets/myscript.ts"),
],
// ...
}
module.exports = AssetDefs;
w myscript.ts
:
import { memoize } from "lodash";
import { someUtility } from "./utils.ts";
Pełna nazwa bundle to news-test-bundle
(to jej należy użyć w szablonie Django). Należeć do niego będzie zarówno myscript.ts
, utils.ts
, jak i kod z lodash
implementujący funkcję memoize
. Wystarczy jednak zdefiniować tylko punkt wejściowy/korzeń drzewa zależności. By załączyć skompilowany bundle w template, należy napisać:
{% render_bundle "news-test-bundle" %}
Pliki, które nie wymagają przetwarzania w ramach procesu kompilacji. Opcja ta służy do pracy z obrazami czy dokumentami (pdf).
rawfiles
umieszczamy w katalogu <katalog aplikacji>/static/<nazwa aplikacji>
. Dla przykładu, jeśli chcemy użyć pliku img.png
w aplikacji news
, umieszczamy go w lokalizacji: apps/news/static/news/img.png
.
By go później użyć w szablonie, np. by stworzyć bezpośredni odnośnik, należy skorzystać ze static
: {% static "news/img.png" %}
.
Podczas kompilacji każdy bundle kompilowany jest do jednego pliku dla każdego typu plików wejściowych. Innymi słowy, pliki .ts
należące do jednego bundle zostaną skompilowane do jednego pliku .js
, zaś pliki .scss
- do jednego pliku .css
. Folderem wyjściowym dla kompilacji jest zapisy/compiled_assets
. Skompilowane bundles zostaną wypisane bezpośrednio do tego folderu.
WAŻNE: jak wspominano wcześniej, do nazwy każdego bundle z zasady dodajemy z przodu nazwę aplikacji do której należy. Dla przykładu: dla aplikacji news
określono w asset-defs.js
bundle o nazwie main
; jego pełna nazwa, którą należy zadeklarować i użyć później w szablonie, to news-main
.
Wykonaj polecenie run server
. Uruchomi to serwer Django i kompilator assetów naraz. Kompilator assetów automatycznie wykryje wszelkie zmiany i przeprowadzi ponowną kompilację tylko zmienionego kodu, co z reguły trwa nie dłużej, niż 1-2 sekundy.
UWAGA: Opcje podane tutaj są potrzebne wyłącznie do zaawansowanych zastosowań (jak choćby konfiguracja fabfile, która opisuje, w jaki sposób zbudować produkcyjną wersję assetów); do użytku standardowego nie ma potrzeby zapoznawać się z nimi.
Kompilację uruchamia się skryptami zdefniowanymi w package.json
. Dostępne są cztery tryby:
-
yarn dev:watch
- uruchom w trybie development (brak minifikacji i optymalizacji, generowanie sourcemap), tryb watch (automatyczna, inkrementalna kompilacja przy wykryciu zmian w plikach). Tryb ten jest uruchomiony jako usługa systemowa na wirtualnej maszynie developerskiej. -
yarn dev
- jak wyżej, ale bez trybu watch (proces kompilatora kończy pracę się po przetworzeniu plików) -
yarn dev:tc
- jak wyżej, ale ze sprawdzaniem typów przez Typescript. Domyślnie uruchomione podczas automatycznych testów na Travis-ci. -
yarn build
- skompiluj projekt przy ustawieniach produkcyjnych (brak sourcemap, minifikacja oraz optymalizacja skryptów i styli). Domyślnie uruchomione przy deploy'ach (wrzucaniu nowej wersji kodu na maszynę produkcyjną).
Skrypt run.py
w głównym katalogu uruchamia serwer Django oraz Webpack w trybie devw
jak powyżej; tego rozwiązania należy używać przy codziennej pracy. Skrypt w obecnej konfiguracji jest uruchamiany automatycznie.
Szczegół implementacyjny: W settings.py STATICFILES_DIRS
ustawione jest na compiled_assets
. Chodzi o to, by:
- Przy
DEBUG = True
serwer Django odczytywał i udostępniał skompilowane assety z tego katalogu - Podczas deploy za pomocą
fabfile.py
można było standardowo zebrać wszystkie pliki używająccollectstatic
. Warto zwrócić uwagę, że nie można po prostu ustawićSTATIC_ROOT
nacompiled_assets
, bo istnieją również inne pliki statyczne z aplikacji wbudowanych w Django, np.contrib.auth
. Dlatego niezbędne jest uruchomieniecollectstatic
, które pozbiera pliki zarówno zcompiled_assets
, jak i pozostałych aplikacji w systemie, i zbierze je w jednym miejscu.
Szczegół techniczny: W Ubuntu yarn
kryje się pod komendą yarnpkg
.
Pierwszą konfigurację z Webpackiem i bundle'ami stworzył w Systemie Zapisów @user-45-20. Istotne uproszczenia zostały wprowadzone (#962) przez @basiekjusz i @jasiekmarc.