Skip to content
Playground

6'.静的リソースとレイアウト

学習目標

  • static フォルダーに CSS や画像を置き、ルート相対パスでテンプレートから参照できる
  • フラグメントで共通部分を 1 か所にまとめられる

テンプレート で動的に表示した画面に、CSS や画像を組み込み、共通する部分を 1 か所にまとめます。

CSS・画像・favicon のように、コントローラーを経由せずブラウザーから直接読み込まれるファイルを 静的リソース と呼びます。動的に内容が変わらないため、コントローラーで処理する必要がありません。

静的リソースは src/main/resources/static に置きます。static/ 配下のファイルは、ファイル名がそのまま URL のパスになり、Spring Boot が自動で配信します。

配置場所URL のパス
static/css/style.css/css/style.css
static/images/book.png/images/book.png
static/favicon.ico/favicon.ico

画面の見た目を整える CSS は、static/css/ に置きます。文字色を変える style.css を作成します。

src/main/resources/static/css/style.css
body {
color: #34302d;
font-family: sans-serif;
}
h1 {
color: #6db33f;
}

テンプレートからは、<link> タグの href 属性で参照します。

templates/home.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/css/style.css">
<title>Spring Books</title>
</head>
<body>
<h1>Spring Books</h1>
</body>
</html>

href="/css/style.css" は、static/css/style.css に対応します。先頭の / から、static/ 配下のパスを書きます。ブラウザーで / を開くと、本文が濃いチャコール、見出しの Spring Books が緑色で表示されます。

画像は static/images/ に置きます。book.png を表示するには、<img> タグの src 属性で参照します。

<img src="/images/book.png" alt="">

ブラウザーのタブに表示される favicon は、static/favicon.ico に置きます。Spring Boot がデフォルトで配信するため、テンプレートに <link> を書かなくても自動で読み込まれます。

CSS や画像の参照は、/css/style.css のように先頭に / を付けた ルート相対パス で書きます。先頭の / はアプリケーションのルートを指し、Spring Boot ではそれが static/ に対応します。先頭に / を付けない 相対パス でも書けます。

<!-- ルート相対パス -->
<link rel="stylesheet" href="/css/style.css">
<!-- 相対パス -->
<link rel="stylesheet" href="css/style.css">

相対パスは、ブラウザーで表示しているページの URL を基準に解決されます。テンプレートが templates/ のどこに置かれているかは関係しません。基準にする URL が変わると、同じ css/style.css でも参照先が変わります。

書き方URL が / のページURL が /books/new のページ
/css/style.css(ルート相対パス)/css/style.css/css/style.css
css/style.css(相対パス)/css/style.css/books/css/style.css

ルート相対パスは、どのページから参照しても同じ /css/style.css を指します。相対パスは、URL が /books/new のページでは /books/css/style.css を指し、そこに CSS はないためスタイルが適用されません。複数のページで共有する CSS や画像は、ルート相対パスで書きます。

各ページは、<head> の中身(文字コード・スタイルシート・タイトル)を、それぞれのファイルに同じように書いています。同じ内容を複数のファイルに持つと、スタイルシートを 1 つ足すだけでも全ページの <head> を直すことになります。

Thymeleaf では、共通する部分を 1 か所に定義し、各ページから読み込めます。この共通部分を フラグメント(fragment)と呼びます。

共通にする部分のタグに th:fragment を付けて、フラグメントを定義します。ページごとに変わる値は、引数で受け取れます。

<タグ th:fragment="フラグメント名(引数)">
... 共通の内容。引数は ${引数} で参照する ...
</タグ>

各ページ共通の <head> を、fragments/layout.html にフラグメントとして定義します。ページごとに変わるタイトルは、引数 title で受け取ります。

templates/fragments/layout.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head th:fragment="head(title)">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/css/style.css">
<title th:text="${title}">タイトル</title>
</head>
</html>

th:fragment="head(title)" は、head という名前のフラグメントを、引数 title を取る形で定義しています。<title th:text="${title}"> は、受け取った title をタイトルに表示します。

定義したフラグメントは、th:replace で挿入します。引数には、そのページの値を渡します。

<タグ th:replace="~{テンプレートのパス :: フラグメント名(引数の値)}"></タグ>

books.html<head> を、フラグメントの挿入に置き換えます。

templates/books.html
<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head th:replace="~{fragments/layout :: head('書籍一覧')}"></head>
<body>
<h1>書籍一覧</h1>
<!-- ... -->
</body>
</html>

~{fragments/layout :: head('書籍一覧')} は、「templates/fragments/layout.htmlhead フラグメントに、引数 '書籍一覧' を渡したもの」を指します。th:replace は、この <head> をフラグメントの内容で丸ごと置き換えます。

各ページの <head> を同じように置き換えると、<head> の定義は fragments/layout.html の 1 か所にまとまります。スタイルシートを足すときも、フラグメントを 1 か所直すだけで全ページに反映されます。