vue-templatify という Browserify プラグインを作った
2016/2/1 追記: 単一ファイルコンポーネントでも、JSとテンプレートを分けて書くこと自体はできるので(refs: https://jp.vuejs.org/v2/guide/single-file-components.html#関心の分離について)、このプラグインは基本的に不要かもしれません。
作った。
Vue.js を使っていて、vueify や vue-loader で .vue
を書くのは大げさだけど、コンポーネントを作って、JSとテンプレートを分けて管理したい、ということがあります。コードだと以下のようなイメージです。
<!-- app.html --> <div class="app"> <p>{{ text }}</p> <button type="button" @click="log">Log</button> </div>
// app.js import template from './app.html' export default { template data () { return { text: 'Example text' } }, methods: { log () { console.log('output log') } } }
Vue.js 1.x までは、Browserify を使っている場合 partialify や stringify 等のプラグインでこれが実現できていましたが、Vue.js 2.x ではこのままでは動きません。
動かない理由、2.xのビルドプロセスと2種類のビルド
以下の2つが動かない理由です。
- 2.x では、レンダリングの過程で、テンプレートを VirtualDOM のインスタンスを返す render関数 にコンパイルする必要がある
- 2.x では、コンパイルロジックが含まれているスタンドアローンビルドと含まれていないランタイム限定ビルドの2種類のビルドがあり、npmパッケージでmainに指定されているのはランタイム限定ビルドである
つまり、単純にvar Vue = require('vue')
やimport Vue from 'vue'
で Vue.js をインポートして、template オプションにテンプレート文字列を渡した場合は、テンプレートのコンパイルが実行されないまま、ブラウザで動作させることになり、エラーになってしまいます。
vueify や vue-loader を利用している場合は、バンドルの過程で、テンプレートを render関数 にコンパイルしているため、問題ありません。
( Vue.js 2.x のレンダリングプロセスについては、kazuponさんのスライドが分かりやすいのでおすすめです。Vue.js 2.0 Server Side Rendering // Speaker Deck )
この問題を解決するには、2つ方法があります。
前者は公式のドキュメントで方法が紹介されています( https://jp.vuejs.org/v2/guide/installation.html#スタンドアロン-vs-ランタイム限定ビルド )。後者は、あくまでランタイム限定ビルドを使う道です。微々たるものかもしれませんが、コンパイルロジックが無い分ファイルサイズが小さいのでロード時間と、render関数 へのコンパイル時間が削減できます。
(前置きが長いね…)
vue-templatify
Webpackではテンプレートを render関数 にコンパイルするプラグインvue-template-loaderを @ktsn さんが作られていましたが、Browserify にはなかったので今回開発しました。ホットモジュールリプレースメントにも対応しているので、テンプレートを編集したら、ページのリロード無しで自動的に内容が更新されます。
GitHub - kitak/vue-templatify: Browserify transform for Vue.js 2.0 template
プラグインを利用した場合、JSのコードは以下のように変更する必要があります。テンプレートは、「コンポーネントのオプションを受け取って、render関数 をマージした新しいオプションを返す関数」に変換されます。
// app.js import withRender from './app.html' export default withRender({ data () { return { text: 'Example text' } }, methods: { log () { console.log('output log') } } })