【Vue.js】 DOMを直接操作　$el $ref

# はじめに
データバインディングを利用することでDOMの更新は効率化されるが、直接DOMを参照したい場合もある。例えば、画面上の要素の位置や高さはDOMを直接参照しなければ分からない。
DOMに直接アクセスするにはインスタンスプロパティの```$el```と```$refs```を使用する。
この２つはDOMを参照するため、ライフサイクルの```mounted以降でなければ使用できない```。

[ライフサイクルについてはこちら](http://...)

## $el（ルートを参照）
$elを使用することでインスタンスやコンポーネントのルートを参照することができる。

```sample.html
 <div id="app">
   <p>Hello</p>
 </div>
```

```sample.js
new Vew({
  el: 'app',
  mounted: function() {
  // <div id="app"></div>が出力される
  console.log(this.$el)
  }
})
```

##### ルートとは
HTMLはツリー構造になっているが、その最上位に位置するのがルートである。HTMLドキュメントでは```html```要素がルートにあたる。

## $refs（DOMの要素を参照）
ルート以外のDOMの要素を参照する場合は、```ref```属性と```$refs```を使用する。
```ref```はDOM要素を参照するための特別な属性で```$refs```のキーとなる。

```sample.html
 <div id="app">
   <!-- 対象となる要素にref属性で名前をつける -->
   <p ref="hello">Hello</p>
 </div>
```

```sample.js
new Vue({
  el: 'app',
  mounted: function() {
    // <p>Hello</p>が出力される
    console.log(this.$refs.hello);
  }
})
```

## 動的にDOMを参照
動的にする場合、refはv-bindディレクティブを使って```:ref```と記述する。
```v-for```で要素を繰り返し表示している場合はv-for部分は配列構造になっているのでscript内でアクセスするには```this.$refs[ref属性でつけた名前]```と記述する。

```sample.html
<div v-for="item in items" :key="item.id">
  <!-- 動的にするためrefにv-bindをつける -->
  <p :ref="name">{{ item.name }}</p>
</div>
```

```sample.js
new Vue ({
  el: 'app',
  data: {
    items: [
      {
        id: '001',
        name: 'apple'
      },
      {
        id: '002',
        name: 'orange'
      }
    ]
  },
  mounted: function() {
    // <p>apple</p>が出力される
    console.log(this.$refs[name][0]);
  }
})
```

## $elと$refsは一時的な変更
$elと$refsは一時的な変更なので、データが更新されたり、アプリケーションが操作されて再描画されると上書きされる（変更がリセットされる）。仮想DOMではなくリアルDOMを参照しているからである。

### リアルDOMと仮想DOM
Vueには```リアルDOM```と```仮想DOM```という２つのDOMが存在する。
##### リアルDOM
リアルDOMは実際にブラウザを描画しているものである。
Vueを使用しないでリアルDOMのみで描画している場合、変更を加えた際はHTML内の全てを読み込むので描画に時間がかかる。

<リアルDOMのみの描画の流れ>
1. **HTML**
2. **リアルDOM**
3. **WEBページ**

##### 仮想DOM
仮想DOMは擬似的に作られたDOM。仮想DOMの中には変更前と変更後の２つのDOMが用意されていて、この２つのDOMの差分のみがリアルDOMに反映されるようになっている。
差分のみを読み込み反映するので、描画のスピードが速い。

<仮想DOMを用いた描画の流れ>
1. **HTML**
2. **仮想DOM**　　　→変更前のDOM & 変更後のDOM（この２つの差分のみリアルDOMへ反映）
3. **リアルDOM**
4. **WEBページ**

→[こちらでわかりやすく説明されている](https://qiita.com/terry_6518/items/4ecb90fa474895b81b8a)

上記の説明を踏まえて動作を確認してみる

```sample.html
<div id="app">
  <!-- ボタンをクリックするとhandleClickメソッドが実行される -->
  <button @click="handleClick">カウントアップ</button>
  <!-- ボタンをクリックするとshowの値が反転する -->
  <button @click="show = !show">v-show 表示/非表示</button>
  <button @click="show = !show">v-if 表示/非表示</button>
  <!-- 値の真偽値によって要素を表示/非表示（非表示の場合display:none;の状態） -->
  <p ref="count" v-show="show">0<p>
  <!-- 値の真偽値によって要素を表示/非表示（非表示の場合DOMから要素が削除されている状態） -->
  <p ref="count" v-if="show">0<p>
</div>
```

```sample.js
new Vue({
  el: 'app',
  data: {
    show: true
  },
  methods: {
    handleClick: function() {
      // ref属性で紐づけたcountを変数countに代入
      var count = this.$refs.count;
      // count(pタグ)の中のテキストを整数（10進数）にして1プラスする
      if(count) {
        count.innerText = parseInt(count.innerText, 10) + 1
      }
    }
  }
})
```

上記のコードではカウントアップボタンをクリックするとpタグの中の数値が1ずつアップしていく仕組みになっている。
```v-if```で表示を切り替えている部分はDOMから要素自体が削除されている（仮想DOMを切り替えている）ので、非表示にして表示し直すとpタグの数値は```0```にリセットされる。
それに対し```v-show```で切り替えている部分は```display:none;```で見えなくなっているだけで要素自体は存在している（リアルDOMには何の影響も与えていない）ので、非表示にして表示し直すとカウントアップした数値はそのまま維持される。
これらのことから```$refs```と```$el```の変更は一時的だということがわかる。