はじめに
Vue.js でテンプレートを使うのに「単一ファイルコンポーネント」という便利なものがあります。しかし公式ドキュメントを読んでも導入がよくわからなかったので、備忘録としてまとめました。
誤字・脱字・勘違いなどありましたらご指摘ください。
前提条件
- Webpack がインストールしてある
- Babel がインストールしてあり、ES2015設定がしてある
①一番基本的な Vue.js の使い方
Vue.js を CDN から持ってきます。Vue はグローバルなオブジェクトになっているので前置きも不要で呼び出せます。
<script src="https://unpkg.com/vue"></script>
<script>
new Vue = ({
el: '#hoge',
data: {
list: [
{ name: '山田' },
{ name: '田中' },
{ name: '鈴木' }
]
}
});
</script>
<div id="hoge">
<ul>
<li v-for="item in list">{{item.name}}</li>
</ul>
</div>
<div id="hoge">
<ul>
<li>山田</li>
<li>田中</li>
<li>鈴木</li>
</ul>
</div>
②HTMLにインラインでテンプレートを記述して子コンポーネントを使う方法
子コンポーネントは、親のデータを加工したい時などに使える
親が持っているデータをそのまま表示するなら前述の簡単な方法でかまいません。ちょっと加工したい時は子コンポーネントを使うと切り分けがしやすくなります。
1ページでしか使わないならインラインテンプレートがおすすめ
後述する「単一ファイルコンポーネント」は便利ですがそのページでしか使わないもののためにファイルを切り出すのも少々面倒です。小規模かつ静的なページならHTML内にテンプレートを記述するインラインテンプレートがおすすめです。
インラインテンプレートの記述には <script type="text/x-template" id="固有ID">
が使えます。
<div id="hoge">
<!-- v-bind でコンポーネントにデータを渡す -->
<!-- member-data はタグ内ではケバブケースだが、定義部ではキャメルケース -->
<member-tag v-for="item in list" v-bind:member-data="item"></member-tag>
</div>
<script type="text/x-template" id="compo-member">
<div>
<div class="name">{{memberData.name}}</div>
<!-- 年齢は HTML が含まれる予定なので v-html を使う -->
<div class="age" v-html="age"></div>
</div>
</script>
// データ定義
var data = {
list: [
{ name:'山田', age:'30歳' },
{ name:'田中', age:'29歳' },
{ name:'鈴木', age:'25歳' }
]
};
// コンポーネント定義
var compo_member = {
// id="compo-member" のタグの中身を取得
template: '#compo-member',
// 必要なデータを持たせる準備。型の定義もする。
// ここではキャメルケースを用いる
props: {
memberData: Object
},
computed: {
// 数字だけ <span></span> で囲む
age: function(){
var age = this.memberData.age.replace(/(\d+)/g, "<span>$1</span>");
return age;
}
}
}
// Vue.js インスタンス
new Vue = ({
el: '#hoge',
data: data,
components: {
// コンポーネントの登録。左辺がタグ名になる
'menber-tag': compo_member
}
});
<div id="hoge">
<div>
<div class="name">山田</div>
<div class="age"><span>30</span>歳</div>
</div>
...
</div>
正直、このレベルの加工なら Vue.js インスタンスの定義で methods: を使う方法もあります。しかしコンポーネントとして切り出すことで問題の切り分けや、別のコンポーネントを追加したい時に複雑にならずにすみます。
③単一コンポーネントファイルを使って完全に分離する方法
JS もテンプレートの定義も、さらには css まで分離できるのが単一コンポーネントファイル(拡張子.vue)です。JS のモジュールのような感覚で記述できるのでメインのスクリプトをシンプルなまま保つことができます。
プリコンパイルのために Webpack が必要になる
ここからは Vue.js を CDN版から npm版になります。分割された .vue
を結合するためには Webpack も必要になります(Browserify でもかまいません)。
この記事では Webpack が導入されている前提なので、追加モジュールのみ記載します。
環境をまとめて構築してくれる VueCLI
は便利ですがここでは使いません。既存プロジェクトの組み込みには変更箇所のみを把握したほうが後々良いと個人的に考えてます。
必要な npm のインストール
$ npm install --save-dev vue
$ npm install --save-dev vue-loader
$ npm install --save-dev vue-template-compiler
webpack.config.js の変更
module:{
loaders: [
// .vue ファイルを組み込むためのモジュール
{
test: /\.vue$/,
use: 'vue-loader'
}
]
},
resolve: {
extensions: ['.js', '.vue'],
modules: [
"node_modules"
],
alias: {
// vue.js のビルドを指定する
vue: 'vue/dist/vue.common.js'
}
},
// 〜略〜
インストール先の node_modules/vue/dist/
を見るといくつかの vue.*.js
があります。フルセット版、ランタイム版などが入っていますが、よくわからない場合は vue.common.js
を選んでおけば問題ありません。
個人的ハマりポイント①
node_modules の中を見たら vue-template-es2015-compiler
が入っていたのですがModule build failed: Error: Cannot find module 'vue-template-compiler'
というエラーが出ました。別途 vue-template-compiler
をインストールすることで解消されました。
個人的ハマりポイント②
[Vue warn]: You are using the runtime-only build of Vue where the template option is not available. Either pre-compile the templates into render functions, or use the compiler-included build.
というエラーが表示されたのですが、Webpack で alias
を指定することで解決しました。
ページの一部分を .vue にする場合
方法②のような使い方で、コンポーネント部分のみを別ファイルの .vue
にする方法です。
<template>
<section>
<div class="name">{{memberData.name}}</div>
<div class="age" v-html="age"></div>
</section>
</template>
<script>
module.exports = {
props: {
memberData: Object
},
computed: {
age: function(){
var age = this.memberData.age.replace(/(\d+)/g, "<span>$1</span>");
return age;
}
}
};
</script>
<div id="hoge">
<member-tag v-for="item in list" v-bind:member-data="item"></member-tag>
</div>
import Vue from 'vue';
import MemberTag from '../vue/member-tag.vue';
// データ定義は方法②と同じなので省略
// Vue.js インスタンス
new Vue = ({
el: '#hoge',
data: data,
components: {
'member-tag': MemberTag
}
});
結果も方法②と同じなので省略します。
ベースになる HTML からテンプレート構文が消えてだいぶスッキリしました。JS もコンポーネント定義が消え、 import
で読み込むだけになっています。
ページ全体を .vue にする方法
ページ全体というと語弊がありますね。<div id="hoge"></div>
の部分をまるっと差し換える方法です。
<template>
<main id="app">APP</main>
</template>
<script>
module.exports = { };
</script>
<div id="app">
<member-tag v-for="item in list" v-bind:member-data="item"></member-tag>
</div>
import Vue from 'vue';
import App from '../vue/app.vue';
// Vue.js インスタンス
new Vue = ({
el: '#app',
template: '<App/>',
components: { App: App }
});
↓
<main id="app">APP</main>
元の HTML では <div id="app">〜
となっていたのに中身も含めて完全に差し替わっています。
筆者自身は中規模以上の開発経験がないのでそのへんはよくわかりませんが、作業分担をするような場面ではこちらのほうが利点があるのでしょう。