半分ネタですが、切実な必要があり試行錯誤した結果です。
どういうこと?
比較的ページ数のあるサイトの作成において、
- 汎用パーツのコーディング品質担保のため、コンポーネントベースの開発が理想的だった
- Vue.js のコンポーネントを利用した開発を検討
- さまざまな理由で、サイトでの Vue.js での利用が NG になった
- 開発は Vue のコンポーネントを利用し、プリレンダリング出力した静的 HTML と CSS をプロダクション環境に使うことにした
通常プリレンダリングは SEO 対策のために実施するものですが、今回はあくまでもコンポーネントベースでの開発、および静的 HTML と CSS の生成を目的としています。
サンプル
サンプルコード一式は、こちらになります。
https://github.com/megurock/vue-as-template-engine
こういう感じでコーディングします
特に意味はありませんが、 HelloWorld コンポーネントを v-for と Pug の while 構文 でそれぞれ書いてみた例です。この比較では v-for の方がシンプルで使い勝手が良いですが、プリレンダリングされてしまえば、どちらにも差異はありません。
section
h2 v-for でループ
hello-world(msg="Hello, Vue!" v-for="n in 2")
section
h2 Pug の while 構文でループ
- var count = 0
while ++count <= 2
hello-world(msg="Hello, Pug!!")
こちらは 単一ファイルコンポーネント で作成した HelloWorld コンポーネントです。msg プロパティで受け取った文字列を、<p class="hello-world">
として出力します。
<template lang="pug">
p.hello-world {{ msg }}
</template>
<script lang="ts">
export default {
props: {
msg: { type: String, default: 'Hello, World!' }
}
}
</script>
<style lang="scss">
.hello-world { color: #42b983; }
</style>
プリレンダーするとこうなります
プリレンダーを実行することで、以下の html と css が出力されます。
$ yarn prerender
<section>
<h2>v-for でループ</h2>
<p class="hello-world">Hello, Vue!</p>
<p class="hello-world">Hello, Vue!</p>
</section>
<section>
<h2>Pug の while 構文でループ</h2>
<p class="hello-world">Hello, Pug!!</p>
<p class="hello-world">Hello, Pug!!</p>
</section>
.hello-world { color: #42b983; }
プリレンダリングについて
Vue.js のプリレンダリングには、Prerender SPA Plugin や、Nuxt の generate を使った実装が有名だと思いますが、これらは SPA(1 ページにひとつの単一ファイルコンポーネント)を想定しているようなので、今回は prerenderer プラグイン を使いました。
プリレンダーのスクリプトは、ほぼ サンプルコード のままです。Puppeteer と JSDOM のいずれかを使ったレンダラ―を選択できるのですが、JSDOM 実装の方は高速で、一方 Puppeteer の方は信頼度が高いとのことです。
ちょっとした注意点なのですが、prerenderer プラグインはまだ動作が安定していないとのことらしく、実際に大量のページを対象にプリレンダリングを実行すると、Node の非同期処理でタイムアウトエラーを起こしてしまうことがありました。回避策として、対象ページの指定に glob を適用し、適宜ファイル数を絞りつつ実行できるようにしました。
ポストプロセス
プロダクション環境には、Vue.js や コンポーネント用のスクリプトは必要ないので、プリレンダリングされた HTML タグから <script>
タグの記述を削除してからファイルを書き出ししています。
/**
* <script> タグをすべて削除
* (必要な JS がある場合は、もう少し丁寧な正規表現を書いてください)
*/
function postProcessHtml(html) {
return html
.replace(/\s*<script[^<]*<\/script>\s*/igm, '')
.trim()
}
JS はどうするのか?
鋭い人は「HTML と CSS は良いとしても、JS はどうするの?」と思ったかもしれませんが、単一ファイルコンポーネントの <script>
ではタグから受け取るプロパティと、必要であれば算出プロパティを定義するだけで、コンポーネントの振舞いは書きません。潔くあきらめて別途 JS を書きましょう。
以上になります。
FORK Advent Calendar 2018
13日目 Google Apps Scriptでの申請フォームの作成とハングアウトチャットとの連携 @pino_dmdm
15日目 Photoshopで画像をスライスするJS @re_sai