先日ダイナミックレンダリングの仕組みを簡単に導入できるRendoraというツールがリリースされていたので試してみました。
Rendora
https://rendora.co/
ダイナミックレンダリングとは?
ダイナミックレンダリングとは、特定のユーザーエージェントに対して、クライアント側でレンダリングされるコンテンツとプリレンダリング(事前レンダリング)したコンテンツを切り替えることを指します。
React、Vue、Angularなどの最新のJavaScriptフレームワークで書かれたWebサイトのSEOを向上させることを目的としています。
Googleのダイナミックレンダリングに関する記事
https://developers.google.com/search/docs/guides/dynamic-rendering
Bingのダイナミックレンダリングに関する記事
https://blogs.bing.com/webmaster/october-2018/bingbot-Series-JavaScript,-Dynamic-Rendering,-and-Cloaking-Oh-My
Rendoraとは?
Rendoraはフロントエンド、バックエンドのコードを変更することなく、
ダイナミックレンダリングの仕組みを導入できるGolang製のプログラム(ダイナミックレンダラー)です。
Rendoraはバックエンドの前でリバースHTTPプロキシとして機能します。
Google Bot等のWebクローラー(または指定したユーザーエージェント)からのリクエストを検出し、ヘッドレスChromeでレンダリングを行い、
そのレンダリングしたHTMLをクライアント(Webクローラー)に返します。
Rendoraと似たようなサービスやツールには、Prerender.ioやRendertronがあります。
その他、Rendoraに関する情報を知りたい方はRendora公式サイトをご参照ください。
Rendoraを試す
Rendoraの動きを確認するため、簡単なプロジェクトを作成します。
今回はMac上でRendoraを動かすことに挑戦してみました。
検証環境
- Mac mini (2018) / 16GBメモリ / 3.2GHz Intel Core i7
- macOS Mojave 10.14.1
- Google Chrome 71.0.3578.80 (64ビット)
- node v10.14.1
- npm 6.4.1
- go 1.11.2 darwin/amd64
作業用ディレクトリ作成
作業用のディレクトリを作成して、その中でコードを書いていきます。
# 作業用ディレクトリ作成
mkdir rendora-example && cd $_
Vue.js プロジェクトの作成
バックエンドおよびフロントエンドのコードが必要になるので、手っ取り早くVue CLIでVueプロジェクトを作ります。
npm install -g @vue/cli
vue create client
#=> default を選択
cd client
npm run serve
ブラウザで http://localhost:8080/ にアクセスします。
ソースコードを確認します。
これがクライアント側に返されたHTMLになります。
当然ですが、JavaScriptでレンダリングされるコンテンツはHTMLに含まれておりません。
そのためWebクローラーがアクセスするとコンテンツが存在しないかのように見えます。
そこで、Rendora(とヘッドレスChrome)を使ってダイナミックレンダリングを行います。
Rendoraのビルド
先ほど npm run serve
動いているVueアプリケーションはそのままにしておき、別のターミナルウィンドウを開いて、Rendoraのセットアップを行なっていきます。
Rendoraのバイナリがまだ配布されていないようなので、
今回はRendoraをビルドしてバイナリを生成します。
RendoraはGo言語で書かれています。ビルド環境を整えるために、今回はHomebrewを使ってGo言語をインストールします。
brew install golang
Rendoraのソースコードをgit cloneし、ビルドを行います。
# 別ターミナルウィンドウで
cd rendora-example
git clone https://github.com/rendora/rendora
cd rendora/rendora
go build
/usr/local/include/Block.h:16:3: error: Never include this file directly. Use <lzma.h> instead.
# error Never include this file directly. Use <lzma.h> instead.
^
1 error generated.
おっと、エラーが発生しました。
Google先生に助けを求めます。
"I found a workaround by renaming /usr/local/include to /usr/local/include_old (well, name does not really matter,
just the fact that g++/clang will not search for headers in this folder anymore). "*g++/clang is looking for headers for some reason in this path,
This happens when you migrate from one Mac to another and/or upgrade to HighSierra.
macOSの移行アシスタントを使ってアカウントを移行すると発生することがあるみたいです。
エラーを回避するには/usr/local/include
を/usr/local/include_old
にリネームすると良いとのことでした。
High Sierra以降の方は以下のコマンドを実行します。
sudo mv /usr/local/include /usr/local/include_old
そして、もう一度Rendoraをビルドします。
go build
今度はビルドに成功しました。
rendora
というバイナリが生成されているので、実行してみます。
./rendora -h
#=> Usage of ./rendora:
#=> -c, --config string set config file
#=> pflag: help requested
Usageが表示されました!正しくビルドできたようです。
Rendoraの設定ファイル作成
続いて、Rendoraの設定ファイルであるconfig.yamlファイルを作成します。
cd ../
touch config.yaml
vim config.yaml
debug: true
listen:
address: 0.0.0.0
port: 3001
target:
url: "http://127.0.0.1"
backend:
url: "http://127.0.0.1:8080"
headless:
waitAfterDOMLoad: 0
internal:
url: http://127.0.0.1:9222
output:
minify: false
filters:
userAgent:
defaultPolicy: blacklist
exceptions:
keywords:
# NOTE: キーワードは小文字にすること(大文字だとマッチしない)
- bingbot
- yandex
- curl
server:
enable: true
設定ファイルを作成はこれだけです。
いよいよRendoraを動かしますが、ヘッドレスChromeが必要となるため、事前に起動しておきます。
ヘッドレスChromeの起動
別のターミナルウィンドウを起動して、ヘッドレスChromeを起動します。
# macOS
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --headless --remote-debugging-port=9222
Rendoraの起動
バックエンドのサーバー(Vueアプリケーション)とヘッドレスChromeが起動している状態で、Rendoraを起動します。
./rendora/rendora -c ./config.yaml
動作確認
別ターミナルを開き、curlを使ってHTMLを取得します。
# curlの実行
# NOTE: URL末尾の`?$(date +%s)`はレスポンスをキャッシュさせないためのものです
curl "http://localhost:3001/?$(date +%s)"
すると以下のレスポンスが返ってきます。
<!DOCTYPE html><html lang="en"><head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="/favicon.ico">
<title>client</title>
<link href="/app.js" rel="preload" as="script"><style type="text/css">
h3[data-v-469af010] {
margin: 40px 0 0;
}
ul[data-v-469af010] {
list-style-type: none;
padding: 0;
}
li[data-v-469af010] {
display: inline-block;
margin: 0 10px;
}
a[data-v-469af010] {
color: #42b983;
}
</style><style type="text/css">
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style></head>
<body>
<noscript>
<strong>We're sorry but client doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"><img alt="Vue logo" src="/img/logo.82b9c7a5.png"><div data-v-469af010="" class="hello"><h1 data-v-469af010="">Welcome to Your Vue.js App</h1><p data-v-469af010="">
For a guide and recipes on how to configure / customize this project,<br data-v-469af010="">
check out the
<a data-v-469af010="" href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p><h3 data-v-469af010="">Installed CLI Plugins</h3><ul data-v-469af010=""><li data-v-469af010=""><a data-v-469af010="" href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li><li data-v-469af010=""><a data-v-469af010="" href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li></ul><h3 data-v-469af010="">Essential Links</h3><ul data-v-469af010=""><li data-v-469af010=""><a data-v-469af010="" href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li><li data-v-469af010=""><a data-v-469af010="" href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li><li data-v-469af010=""><a data-v-469af010="" href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li><li data-v-469af010=""><a data-v-469af010="" href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li><li data-v-469af010=""><a data-v-469af010="" href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li></ul><h3 data-v-469af010="">Ecosystem</h3><ul data-v-469af010=""><li data-v-469af010=""><a data-v-469af010="" href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li><li data-v-469af010=""><a data-v-469af010="" href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li><li data-v-469af010=""><a data-v-469af010="" href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li><li data-v-469af010=""><a data-v-469af010="" href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li><li data-v-469af010=""><a data-v-469af010="" href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li></ul></div></div>
<!-- built files will be auto injected -->
<script type="text/javascript" src="/app.js"></script>
</body></html>
プリレンダリングされたHTMLが返ってきました!感激!
所感
クライアントとサーバーの間にRendoraを入れるだけで簡単にダイナミックレンダリングの仕組みが導入できました。
Rendoraはまだリリースされたばかりなので、機能的に足りていない機能やブラウザで開くとリソースの取得が完了しない等の不安定な部分もあります。
本番環境で使うにはまだ早いですが、ダイナミックレンダリングの仕組みを知るということにおいて、参考となるプロジェクトです。
今後が楽しみですね!
興味を持った方はRendoraを試してみてください!