Vue.jsでv-bindが利かなかった。→bindした変数を初期化してなかったからだった

# 現象

v-bindでバインドしたはずの変数を更新しても画面が一向に更新されず、転職を考え始める

※原因・対策を早く見たい方はそれぞれの章へお飛びください

# 再現環境・コード

### 再現環境

https://codesandbox.io/s/23p38jzqwp

- 期待動作：「へい」ボタンをおしたら「wow」が「へい」になる
- 実際動作：「へい」ボタンをおしても「wow」が「へい」にならなくて転職を考え始める

![strange_line.gif](https://qiita-image-store.s3.amazonaws.com/0/284915/7d59c9c9-5081-ab84-6ab4-72ddecb3aba2.gif)


### コード

※以下再現環境と同じもの。codesandboxもいつまであるか知らないのでここにも記す。

```html:index.html
<!DOCTYPE html>
<html>

<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width,initial-scale=1.0">
	<title>CodeSandbox Vue</title>
</head>

<body>
	<div id="app"></div>
	<!-- built files will be auto injected -->
</body>

</html>
```
```javascript:main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from "vue";
import App from "./App";

Vue.config.productionTip = false;

/* eslint-disable no-new */
new Vue({
  el: "#app",
  components: { App },
  template: "<App/>"
});
```

```vue:App.vue
<template>
  <div id="app">
    <img width="25%" src="./assets/logo.png">
    <div>{{str}}</div>
    <button @click="hey">へい</button>
  </div>
</template>

<script lang="ts">
import * as Vue from "vue";
import Component from "vue-class-component";

@Component({})
export default class App extends Vue {
  name: string;
  str:string;
  constructor() {
    super();
    this.name = "App";
    // this.str = ""; // ★
  }
  created() {
    this.str= "wow";
  }
  hey() {
    this.str = "へい";
  }
};
</script>

<style>
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
```

# 原因

`constructor()`内で変数を初期化していないから。

TypeScriptではメンバ変数を宣言しても、まだJavaScript的にオブジェクトのプロパティに追加されているわけではない。
宣言時に初期化するか(`str: string = "wow"`)、コンストラクタで初期化する(`constructor(){this.str = "wow"}`)して初めてオブジェクトのプロパティに追加される。

Vue.jsがDOMと`v-bind`されているオブジェクト変数を探すとき、その時点でプロパティ`str`が存在していなかったため、DOMと変数の束縛ができず、Vue.jsが変更検知できず、私が転職を考え始める結果となった。

# 対策

`constructor()`内で`this.str = ""`する。
[再現環境](https://codesandbox.io/s/23p38jzqwp)のApp.vueの`★`部分をアンコメントすれば動くようになる。

![strange_line.gif](https://qiita-image-store.s3.amazonaws.com/0/284915/8bfd6ecb-5487-4539-e8e9-65990b967d86.gif)

# なぜ気づかなかったか(はまったか)

初期化はしている**つもり**だったのが大きい。

「ライフサイクルフックの`created()`で初期化してるからええやろ！[^1]」と思っていたが、これはVue.jsによる束縛作業などが完了した後に呼ばれるので、結果的にバインドできていなかった。  

そのため、動かない原因の目星を付けるときに「変数初期化」へ考えが至るのが遅くなった。

[^1]: `created()`で初期化しているのは、同じクラス内で定数を宣言した後に、その定数を初期値として当該変数に入れたかった思いがあったため。`constructor()`内では、そこで同時に初期化したプロパティを代入することはできない。(例えば`this.str = this.name`はできない)

# なぜ気づいたか

chromeの開発コンソールでVueタブ([Vue.js devtoolsというアドオンです](https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd))を確認し、「あれれ～？`name`はあるのに`str`はないよ～？？」となったから。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/284915/ae838747-b299-d403-68f1-23cf598931ec.png)

# その他

- TypeScript + Vue.jsは涙流しがち。（でも型なしではやりたくない）
   - Vue.js使ってると*.vue(単一ファイルコンポーネント)使いたくなってきがち
   - TypeScriptと*.vue使ってるとvue-class-component使いたくなってきがち
   - vue-class-component使ってるとVue.js自身の書き方(computedとか)忘れがち(そもそも逆に知らないところありがち)
   - Vue.jsは[TypeScriptに前向きっぽい](https://jp.vuejs.org/v2/guide/typescript.html)けど、情報出てきづらがち
       - さらにvue-class-componentとかあれとかこれとか使ってるもんだからフロントあるあるの「ライブラリ組み合わせ爆発」で検索しんどがち(俺みたいなネット情報頼りなショボい奴には痛い)
- [公式はちゃんと注意してる](https://jp.vuejs.org/v2/guide/instance.html#%E3%83%87%E3%83%BC%E3%82%BF%E3%81%A8%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89)。  
ちゃんと読んで察せ、俺・・・と思いやはり転職を考える始めるがよそに行っても「ここの空気を吸うだけで僕は高く跳べると思ったのかなぁ...」となりそうなので、だましだまし頑張ることにする。
- *.vueファイルの動作を試すなら[codesandbox](https://codesandbox.io)がよい。と、[Vueのフォーラム](https://forum.vuejs.org/)で教えてもらった。