フレームワークのようにベースとなるHTMLをつくる。
成果物
extends
ベースとなるHTMLをつくり、その内側の一部分だけ変更したいときがある。そのときにextendsを使う。
templates/base.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> {%- block head %}{% endblock -%} </head> <body> {%- block content %}{% endblock -%} </body> </html>
headとcontentというブロック要素が可変部分である。
{%- block head %}{% endblock -%}
{%- block content %}{% endblock -%}
これを各テンプレートごとに少しずつ変えて書く。
- index.html
- section.html
- page.html
templates/index.html
{% extends "base.html" %}
{%- block head %}
<title>{{ config.title }}</title>
{% endblock -%}
{%- block content %}
<h1>{{ config.title }}</h1>
<p>{{ config.description }}</p>
{% endblock -%}
まずはファイルの先頭で{% extends "base.html" %}する。
つぎにベースで定義したblock要素に書き出す内容を書いていく。
templates/section.html
{% extends "base.html" %}
{%- block head %}
<title>{{ section.title }} | {{ config.title }}</title>
{% endblock -%}
{%- block content %}
<ul>
{% for page in section.pages %}
<li><a href="{{ page.permalink }}">{{ page.title }}</a></li>
{% endfor %}
</ul>
{% endblock -%}
templates/page.html
{% extends "base.html" %}
{%- block head %}
<title>{{ page.title }} | {{ config.title }}</title>
{% endblock -%}
{%- block content %}
{{ page.content | safe }}
{% endblock -%}
気に食わない点
include,import,extendsを使い分けねばならない{% block * %}{% endblock * %}は必須で冗長
可読性が低い。パッと見よくわからない。以下のように短く書きたかった。
base.html
<head>{$ head $}</head> <body>{$ content $}</body>
継承する側も以下のように短く書きたかった。
index.html
{$ base.html $}
$head
<title></title>
$content
<h1></h1>
罠
マクロを隠しインクルードする技はextendsで使えない
以下のようなマクロを作ったとする。
templates/macro/footer.html
{% macro footer() %}
<footer>© 2021 {{ config.extra.author }}</footer>
{% endmacro footer %}
マクロ呼出だけをテンプレートにする。
templates/include/footer.html
{% import "macro/footer.html" as macro %}
{{ macro::footer() }}
マクロ呼出テンプレをベーステンプレでインクルードする。
templates/base.html
{%- include "include/footer.html" %}
ベースをほかのテンプレで継承する。
templates/index.html templates/section.html templates/page.html
{% extends "base.html" %}
実行するも、エラーになる。
zola serve
Failed to build the site Error: Failed to render page '/tmp/work/0/ytyaru-zola/content/archive/index.md' Reason: Failed to render 'page.html' (error happened in 'base.html'). Reason: Macro namespace `macro` was not found in template `base.html`. Have you maybe forgotten to import it, or misspelled it?
「macroというマクロ名前空間が見つからない」と怒られた。おそらく「importはファイルの先頭に書かねばならない」ルールに抵触したせいだろう。今回のテンプレではインポートがファイルの途中に書き出されてしまう。以下のように。ファイルの先頭にないせいで正しくインポートできず、マクロ名前空間も存在しない。よって本件のエラーになったものと推測する。
<!DOCTYPE html> <html> ... {% import "macro/footer.html" as macro %} {{ macro::footer() }} ... </body> </html>
今回わかったことは以下。
- インポートとマクロ呼出は直接ベース(
extends元)に書かねばならない
それだとゴチャゴチャになるんだよなぁ。それを避けたくてマクロ呼出をインクルードする技を使ったのになぁ。
残念。
include, macro(import), extendsのちがい
フレームワークにすべき部分はどこか?
その答えは全ページに共通している部分だ。かなり限られていると思う。<html>や<body>など必須の要素のみが対象だ。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> {%- block head %}{% endblock -%} </head> <body> {%- include "include/header.html" %} {%- block content %}{% endblock -%} {%- include "include/footer.html" %} </body> </html>
lang属性
これはページごとに変わりうる
メタデータ
ファビコンやビューポートなら変わらないかもしれない。
<meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="shortcut icon" href="favicon.svg">
個人サイトなら著者も同じだろう。
<meta name="author" content="{{ config.extra.author }}">
タイトル、説明はほぼ違う。
<title>{{ page.title }} | {{ config.title }}</title> <meta name="description" content="{{ page.description }}">
変数を使えば共通化できるかもしれないが、pageやsectionなど変数自体が別物だと統一できない。
<title>{{ section.title }} | {{ config.title }}</title> <meta name="description" content="{{ page.description }}">
条件分けすれば共通化できるか? 未確認。
<title> {%- if page and page.title %}{{ page.title }} | {% endif -%} {%- if section and section.title %}{{ section.title }} | {% endif -%} {%- if config and config.title %}{{ config.title }}{% endif -%} </title>
CSSは変わりうる。メインコンテンツ次第。
<link rel="stylesheet" href="/css/black.css""> <link rel="stylesheet" href="/css/white.css""> <link rel="stylesheet" href="/css/mobile.css" media="screen and (max-width: 899px)"> <link rel="stylesheet" href="/css/pc.css" media="screen and (min-width: 900px)">
メインコンテンツ。これはもう絶対に違う。パターン化できたとしてもセクション単位。サイト全体ではほぼ間違いなく変わる。[zola][]においてもindex.html, section.html, page.htmlの3種類の異なるテンプレートがあるくらいだ。最低でも「記事ページ」とその「一覧ページ」の2種類は必要だ。それらの内容が同じわけがない。
<main><article><h1>{{ page.title }}</h1><p>{{ page.description }}</p>{{ page.content | safe }}</article>bbbbbbbbbbbbb</main> <aside> <nav class="pagination"> {% if page.earlier %}<a class="previous" href="{{ page.earlier.permalink }}">‹ {{ page.earlier.title }}</a>{% endif %} {% if page.earlier and page.later %} {% endif %} {% if page.later %}<a class="next" href="{{ page.later.permalink }}">{{ page.later.title}} ›</a>{% endif %} </nav></aside> <aside><!-- ad --></aside>
<main>/<aside>/<article>の大枠くらいは共通できそうな気がする。
<main><article><h1>{{ page.title }}</h1><p>{{ page.description }}</p>{{ page.content | safe }}</article></main>
よくある前後記事リンク。これは欲しいときとないほうがいいときがありそう。
``html
メイン以外。広告とかサイドバーなど。でもこれって、そもそも存在しないほうが嬉しいものなんだよね。共通化して全ページで出したら邪魔すぎて殺意をいだくやつ。
もし`<body>`タグ内で全ページ共通するところがあるとすれば`<header>`や`<footer>`くらいだろう。
* `about`
* `author`
* `archive`
* `feed`
* `sitemap`
* `search`
* `language`
* `style`
* `Github Repository`
* `twitter`
* `copyright`
* `license`
* `Site Policy`
パンくずリストは階層によって変わる。なので全ページ共通にはできない。マクロを使えば共通化できるかもしれないが、今の知識ではわからない。
こうして考えてみると、以下のような問題がある。
* 共通化できるか?
* どうやって共通化する?: `include`, `macro`(`import`), `extends`
そもそも共通化できるかどうかわからない。その答えにたどりつくには[zola][]や[tera][]に精通している必要がある。そして、それができているなら、共通化する手段についても当たりがつく。
結局、[zola][]や[tera][]を学ぶしかない。
* `<html>`
* `<head>`
* `<meta charset="utf-8">`
* `<title>{{ page.title }} | {{ config.title }}</title>`
* `<meta name="description" content="{{ page.description }}">`
* `<meta name="author" content="{{ config.extra.author }}">`
* `<meta name="viewport" content="width=device-width, initial-scale=1">`
* `http-equiv`
* `<meta http-equiv="content-language" content="ja">`
* `<html lang="ja">`が正しい
* べつにいらない
* `<meta http-equiv="default-style" content="default.css">`
* OGP
* `<meta property="og:title" content="ページタイトル">`
* `<meta property="og:type" content="article">`
* `<meta property="og:site_name" content="サイト名">`
* `<meta property="og:url" content="ページURL(絶対パス)">`
* `<meta property="og:image" content="ページOGP画像パス(絶対パス)">`
* `<meta property="og:description" content="ページ説明文">`
* https://ogp.me/
* `og:audio`
* `og:video`
* `og:image`
* `og:type`
* `music.song`
* `music.album`
* `music.playlist`
* `music.radio_station`
* `video.movie`
* `video.episode`
* `video.tv_show`
* `video.other`
* `article`
* `book`
* `profile`
* `website`
* ``
* ``
* ``
* ``
* ``
* ``
* ``
* ``
* ``
* ``
* ``
* ``
* ``
* ``
* Audio
* `og:audio`
* `og:description`
* `og:determiner`
* `og:locale`: `ja_JP`
* `og:locale:alternate`: `en_US`
* `og:site_name`
* `og:video`
* `<>`
* `<body>`
# 所感
# 対象環境
* <time datetime="2021-08-02T17:28:01+0900" title="実施日">2021-08-02</time>
* [Raspbierry pi](https://ja.wikipedia.org/wiki/Raspberry_Pi) 4 Model B
* [Raspberry Pi OS](https://ja.wikipedia.org/wiki/Raspbian) buster 10.0 2020-08-20 [※](http://ytyaru.hatenablog.com/entry/2020/10/06/111111)
* [bash](https://ja.wikipedia.org/wiki/Bash) 5.0.3(1)-release
$ uname -a Linux raspberrypi 5.4.83-v7l+ #1379 SMP Mon Dec 14 13:11:54 GMT 2020 armv7l GNU/Linux