【コードの隠し味】あなたのコードは大丈夫??一瞬のUI崩れをVue.js templateタグが解決してくれる。

記事タイトルがターゲットニッチすぎて見つけられないことは承知の上、この現象を紹介します。

まずはこちらのサイトの動きを見てください。（1度リロードしています）

[![Image from Gyazo](https://i.gyazo.com/c80a48ac89ec13c9161c16aa6e371db0.gif)](https://gyazo.com/c80a48ac89ec13c9161c16aa6e371db0)

こちらのページでは、SNSボタン付近が読み込まれる時、ローディング後（今回はリロード後）に、画面が少し下がってからSNSボタンが表示されます。
このような少し不自然なガクッとなる状況を、Vue.jsの`template`タグを使って、解決しようと思います。

## ガクッとなる原因のコードは?
まずは、このガクッとなる部分で何が起きているのか見ていきます。
デベロッパーツールで確認してみると、このような構成になっていました。

<img width="1084" alt="template.png" src="https://qiita-image-store.s3.amazonaws.com/0/119773/37ae55e2-066a-9632-a5c9-97fcc9f3c3d2.png">


SNSボタン表示後のスタイルを見ると、周りに画像のような margin がみられます。
これはHTMLのコードで書いてみれば下記のような感じです。

```html
<!-- html -->
<div v-if="SNSボタンがあれば" class="sns-wrap">
  <div class="sns-wrap__btn">シェア</div>
</div>
<NotfoundBlock v-else-if="SNSボタンがなければ(*1)"/>
<LoadingBlock v-else="SNSボタンを読み込み中(*2)" />
```

```scss
.sns-wrap {
  &__btn {
    margin: 10px 20px 10px 0;
  }
}
```
※`NotfoundBlock`(データが取得できない)と`LoadingBlock`(ローディング中のクルクル)は独自のコンポーネントです。


上記のようなコードの場合処理の順番は
1. ``<~ class="sns-wrap">`` のloading中だと`LoadingBlock`が呼ばれる。
2. そのあとloadされた``<~class="sns-wrap">``が出てくる。
このとき`LoadingBlock`と``<~ class="sns-wrap">``の大きさの差（sns-wrapにmarginが付いているため）でガクッと表示されているように見える。

です。

ガクッと潰れてしまう問題を解決したいので、こんな解決策が思いついたので試してみます。
## 安直な解決策（間違い）

marginを指定している`div`の中に書くことで、読み込みがされている最中にもmarginが適用され、ガクッとなる問題は解決するのでは(?)と思い、`NotfoundBlock`と`LoadingBlock`のコンポーネントを、marginをつけている `div`の中に入れてみました。（これは間違いです）

```html
<div v-if="SNSボタンがあれば" class="sns-wrap">
  <div class="sns-wrap__btn">シェア</div>
  <NotfoundBlock v-else-if="SNSボタンがなければ"/>
  <LoadingBlock v-else="SNSボタンを読み込み中" />
</div>
```

`v-if` の直下に `v-else`, `v-else-if` が配置されています。しかし、この3つは本来並列に配置されるものなので、そもそも書き方が間違っているため、このコードでは正しくありません。


## 正しい解決策
`v-if` , `v-else`, `v-else-if` を並列に配置しながら、marginの影響化に置きたいとき、`template`タグを使うことでうまくいきます。

以下のように`template`を使います。

```html
<div class="sns-wrap">
  <template v-if="SNSボタンがあれば">
    <div class="sns-wrap__btn">シェア</div>
  </template>
  <NotfoundBlock v-else-if="SNSボタンがなければ"/>
  <LoadingBlock v-else="SNSボタンを読み込み中" />
</div>
```


このように `template`を使用することで、`sns-wrap`のスタイル（margin）を残したまま、読み込み、読み込み中、読み込みできなかったが実装することができます。
`template`タグを使えば、読み込み後に急にmarginがついてガクッとなるというような違和感のあるコードを修正することができます。

## template タグの役割
`template` タグは、`div` タグのような階層をつくることなく、並列関係を保つことができます。また、classはつけられないという特徴もあります。


## div タグでもいい?
`div` でもいいけれど、`template` のように本来ないタグのように扱われないので、divタグで囲った中の要素は、その上のタグの直下の影響下に置くことはできません。

下記コードにおいて、A, B, Cは並列関係にあります。

(例) `template` タグを使用した例

```html
<!-- html -->
<div class="sns-wrap">
  <template v-if="SNSボタンがあれば">
    <div class="sns-wrap__btn">シェア</div> // A
  </template>
  <NotfoundBlock v-else-if="SNSボタンがなければ"/> // B
  <LoadingBlock v-else="SNSボタンを読み込み中" /> // C
</div>
```

(例) `div` タグを使用した例

```html
<!-- html -->
<div class="sns-wrap">
  <div v-if="SNSボタンがあれば"> // A
    <div class="sns-wrap__btn">シェア</div>
  </div>
  <NotfoundBlock v-else-if="SNSボタンがなければ"/> // B
  <LoadingBlock v-else="SNSボタンを読み込み中" /> // C
</div>
```

`template`タグ、`div`タグそれぞれの効果を考えて、使用する際に使い勝手の良いものを使用するのがいいですね。


記事冒頭にも書きましたが、タイトルがターゲットニッチすぎていいねがもらえないことは承知の上、この現象を紹介しました。しかし、読んでいただいて気づかれたかもしれませんが、この現象は様々なページでよくある現象なのです。

フロントエンドエンジニアたるもの、こういった細かい知識の積み重ねが、UIを崩さない理想の開発者に繋がります。ユーザーの「なにこれ?」という違和感を生まないためにもこういった知識の積み重ねを私自身怠らないようにしたいと思います。


【緩募】
なんという検索でこちらの記事に行き着きましたか?w よければコメントで教えてくださいww