Chienomi

はるらぼをハイブリッドSPA化、検索エンジン対応に

開発::website

  • TOP
  • Articles
  • 開発
  • はるらぼをハイブリッドSPA化、検索エンジン対応に

はるらぼは「PureBuilder Simply + Vanilla JS SPA」というなかなかキワモノ構成のサイトである。

多少動作の問題はあれどおよそ良好に動作していたのだが、大きな問題として「検索エンジンに全くかからない」というのがあった。 ページロード時にfetch APIを使って本文をロードしているのだが、GoogleもBingも対応できないらしい。

これまではあまり気にしていなかったが、公開予定のドキュメントがなかなか大事なものになるため、検索インデックスを気にしてハイブリッドSPA化した。

これは今のところ見た目には分からない変更だ。

PureBuilder SimplyでハイブリッドSPA

PureBuilder Simplyには(まだリリースされていない)3.3でjsonout機能が追加されたため、SPAに対応しやすくなっている。

だが、ハイブリッドSPAを実現するためには、「完全なHTML」と「SPA用のデータ」の2種類が必要となる。

ここで役に立つのがHOOK機能だ。 実は非常に単純で、.pbsimply-hooks.rbとして次のようにした

#!/usr/bin/ruby
require 'fileutils'
require 'json'

def (PBSimply::Hooks).load_hooks h
  h.process << ->(v) {
    unless File.exist? File.dirname(v[]).sub("/page/", "/data.spa/")
      FileUtils.mkdir_p(File.dirname(v[]).sub("/page/", "/data.spa/"))
    end

    File.open(v[].sub("/page/", "/data.spa/").sub(/\.html$/, ".json"), "w") do |f|
      IO.popen(["pandoc", "--shift-heading-level-by", "1", v[]]) do |io|
        body = io.read
        f.puts JSON.dump({
          "title" => v[]["title"],
          "date" => v[]["date"],
          "body" => body
        })
      end
    end
  }
end

これで/page/以下に完全なドキュメントが、/data.spa/以下にSPA用のJSONが置かれるようになる。 JSONのドキュメントは、別途Pandocで生成している。

これでドキュメントのリンクを/page/以下にすれば、普通にHTML文書として成立し、JavaScriptのないシステムでも読むことができる。

スクリプトはリンクを置き換える処理を修正、基本的には/page以下へのa要素を使い、

メニューボタンの挙動変更

記事内のリンクはこれで問題ないが、静的なページとして動作するようにするためには、トップページからの導線と、メニューナビゲーションをなんとかしないといけない。

トップページに関しては単なるスタート画面なので、noscript要素を使ってトップページへのリンクを設置する。

メニューナビゲーションはaboutに関しては単純なページへのリンクであり、loadlink()を呼んでいるだけだから、これをa要素化する。

メニューに関しては、menu_show()を呼ぶためのボタンなので、a要素化した上で、通常はアクセスできないフォールバック用のメニューページを用意する。

すごく微妙な話として、この構造変更でa > divの形式になったのだけど、これがDilloやw3mだと動作しないし、エラーになる。

これはHTML4においてadiv要素を子要素に持てなかったためで、エラーという扱いになるようだ。 この問題はChromiumやFirefoxはもちろん、NetSurfでも発生しない。

この対策は非常に単純で、先頭に

<!DOCTYPE html>

を入れるとHTML5としてパースされるようになり、意図したように動作する。

HTML5でのDTD宣言は意味がないように言われるが、こういう非常に微妙なところでDOCTYPE宣言の有無が問題になったりする。

SPA vs Static

実際にこの仕様でどう動作するかというと、Chromium, Firefox, Epiphany (Gtk WebKit)はSPAとして動作し、NetSurf, Dilloは静的ページとして動作する。

w3mの場合、メニューを表示するスクリプトは機能するが、fetch APIは動作せず、ページ読み込みはstaticに行われる。

いずれの場合でも問題なく表示・動作可能なので、さすがにこれで検索エンジンにインデックスされるようになるだろう。

Hybrid SPAしたい?

「ハイブリッドSPAいいじゃん!」って思う人がどれくらいいるかは分からないけど、需要があるようだったらPureBuilder Simplyのサポートプロジェクトとして作って公開してもいいかもしれない。