地元のグルメ情報を紹介するWebアプリをVue.jsで作り、WordPressで運営しているサイトのサブディレクトリー上に公開しました。また。WebアプリのURLがSNSでシェアされた時に写真が展開されるよう、OGPに対応させています。
この記事ではWordPressを使っているサイトにVue.jsのアプリを同居させ、プリレンダリングを使ってOGPに対応させる方法について紹介します。WordPressは https://example.com で運営され、そこにVueアプリを https://example.com/app-name/ で公開するものとします。
前提とする環境
Vue CLIを使ってアプリを開発しています。
$ npm install -g @vue/cli
$ vue --version
@vue/cli 4.2.3
ルート以外でVueアプリを公開する
開発したアプリはルートディレクトリ https://example.com/ で公開されるように作られています。これを、サブディレクトリ https://example.com/app-name/ で公開されるように変更します。
WordPressをサーバーのディレクトリー/
にインストールしている場合、その下に新しく/app-name
ディレクトリーを作り、Vueアプリの成果物をアップロードします。
最初はWordPressの動作をよく理解していなかったので、
- /app-name/ でアクセスできるページが必要?
- それなら「固定ページ」で app-name を作ればいいのでは?
と誤解していたのですが、そんなことは不要でした。
vue.config.js を変更する
vue.config.js に publicPath [公式]、outputDir [公式] を追記します。
module.exports = {
publicPath: '/app-name/',
outputDir: 'dist/app-name/',
...
}
まず、開発用サーバーで正しく動作するかを確認します。
$ npm run serve
...
DONE Compiled successfully in XXXXms
App running at:
- Local: http://localhost:8080/app-name/
- Network: http://XXX.XXX.XXX.XXX:8080/app-name/
Note that the development build is not optimized.
To create a production build, run npm run build.
Webブラウザーで http://localhost:8080/app-name/ にアクセスして、正しく動作することを確認します。
public ディレクトリーに.htaccessを作成してビルド
WordPressはWebブラウザーが/app-name
にアクセスした時に/app-name/index.php
を読み込むように設定されています。その設定はサーバーの/.htaccess
に書いてあります。
Vue.jsでは/app-name
にアクセスした時に(index.phpではなく)/app-name/index.html
を読み込むことを期待します。そこで、/app-name/.htaccess
を準備して/app-name
にアクセスしたときの動作を変更します。ここで作成する.htaccessファイルは開発マシンのpublicディレクトリーに作ります。
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /app-name/
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.html [L]
</IfModule>
参考:WordPressのサブディレクトリでVueアプリを動かす
次にビルドを実行して、dist/app-name/ に成果物が出力されることを確認します。なお、vue uiでbuildタスクを動かす場合、vue.config.js の outputDir が無視されます。UIからoutputを設定してください。
参考:outputDir config not working #2639
$npm run build
...
DONE Build complete. The dist/gourmet directory is ready to be deployed.
INFO Check out deployment instructions at https://cli.vuejs.org/guide/deployment.html
WordPressの.htaccessを書き換える
生成された dist/app-name をサーバーの/app-name
としてアップロードします。ここで https://example.com/app-name にアクセスしても、まだVueアプリは動作しません。サーバー側の/.htaccess
を修正して、/app-name
へのアクセスだけは/app-name/.htaccess
に書いたルールで動作させる必要があります。
修正を間違えるとWordPressが正しく動作しなくなるため、.htaccessファイルのコピーを別名で作ってから作業してください。
# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !(^/app-name/) # この行を追加
RewriteRule . /index.php [L]
</IfModule>
# END WordPress
参考:WordPressのサブディレクトリでVueアプリを動かす
https://example.com/app-name にアクセスしてVueアプリが正しく動作することを確認してください。
VueアプリをOGPに対応させる
OGPではシェアしたときに表示したい写真のURLなどをHTMLの
タグにあるタグに記述します。そのページのURLがSNSでシェアされた時に、SNSのボットがそのURLにアクセスし、metaタグを解析することでSNSに表示する情報を決めています。VueアプリのトップページをOGPに対応させる
public/index.html
を編集して、OGPに対応させます。このファイルはビルドにより dist/app-name/index.html
として出力されるのでそのままサーバーにアップロードして公開します。
<!DOCTYPE html>
<html lang="ja">
<head prefix="og:http://ogp.me/ns# fb:http://ogp.me/ns/fb# website:http://ogp.me/ns/website#">
<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>[アプリ名]</title>
<meta name="description" content="[アプリの説明]" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="[サイト名]" />
<meta property="og:title" content="[アプリ名]" />
<meta property="og:url" content="https://example.com/app-name/" />
<meta property="og:image" content="https://example.com/app-name/img/app-name.jpg" />
<meta property="og:description" content="[アプリの説明]" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@[アプリのTwitterアカウント名]" />
<meta name="twitter:player" content="@[アプリのTwitterアカウント名]" />
</head>
<body>
<noscript>
<strong>We're sorry but vue_app doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
公開後、OGPの情報が正しく設定されているかを確認するため、以下のツールにアプリのURLを入力して動作を確かめます。
Vue Routerで作ったページをOGPに対応させる
OGP対応で問題になるのが、Vue Routerを使ったアプリです。
OGPの情報を収集するFacebookやTwitterのボットはJavaScriptに対応していません。なので、Vue Routerで生成したURLにボットがアクセスしてもOGPの情報を収集することがでず、そのURLをSNSでシェアしても、写真などが展開されることはありません。
そこでビルドの際にヘッドレスブラウザーPuppeteerを使ってプリレンダリングを実施し、Vue RouterのURLごとにindex.htmlを成果物として作成します。出来上がったindex.htmlをそのままサーバーにアップロードして公開すれば、ボットがOGPの情報を収集できるようになります。
vue-meta を使ってURLごとにOGPを設定する
Nuxt.jsに含まれるvue-metaを使えば、URLごとに異なるOGP情報を設定できます。
vue-metaを使う時に気をつける必要があるのは、変更するタグには data-vmid 属性を付与することです。
ここでは、description
, og:title
, og:url
, og:image
, og:description
の5つを変更するため、public/index.html
を次のように修正します。
...
<meta data-vmid="description" name="description" content="[アプリの説明]" />
<meta data-vmid="og:title" property="og:title" content="[アプリ名]" />
<meta data-vmid="og:url" property="og:url" content="https://example.com/app-name/" />
<meta data-vmid="og:image" property="og:image" content="https://example.com/app-name/img/app-name.jpg" />
<meta data-vmid="og:description" property="og:description" content="[アプリの説明]" />
...
その上で、vue-meta をインストールして、URLに応じてタグを書き換えるために metaInfo() を記述します。
$ npm install --save vue-meta
...
<script>
...
data: {
return: function() {
id: '...',
titie: '...',
desc: '...'
}
},
metaInfo() {
return {
title,
meta: [
{ vmid: 'description', name: 'description', content: desc },
{ vmid: 'og:title', property: 'og:title', content: title },
{ vmid: 'og:description', property: 'og:description', content: desc },
{ vmid: 'og:url', property: 'og:url', content: `https://example.com/app-name/spots/${this.id}/` },
{ vmid: 'og:image', property: 'og:image',
content: `https://example.com/app-name/img/${this.id}.jpg`
}
]
}
}
}
</script>
prerender-spa-plugin を使ってビルドの際にプリレンダリングを実施する
prerender-spa-pluginを使ってビルド時に開発用サーバーを起動し、PupeteerでVueアプリにアクセスすることでHTMLを生成していきます。
ここではVueアプリのURLとして /app-name/spots/00001/ から /app-name/spots/00100/ が存在するとします。
$ npm install --save prerender-spa-plugin
staticDir
は app-name なし、indexPath
は app-name ありで index.html まで指定することに気をつけてください。
なお、アクセスする先の /app-name/spots/:id は、アクセスしてから <div id="spot" />
が表示されるまでに少し時間がかかるので、Puppeteer にそのタグが出るまで待たせるようにしています。皆さんのアプリには不要なので削除してご利用ください。
const path = require('path')
const PrerenderSPAPlugin = require('prerender-spa-plugin')
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer
module.exports = {
publicPath: '/app-name/',
outputDir: 'dist/app-name/',
configureWebpack: () => {
if (process.env.NODE_ENV === 'production') {
const routes = []
for (let i = 1; i <= 100; i++) {
let id = String(i).padStart(5, '0')
routes.push(`/app-name/spots/${id}/`)
}
return {
plugins: [
new PrerenderSPAPlugin({
staticDir: path.join(__dirname, 'dist'),
indexPath: path.join(__dirname, 'dist/app-name/index.html'),
routes,
renderer: new Renderer({
renderAfterElementExists: '#spot'
}),
})
]
}
}
}
}
あとはビルドを実行して、dist/app-name/spots
の下に 00001/index.html, ..., 00100/index.html がそれぞれ出力されて、タグに期待通りの情報が記述されていることを確認してください。
参考:プリレンダリングを用いてVue.jsのSPAをビルドする導入から設定まで
問題がなければdist/app-name
をサーバーにアップロードし、Facebook デバッガーなどで https://example.com/app-name/spots/00001/ などが期待通りに動作するか確認してください。