Edited at

私がハマったVue.jsのショボいミス


誰も書かないような凡ミスをあえて書く

Vue.jsめっちゃ好きなんですけど、最初の頃はググってもよくわからない変なミスでハマることがあったので、あえてそれを書いていこうと思います。

ちなみにこの記事は私がハマったVue.jsのショボいミス - GitPitch というLTで発表した内容を、整理した内容です。


なんかよくわからんけど動かない(2019/04/22追記)

ホットリロードがあるから大丈夫!と全面的に信用しないでください。

リロードしたら直ることがあります。

細かい理由は後述。


ちょっといじったら突然画面いっぱいにエラーが表示された

デカいわ黒背景の白文字だわでビビりますが、落ち着いてメッセージを呼んでください。

このオーバーレイはコンパイルエラーなので、どこかに間違いがあるはずです。

そして、だいたいの場合はどこがエラーの原因なのかもちゃんと書いてあります。


タグを足したらエラーになった

タグを追加する場所が悪いとそうなります。

<template>

<div class="hello">
</div>
<div>

</div>
</template>

Failed to compile.

./node_modules/vue-loader/lib/template-compiler?{"id":"data-v-469af010","hasScoped":true,"transformToRequire":{"video":["src","poster"],"source":"src","img":"src","image":"xlink:href"},"buble":{"transforms":{}}}!./node_modules/vue-loader/lib/selector.js?type=template&index=0!./src/components/HelloWorld.vue
(Emitted value instead of an instance of Error)
Error compiling template:

<div class="hello">
</div>
<div>

</div>

- Component template should contain exactly one root element. If you are using v-if on multiple elements, use v-else-if to chain them instead.

@ ./src/components/HelloWorld.vue 11:0-366
@ ./src/router/index.js
@ ./src/main.js
@ multi (webpack)-dev-server/client?http://localhost:8081 webpack/hot/dev-server ./src/main.js

注目すべきは Component template should contain exactly one root element. If you are using v-if on multiple elements, use v-else-if to chain them instead. このメッセージですね。

ルートエレメントは1個でなければなりません。


「Main」コンポーネントがエラーになる

Mainコンポーネントは作れません。

なぜならHTML5に<main>タグがあるからです!

[Vue warn]: Do not use built-in or reserved HTML elements as component id: main

はい、メッセージにちゃんと予約されたエレメントですって出ますね。

(私がハマった時はこのメッセージではなかったような?)

同様の理由で、head, header, footer などのコンポーネントも作れないので気をつけてください。

<script>

export default {
name: "main" //←アウト *Main.vueでnameを省略もアウト
}
</script>

そもそも論、Vue.jsのスタイルガイドに コンポーネントの名前は2単語の組み合わせにしなければなりません というものがあるので、そちらを守ればこんなことにはならないのですけど。


絶対にあるはずのprops受け取り/子コンポーネントが呼び出しがうまくいかない

「prop」「component」って書いてたとか、やらかしました。これらは複数形にすべきで、props, components が正しいですね。

よくよく考えたら配列やオブジェクトとして受け取りますし。単数ではなく複数形です。

地味になんのメッセージも出ないので、一度ハマるとなかなか気づけない罠ポイントかなと思っています。


コンポーネントのマウントがうまくいかない(2019/04/22追記)

import したコンポーネントのオブジェクトをそのまま渡してください。

うっかり文字列で渡したりするとうまくマウントできません


fuga.vue

<script>

import Hoge from '~/Hoge.vue'

export default{
// components: [ 'Hoge' ] ←色々間違い
components: { Hoge } // ← importしたオブジェクトをマウント
}
</script>



絶対にあるはずのメソッドがない

前述の「method」にしてたパターンに加えて、ネストが間違ってて methods の下にいなかったことがあります。


絶対にあるはずメソッドetcがないその2(2019/04/22追記)

JavaScriptの監視機構の都合で新しいキーを追加した場合やファイルを追加した場合、そのことを検知できないことがあります。

なので、Vueクラスに初めてmethodsを追加した場合などは

おそらくmethodsが生えていることを感知できていない可能性があります。

ということでリロードしてみましょう


全部アロー関数にしたらハマった

ES6系のシンタックス紹介記事で「どんどんアロー関数に置き換えよう」「普通のfunction() 定義とかもういらないし」みたいなノリの文章とかあるじゃないですか。

だもんで私もやってみたんですよ。

次のコードは、isShowの状態を切り替えて、v-ifを表示したり消したりしたいコンポーネントです。


hoge.vue

<template>

<div class="container">
<button v-on:click="toggelShow">ボタン</button>
<p v-if="isShow">見えてる</p>
</div>
</template>

<script>
export default {
//↓多分危険
data: () => {
return {
isShow : true
}
},
methods : {
//↓アウト
toggelShow: () => {
this.isShow = !this.isShow;
}
},
}
</script>


実はこのコンポーネント、うまく動きません。

というのも、 toggleShow をアロー関数にしたためthis = 親コンポーネントとか、windowとか指します!thisをこのコンポーネントに束縛していないのです!!

data に関しても、thisを束縛していないので、propsで受け取った値をthis経由で参照しようとすると失敗するんじゃないかと思います。

解消法は二通り。(下記のコード


hoge.vue

<script>

export default {
//↓ES5までの記法
data: function() => {
return {
isShow : true
}
},
methods : {
//↓オブジェクトのメソッド記法
toggelShow() {
this.isShow = !this.isShow;
}
},
}
</script>

従来どおりの「プロパティは関数ですよ」と定義するか、ES6シンタックスの「オブジェクトのメソッドですよ」と定義してあげればOKです。

アロー関数の使い所は各種コールバックに使う、ですね。

axiosやlodashのメソッド群のコールバックに使うとめっちゃ気持ちいいです。


ESLintがとんでもなくうざかった/ESLintを上手に活用したい

最近initしたプロジェクトではそんなこともなさそうですが、

昔はルール違反なコードを書くとオーバーレイが全面を覆ってとんでもなかったです。


config/index.js

    // Use Eslint Loader?

// If true, your code will be linted during bundling and
// linting errors and warnings will be shown in the console.
useEslint: true, // ← falseにしましょう

// If true, eslint errors and warnings will also be shown in the error overlay
// in the browser.
showEslintErrorsInOverlay: false, // ← デフォルトtrueだったのかなぁ


ESLintにコードを修正してもらうには、

package.json の scriptsの lint をチェックして、 --fix を加えたscriptを作りましょう。

gitのpre commit hookとかに仕込むとさらによさげです。


package.json



scripts: {
//前略
"lint": "eslint --ext .js,.vue src ",
"fix" : "eslint --fix --ext .js,.vue src"
//後略
}


Vueの{{}}を別のシンボルに置き換えたい(2019/04/22追記)

テンプレートでの変数展開公文 {{}} mastacheですが、これは変更可能です。

delimiters API — Vue.js

もし、テンプレートエンジンの変数展開シンタックスと被った場合は、

変更を考慮してもよいかもしれません。

(言うまでもありませんが、テンプレートエンジンの変数展開シンタックスとどちらのシンボルを置き換えるかはプロジェクトによって最適な選択をしてください)


ストアを追加したが読み込まない(2019/04/22追記)

前述の監視機構の都合の話の追加です。

storeを途中から追加した場合、そのファイルが読み込まれていないことがあります。

こちらはwebpack上のファイル監視対象から漏れている可能性があります。

この場合、npm run dev(npm run hot)などをやり直すことで初めて読み込まれるようになるので、気をつけてください。


mapXXX から値が参照できない(2019/04/22追記)

コンポーネントでmapXXXX()を使って参照する時の引数はString Arrayです。

うっかり参照したいものが1個だからといってStringを渡したり、あるいは複数を参照したいからとString引数2個を渡したりしないようにしてください。呼び出せません。


dispatch, mutationの時にpayloadの引き渡しがうまくいかない(2019/04/22追記)

受け取りの型をオブジェクトにしてたら渡す時の型もオブジェクトにして引数を揃えてください。

型が合ってないと値が渡せません


(余談)サーバーサイドプロジェクトから見たVue.jsとの付き合い方(2019/04/22追記)


  • SPAを考えるならVue(CLI)/NuxtのプロジェクトとAPIサーバーのプロジェクトの2つに分けるほうがだいたい楽

  • サーバーサイドのルーティングとSPA(フロントのルーティング)が両方あると世界が崩壊するのでやってはいけない

  • SPAでOGPやSEOを考えるならSSRができるNuxtのUniversalモードを選ぶべき

  • 中規模開発くらいになると色々規約に乗っかれて開発できるNuxtが楽だと思う

  • 小規模開発かつOGPやSEOを気にしなくていいならVueCLIで構築したプロジェクトのほうが考えること少なくて楽かも

  • 中規模か小規模かの分水嶺は個人的にはVuex使かどうかだと思っていて、ページをまたいで状態を管理したいならNuxtでいいんじゃない?って思っている(諸説あります)

↑↑↑ここまでSPA開発の話↑↑↑

↓↓↓ここからテンプレートエンジンでHTMLをレンダリングする世界の話↓↓↓


  • サーバーサイド言語のテンプレートエンジンに部分的にVueを導入することもできる(むしろチュートリアルではこの方法で紹介している)

  • ごく小規模なら単一ファイルコンポーネント(.vueファイル)を使わない開発の方が小回りが効いていいかも

  • もしくはLaravelMixで単一ファイルコンポーネントとテンプレートエンジンの世界を両立する

  • 別途CLIで作ったプロジェクトを作って、buildした結果をサーバーサイドプロジェクトに展開して利用するという方法はありかもなぁ

  • vue-routerを使うときはよく考えること


まとめ

以上が私の記憶の限りのハマりどころになります。

ううーん、始めた頃の記憶をもうあんまり覚えてない。

ということで、「こんなところにハマったよ!」などなどあればコメントいただければ嬉しいなと思います。


こんな記事も書いてます