Edited at

初心者向:Vue.js コンポーネント作成がうまくいかないときにチェックする5つのこと

More than 1 year has passed since last update.

こちらVue.js #4 Advent Calendar 20174日目の記事です~。

どもども。ソニックムーブっていう会社で、マークアップしたりJavascriptをちょいちょい触ったりしているココエっていいます。

最近Vue.js絡みの仕事ばっかりやっていて、初心者の方にVue.jsの使い方を教える機会がでてきたので、こんな記事を書いてみようと思いました。

やっぱり最初の難関がコンポーネント作成ですよね。

チュートリアル見ながら、コンポーネント作ってみたけど、なんかうまくいかなーーーい!という方は、後述する、コンポーネント作成がうまくいかないときにチェックする5つのことをチェックしてみてください。

お役に立てればこれ幸い。


コンポーネントのテンプレートのルート要素は1つのみ

おおよそ初心者の方がハマるのがここ。

2系からなのですが、コンポーネントのテンプレートのルート要素は1つしか持つことができません

公式には、

https://jp.vuejs.org/v2/guide/migration.html#テンプレート

こちらに、さらっと書いてあったりします。

どういうことか例をあげます。まず、以下がうまくいかない例。

<div id="app">

<my-component></my-component>
</div>

<script type="text/x-template" id="tpl_my_component">
<div>ほげ</div>
<div>ふが</div>
</script>

<script>
(function() {

var myComponent = {
template: '#tpl_my_component',
};

var vm = new Vue({
el: '#app',

components: {
'my-component': myComponent
},
});

})();
</script>

https://jsfiddle.net/kokoe/xenqynw2/

こちらは、実行すると「ほげ」しか表示されません。(「ふが」が表示されない。)

コンポーネントのテンプレート(#tpl_my_component)の直下(ルート)には、要素(div)が2つ存在しているため、正しく表示されないようですね。

「あれっ、作ったコンポーネントが全部表示されない!」

ってあたふたしている場合、こんなオチだったりしますします。


コンポーネントのテンプレートのルート要素でv-forしない

前述と同じことですが、テンプレート直下(ルート)でv-forを使うと、これまた、正しく表示されません。v-forで引き回す配列値が1つのみだったとしても表示されません。

<div id="app">

<todo v-bind:items="todoList"></todo>
</div>

<script type="text/x-template" id="tpl_todo">
<div v-for="item in items" v-bind:key="item">
{{ item.title }}
</div>
</script>

<script>
(function() {

var myComponent = {
template: '#tpl_todo',
props: ['items']
};

var vm = new Vue({
el: '#app',

components: {
'todo': myComponent
},

data: {
todoList: [{
title: '歯磨き',
isDone: false
}, {
title: '手洗い',
isDone: false
}, {
title: 'にらめっこ',
isDone: false
}, ]
}
});

})();
</script>

https://jsfiddle.net/kokoe/Lj45jrdo/

テンプレートは、インラインテンプレートで指定していようが、script[type=text/x-template]で指定していようが同じで、ルートは要素は1つのみなのです。


templateって正しく書いてますか

自身の体験談でお恥ずかしい限りですが

var myComponent = {

template: '#tpl_my_component'
};

この template っていう単語。

templete とか tamplate とかになっていませんか?!w

いわゆるスペルミスです。急いでいる時とか気づかないもんなのです。。。

スペルミスしている時、Developer Toolsで該当の要素を覗いてみると

<!--function (t,n,r,i){return mt(e,t,n,r,i,!0)}-->

こんなんでていたりするので、その場合はスペルミスを疑ってみたらよいと思います。

こちらにスペルミスの例をあげてます。

https://jsfiddle.net/kokoe/vh0m14La/


v-bindでコンポーネントに渡す値が抜けてませんか?

以下もうまくいかない例です。

my-componentのhogeにv-bindで値を渡し忘れている例です。

これを実行しても、コンポーネントの部分だけでなく、new Vueでマウントしているルート自体が表示されません。


<div id="app">
<my-component v-bind:hoge=""></my-component>
</div>

<script type="text/x-template" id="tpl_my_component">
<div>ほげ</div>
</script>

<script>
(function() {

var myComponent = {
template: '#tpl_my_component',
props: ['hoge']
};

var vm = new Vue({
el: '#app',

components: {
'my-component': myComponent
},
});

})();
</script>

https://jsfiddle.net/kokoe/j5xqvu7y/

Developer Toolsで要素を覗いてみると、new Vueでマウントしている要素が

<!---->

こんな感じでコメントアウトが入っています。

v-bindで値を渡し忘れていないかチェックしてみてくださいね。

(そんなことあるのかって突っ込まれそうですが、後で値いれようと思ってて忘れちゃったりするのです。。)


new Vueでマウントしている要素(ルート)の中にテンプレートファイルをいれない

コンポーネントのテンプレートを、script[type=text/x-template]で指定している場合、割とやりがちなミスだったりします。

以下、うまくいかない例です。

これを実行すると、ReferenceError: text is not defined と怒られます。

<div id="app">

<my-component></my-component>

<script type="text/x-template" id="tpl_my_component">
<div>{{ text }}</div>
</script>
</div>

<script>
(function() {

var myComponent = {
template: '#tpl_my_component',
props: ['text']
};

var vm = new Vue({
el: '#app',

components: {
'my-component': myComponent
},
});

})();
</script>

https://jsfiddle.net/kokoe/rsrcy2w5/

new Vue でマウントしている要素の中にMustache(二重中括弧)が入ると、script要素の中であろうがなかろうが、対応するオブジェクトプロパティの値に置き換えられます。(上記の例だと text プロパティ値を探しに行く。で、無いから怒られる)

ということで、コンポーネントのテンプレートをscript[type=text/x-template]で作成する場合は、new Vue でマウントしている要素の中に入らないように気を付けましょう。