親バカエンジニアのナレッジ帳

webのエンジニアをやっており、日頃の開発で詰まったことや書き残しておきたいことを載せています。

Spring BootでページごとにSPAとAMPを切り分ける方法


Spring BootでSPAとAMPの併用

Spring Bootでは基本的にフロント側はThymeleafを使用し、サーバサイドで取得した変数を活用して描画させますが、HTMLの組み方によってはAMPページもThymeleafで作成することができます。
一方SPAはSingle Page Application(シングルページアプリケーション)の名の通り、基本的にはどのURLからのアクセスでもindex.htmlなどの固定のhtmlファイルでページを開いてから、Ajaxなどの非同期通信で情報を取得してJavascriptで描画させることになります。

まったく別の技術を使用している両者ですが、URLによる切り分けで共存させることは可能です。
方法としては、サーバサイド(Spring Boot)側のコントローラーでURLによって呼び出すテンプレートを切り分けることで、両者の併用は可能になります。
ちなみにフロント側では、AMPページは一部を除いてJavascriptが動作しないため、SPAの実装の中にAMPを導入するという方法を取ることはできません。
あくまでサーバサイドのコントローラーで切り分けてください。

以下に実装方法を記載しますが、サーバサイドはJavaのSpring Bootを使い、フロントはvue-cliによるVue.jsを使用しています。



Thymeleafの文法チェックが難点

対応に当たって一番詰まった点としては、Thymeleafの厳しい文法チェックエラーです。
ThymeleafではnekoHTMLというライブラリを使えば厳しい文法チェックエラーを避けることができますが、nekoHTMLを使えば今度はAMPページのバリデーションチェックで以下のようなエラーが出続けます。

AMP validation had errors:
The tag 'head > style[amp-boilerplate]' appears more than once in the document. (see https://github.com/ampproject/amphtml/blob/master/spec/amp-boilerplate.md)
The tag 'noscript > style[amp-boilerplate]' is missing or incorrect, but required by 'head > style[amp-boilerplate]'. (see https://github.com/ampproject/amphtml/blob/master/spec/amp-boilerplate.md)
The mandatory tag 'noscript > style[amp-boilerplate]' is missing or incorrect. (see https://github.com/ampproject/amphtml/blob/master/spec/amp-boilerplate.md)

よって、nekoHTMLは使えないのですが、そうなると今度はVue.js側でThymeleafの文法エラーで引っかかります。

例えばVue.jsでコンパイル後に作成されるindex.htmlでは以下の記述がありますが、

<link href="/static/css/app.xxxxxxxxxxxxxxxxxxxxxxxx.css" rel="stylesheet">

このあたりなどはlinkの閉じがないために以下のような文法チェックエラーになってしまいます。

org.xml.sax.SAXParseException: The element type "link" must be terminated by the matching end-tag "</link>".

よって面倒ですが以下のように閉じなければいけません。

<link href="/static/css/app.xxxxxxxxxxxxxxxxxxxxxxxx.css" rel="stylesheet" />

ただ、これをコンパイルのたびに閉じ直すのは、面倒な上に忘れてしまいがちです。

replace-in-fileで解決

ここで便利なのが、「replace-in-file」というnpmのパッケージです。
その名の通り、ファイルを指定して文字を置換してくれるパッケージです。
本当はこのような無理矢理のやり方でなくてもいい方法があるのかもしれませんが、1つの手段として参考にしていただければと思います。

使う時はnpmでインストールしてください。

npm install replace-in-file --save

まずbuild.jsで、「rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {...」と記述がある前に以下の一行を入れてインポートしてください。

var replace = require('replace-in-file')

そして「console.log(chalk.cyan(' Build complete.\n'))」の記述の前には以下を入れてください。

const options = {
  files: '../resources/templates/index.html',
  from: 'rel="stylesheet">',
  to: 'rel="stylesheet" />'
}
try {
  replace(options)
} catch (error) {
  console.error('Error occurred:', error)
}

これでindex.htmlファイルの「rel="stylesheet">」の部分が「rel="stylesheet" />」に置換されるのです。



サーバサイドで切り分け

あとはSpring Bootのコントローラーで切り分けをすれば完了です。
SPAで開きたいURLの方はindex.htmlを、AMPで開きたい時にはそのhtmlファイル(今回は例としてamp-index.htmlとします)を開くようにしてください。

@Controller
public class TestPageController {

	// SPAで開きたいURLの場合
	@RequestMapping(value = "spa"}, method = RequestMethod.GET)
	String indexSpa() {
		return "index";
	}

	// AMPで開きたいURLの場合
	@RequestMapping(value = "amp"}, method = RequestMethod.GET)
	String indexAmp() {
		return "amp-index";
	}

}



基礎から学ぶ Vue.js

基礎から学ぶ Vue.js

Vue.jsとFirebaseで作るミニWebサービス (技術書典シリーズ(NextPublishing))

Vue.jsとFirebaseで作るミニWebサービス (技術書典シリーズ(NextPublishing))

Spring Boot プログラミング入門

Spring Boot プログラミング入門