4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-12-13

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

どういうこと?

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

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

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

サンプル

サンプルコード一式は、こちらになります。
:octocat: 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>

プリレンダーするとこうなります :smile:

プリレンダーを実行することで、以下の 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 を書きましょう。:grin:

以上になります。


:christmas_tree: FORK Advent Calendar 2018
:arrow_left: 13日目 Google Apps Scriptでの申請フォームの作成とハングアウトチャットとの連携 @pino_dmdm
:arrow_right: 15日目 Photoshopで画像をスライスするJS @re_sai

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?