Vue.js のサイト上での使用は NG だけど、Vue.js での開発はあきらめなかった話


半分ネタですが、切実な必要があり試行錯誤した結果です。

どういうこと?

比較的ページ数のあるサイトの作成において、

  1. 汎用パーツのコーディング品質担保のため、コンポーネントベースの開発が理想的だった
  2. Vue.js のコンポーネントを利用した開発を検討
  3. さまざまな理由で、サイトでの Vue.js での利用が NG になった
  4. 開発は Vue のコンポーネントを利用し、プリレンダリング出力した静的 HTML と CSS をプロダクション環境に使うことにした

通常プリレンダリングは SEO 対策のために実施するものですが、今回はあくまでもコンポーネントベースでの開発、および静的 HTML と CSS の生成を目的としています。

サンプル

サンプルコード一式は、こちらになります。
https://github.com/megurock/vue-as-template-engine

こういう感じでコーディングします

特に意味はありませんが、 HelloWorld コンポーネントを v-for と Pug の while 構文 でそれぞれ書いてみた例です。この比較では v-for の方がシンプルで使い勝手が良いですが、プリレンダリングされてしまえば、どちらにも差異はありません。

index.pug
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"> として出力します。

HelloWorld.vue
<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
prerendered/index.html
<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>
components.css
.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> タグの記述を削除してからファイルを書き出ししています。

prerender.js
/**
 * <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