Thymeleafのth:replaceをobjectタグで再現しようとしたけどダメだった

【前提条件】

[環境]

【概要】

Thymeleafのth:replaceをタグでエミュレートできないものかと実験してみた結果、
近しいところまではいったけど、実運用に耐えられるレベルまでは再現できませんでしたと言うお話です。

【経緯】

th:replaceのところを普通のHTMLで再現できないだろうか?

HTML Importsを使えばいけるんじゃね?

ファイルシステムだとクロスサイトリクエストになってエラーになりました。

「objectタグを使えばいけるんじゃね?」という助言をいただく

やってみた

【ソース】

画面上部のtopMenu.html、左ペインのgenreSearch.html、右ペインのreview.html、
それらをまとめたtop.htmlという4つのファイルからなるゲームレビューっぽいページです。

今回、タグを試して見たのはtop.htmlです。

[top.html]

今回のエントリのメインとなるページです。

<!DOCTYPE html>
<html>
    <head th:include="/common/base/metaInfo :: commonHeader (title='Top Page')" >
        <title th:text="${title}">テストタイトル</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <link rel="stylesheet" href="../../../css/normalize.css" th:href="@{/css/normalize.css}" />
        <link rel="stylesheet" href="../../../css/base.css" th:href="@{/css/base.css}" />
    </head>
    <body>
        <div class="bs-content-area">
            <div th:replace="/common/component/topMenu :: topMenu">
                <object data="../common/component/topMenu.html" style="width: 100%; height: 80px;"></object>
            </div>
            <div class="bs-left-menu">
                <div th:replace="/genreSearch/genreSearch :: genreSearch">
                    <object data="../genreSearch/genreSearch.html"></object>
                </div>
            </div>
            <div class="bs-main-contents">
                <div th:replace="/review/review :: reviewArea(reviewView=${it.review})">
                    <object data="../review/review.html" style="width: 100%;height: 600px;"></object>
                </div>
            </div>
        </div>
    </body>
</html>

th:replaceしている下にタグをつけるだけです。
data属性には対応するhtmlのパスを指定します。

タグはインライン要素らしいので、widthは100%にしないと幅が合わないです。
heightも指定しないとスクロールバーが表示されます。

[topMenu.html]
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
    <head>
        <title>Top Page</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <link rel="stylesheet" href="../../../../css/normalize.css" th:href="@{/css/normalize.css}" />
        <link rel="stylesheet" href="../../../../css/base.css" th:href="@{/css/base.css}" />
    </head>
    <body>
        <div class="bs-top-menu" th:fragment="topMenu">
            <ul class="right-menu">
                <li><a href="" th:href="@{/top}" >TOP</a></li>
                <li><a href="../../login/login.html" th:href="@{/login}" >ログイン</a></li>
            </ul>
        </div>
    </body>
</html>
[genreSearch.html]
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
    <head>
        <title>ジャンル検索</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <link rel="stylesheet" href="../../../css/normalize.css" th:href="@{/css/normalize.css}" />
        <link rel="stylesheet" href="../../../css/base.css" th:href="@{/css/base.css}" />
    </head>
    <body>
        <div class="cp-genre-search" th:fragment="genreSearch">
            <h1>ジャンル検索</h1>
            <ul>
                <li th:each="genre : ${it.genreSearch.genres}">
                    <a th:href="@{/genreSearch/id/{id}(id=${genre.id})}" th:text="${genre.title}">テスト</a>
                </li>
                <li th:remove="all">
                    <a href="#">アクション</a>
                </li>
                <li th:remove="all">
                    <a href="#">RPG</a>
                </li>
            </ul>
       </div>
    </body>
</html>
[revie.html]
<!DOCTYPE html>
<html>
    <head th:fragment="commonHeader">
        <title>レビューページ</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <link rel="stylesheet" href="../../../css/normalize.css" th:href="@{/css/normalize.css}" />
        <link rel="stylesheet" href="../../../css/base.css" th:href="@{/css/base.css}" />
    </head>
    <body>
        <div class="fm-review" th:fragment="reviewArea (reviewView)">
            <ul th:each="review : ${reviewView.reviews}">
                <li>
                    <h2 th:text="${review.title}">テストタイトル</h2>
                    <dl>
                        <dd>熱中度</dd>
                        <dt>5点</dt>
                        <dd>ストーリー</dd>
                        <dt>4点</dt>
                        <dd>操作性</dd>
                        <dt>3点</dt>
                        <dd>ロード時間</dd>
                        <dt>2点</dt>
                        <dd>音楽</dd>
                        <dt>1点</dt>
                    </dl>
                    <h3>良い点</h3>
                    <p th:text="${review.goodPoint}">
                        テストテストテスト。
                    </p>
                    <h3>悪い点</h3>
                    <p th:text="${review.badPoint}">
                        テストテストテスト。
                    </p>
                    <h3>コメント</h3>
                    <p th:text="${review.comment}">
                        テストテストテスト。
                    </p>
                </li>
            </ul>
        </div>
    </body>
</html>

【画面】

ということでファイルシステム上から見た画面とThymeleafを経由した画面を比較してみます。

[Thymelaef経由]

見た目は結構再現できていそうです。

【何がダメか?】

まず、ピクセル単位ではあわないです。
おおよそは一致しているのでWebデザイナとの協業をしていない場合は
ピクセル単位があわないことはそこまで大きな問題じゃないかなと。
結構、調整する必要はありそうですが。

次に問題なのが、DOM構成が実際のものとは異なる。
見た目だけ違いなら大きくはないかなと思っていたのですが、
DOM構成が大きく変わってしまうのは後々問題になりそう。

エンジニアだけで開発してて、画面はThymeleafで生成されたものを
正式なものとする場合はたぶん問題ないです。

【まとめ】

素直にApacheを使ってHTML Importsするのが良さそうですね・・・