eyecatch

Vue2.xでは仮想DOMが使われるようになりました。それに伴い、Vue用のテンプレート仮想DOMをレンダリングするための関数に変換するという処理をコンパイル時に行ってくれる仕組みが組み込まれています。

そして、お手軽にVueを試したい人、プロダクトにVueを使いたい人、コンポーネント志向でアトミックに実装したい人など、様々な用途に応じたテンプレートの書き方(&コンパイルの方法)を用意してくれています。

そのためテンプレートの書き方とコンパイルの種類については仕様がやや複雑で、公式ドキュメントだけでは理解が難しくけっこうハマりどころだと感じている(事実ずっぽりハマりました)ので、知見を残しておこうと思います。

※この記事を書いている時点でのVueのバージョンは2.2.1です。

おさらい Vue.jsの利用方法3パターン

まずVueの利用方法の確認から。

  1. Vueを<script>タグで埋め込む方法
  2. Vue CLIを使ってVueとソースをバンドルする環境を用意する方法
  3. 手動でVueとソースをバンドルする環境を用意する方法

の3つがありますという単純なお話なので、知ってる場合は読み飛ばしてください。

1. Vueを<script>タグで埋め込む方法

<script src="https://unpkg.com/vue"></script>

これでグローバルにVueが用意されます。よくあるHTMLにscript直読込させる方法です。プロダクト開発には向かないですが、軽くVueを試すのには十分ですね。

2. Vue CLIを使ってVueとソースをバンドルする環境を用意する方法

2018/12/10 追記

この記事はVue CLI 2.x に基づいています。現在既に3.xがリリースされているため、ご参考にされる際はご注意ください。

vue-cliとは、Vueを使ったApplicationをつくる環境をセットアップしてくれるツールです。ビルド用のスクリプトやエントリーポイントになるHTMLなどが一括で生成されます。Reactで言うcreate-react-appのようなものです。

以下のコマンドを叩きます。

$ npm install -g vue-cli               # まずはcliのグローバルインストール
$ vue init webpack <project_name>      # webpackを利用したプロジェクトを生成する
$ cd <project_name>                    # プロジェクトのディレクトリに移動して…
$ npm install                          # 依存関係のあるファイルを落としてきて…
$ npm run dev                          # ビルド

3. 手動でVueとソースをバンドルする環境を用意する方法

Webpackによるバンドル等、諸々の環境セットアップを手動でする場合の方法です。ここでは、Webpack/Browserify/rollupの3種類のバンドラでのセットアップ方法を紹介します。

インストールするものはVue本体と、Vueのソースをバンドラが解釈できるようにするためのツールです。

$ npm install -S vue
$ npm install -D vue-loader         # Webpackでビルドする場合
$ npm install -D vueify             # Browserifyでビルドする場合
$ npm install -D rollup-plugin-vue  # rollupでビルドする場合

以降はWebpackを使い自前でビルド環境を用意する前提で、テンプレートの実装方法とコンパイル方法について説明します。

Vueテンプレートの実装方法

ここから本題。
Vueのテンプレートの実装方法は大きく3種類あります。

1. 単一ファイルコンポーネントの<template></template>を使う

.vueの拡張子をつけると単一ファイルコンポーネントとなります。テンプレートとスタイルとスクリプトをひとまとめのコンポーネントとしてカプセル化する手法です。
参考: 単一ファイルコンポーネント - Vue.js

<template>
    <p>Hello! </p>
</template>

<script>
    // 省略
</script>

2. templateオプションを使う

Vue1.xからあった記法で、コンポーネントオブジェクト内にtemplateをキーに定義します。

Vue.component(hello-world, {
  template: <p>Hello! </p>’  // x-template等でテンプレートを分割する場合も含む
})

3. render関数を使う

renderという名前の関数を実装する方法で、仮想DOMをreturnさせます。Reactライクな書き方…と言っていいんですかね、微妙ですかね。

Vue.component(hello-world, {
  render: function (createElement) {
    return createElement('p', this.name);
  }
})

この3つの書き方には、「render関数に変換する(コンパイルする)タイミング」の違いがあります。

  render関数への変換
(コンパイル)
変換のタイミング
1.単一ファイルコンポーネント 必要 プリコンパイル時
2.templateオプション 必要 JITコンパイル時
3.render関数 不要 -

③はそもそもrender関数を利用しているので変換の必要がないのですが、実際に利用する場面はそこまで多くないかもしれません。Vue2.xが浸透した今では②もあまり使われなくなっているかもしれませんね。

まとめると、テンプレートの実装方法は3種類あり、手法によってコンパイルのタイミングにも差が生じるということです。実装しやすさで選ぶか、コンパイルのタイミングで選ぶか、状況によりけりだと思います。

プリコンパイルとJITコンパイル

コンパイルのタイミングには、バンドル時などに事前にされるプリコンパイルと、実行時されるJustInTimeコンパイルの2種類があります。上述のとおり、テンプレートの実装方法によりどのタイミングでコンパイル(render関数への変換)を行うかが異なります。

単一ファイルコンポーネントをつかっている場合

Webpack等によりプリコンパイルされる時にrender関数への変換がなされます。そのためbundleされたファイルにはrender関数に変換済みのロジックとvue本体(vue.js)が含まれることになります。   実行時にコンパイルする必要がないので高速で、vue本体にも余計なものが含まれないので軽量です。

templateオプションをつかっている場合

プリコンパイルでは変換されず、ブラウザにてJITコンパイルされる時render関数へ変換されます。そのためWebpack等でビルドしていたとしても、ブラウザで実行するときに(内部的に)コンパイルが走り変換が行われます。   そのため、実行速度がやや遅くなります。Vue公式ではこう言っています。

これは十分高速ですが、アプリケーションのパフォーマンスが重要な場合は避けるのが最善です。

また、bundleされたファイルにはvue本体 + コンパイラというコンパイラを内包するvue.jsが含まれることになります。そのためvue.jsの容量がやや大きくなります。

比較

  render関数への変換 読み込むVue
単一ファイルコンポーネント プリコンパイル時 vueロジック本体
(ランタイム限定ビルド されたVue.js)
templateオプション JITコンパイル時 vueロジック本体 + コンパイラ
(完全ビルド されたVue.js)
render関数 変換不要 vueロジック本体
(ランタイム限定ビルド されたVue.js)

完全ビルドとランタイム限定ビルドの読み込み方

templateの書き方によってコンパイルのタイミングが違い、それにより読み込むVue.jsにも差が出ることがわかりました。次はそれぞれをどうやって読み込むかです。

vueはデフォルトでランタイム限定ビルド(されたVue.js)を読み込みます
完全ビルド(されたVue.js)を読み込むように変更するには以下の修正が必要です。

webpackの場合はwebpack.config.jsに追記

module.exports = {
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    }
  }
}

browserifyの場合はpackage.jsonに追記

{
  "browser": {
    "vue": "vue/dist/vue.common.js"
  }
}

rollup.jsの場合はrollup.config.jsに追記

const alias = require('rollup-plugin-alias')
rollup({
  plugins: [
    alias({
      'vue': 'vue/dist/vue.esm.js'
    })
  ]
})

まとめ

テンプレートの実装 render関数への変換 必要なvueモジュール
単一ファイルコンポーネント プリコンパイル時 ランタイム限定
templateオプション JITコンパイル時 完全
render関数 変換不要 ランタイム限定

軽量&高速なランタイム限定をつかいたいけど単一ファイルコンポーネントはいやだ

そんな人はいませんか、私です。
Riot.jsなんかも単一ファイル形式ですけど流行りなんですかね。個人的にはテンプレートは別ファイルに切り出したいのですが…。しかも速度とファイル容量を良くしたいからランタイム限定ビルドを使いたい。そんな場合は分離できるそうです。
参考:単一ファイルコンポーネント 関心の分離について

ちなみに私はテンプレートだけ別ファイルにしました。

軽量&高速なランタイム限定をつかいたいし1系の書き方を踏襲させろ

そんな人いませんか、欲張りさんめ。
templateオプションは使いつつプリコンパイルも使う。できるみたいですがやったことありません。
参考: GitHub - ktsn/vue-template-loader: Vue.js 2.0 template loader for webpack

おしまい

長くなりました。
分かりにくい部分や間違っている部分があれば是非@aloerina_までご連絡ください。