フロントエンド学びなおし2020 #1 - Vue.js の v-for を使ってリスト表示する

「思いついたものをすぐに作れる(そしてピボットできる)(そして最悪捨てられる)」そんな力をつけていきたいものです。以前某プロダクトアワードで、プロトタイプを5時間ほどで作りエントリということをしたのですが、それから2年弱が経ち、やはり日頃磨いていない力は錆びついていくなと危機感を感じています。今年はやっていきたいです💪

実は、タイトルを「Vue.js 学びなおし」にするつもりだったのですが、私の CSS 能力が皆無でむしろここに時間を割いたので、「フロントエンド学びなおし」としています。(しばらく生の CSS の勉強をして、そのうち Bootstrap に移っていこう..)

このシリーズでは、「こういう部品を作りたい!」という定義を行い、それを作る過程で学んだことをメモしていこうと思います。

Vue.js に関する副読本はこちらです。最終的には、書籍の検索サイトが作れるようになるようなので、粘り強くやっていく所存です。

これからはじめるVue.js実践入門

これからはじめるVue.js実践入門

CSS 関連の教材はドットインストールさんの 初めての CSS です。相変わらず分かりやすい、、、オススメです!

作った部品

f:id:ketancho_jp:20200318121154j:plain:w300

画像、アイテム名、数値情報、URL を持つ Item をリスト表示させる、というものです。名前をクリックしたら URL に飛ぶ、としたいです。

f:id:ketancho_jp:20200318124025p:plain

作ったものはこんな感じです。最低限やりたいことはできたはず。

学びメモ

v-for ディレクティブの使い方

これすら忘れていた..

    <div id="app">
      <div v-for="item in items" class="item">
        {{ item.name }}
        {{ item.point }} pt
      </div>
    </div>
(function() {
  'use strict';

  var vm = new Vue({
    el: '#app',
    data: {
      newItem: '',
      items: [{
        name: "ピカチュウ",
        point: 87.3
      }, {
        name: "チコリータ",
        point: 77.1
      }, {
        name: "トゲピー",
        point: 64.9
      }, {
        name: "ヒトカゲ",
        point: 88.8
      }]
    }
  });
})();

v-if と v-show の使い分け

v-if は条件を満たしたときに、要素そのものが破棄される。そのため、頻繁に表示/非表示が切り替わる場合は、描画コストが高まる可能性がある。それに対し、 v-show は要素そのものは文書ツリーに残っているが、 display: none; がついた状態になる。

  • 頻繁に切り替える: v-show
  • 最初に決めた表示/非表示がめったに変わらない v-if

CSS コーディングの進め方

ドットインストール講座で、各要素に background-color を指定して開発を進める方式が採用されていました。スタイルがどう当たっているかが分かりやすく、今回これを真似して開発を進めてみました。

f:id:ketancho_jp:20200318123410p:plain

(参考)ソースコード

html

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>Item List</title>
    <link rel="stylesheet" href="css/styles.css">
  </head>
  <body>
    <header>
      <div class="header">
        <h1>
          ここに Header 情報を書くよ
        </h1>
      </div>
    </header>
    <div id="app" class="itemList">
      <div v-if="items.length" v-for="item in items" class="item">
        <div class="itemImage">
          <img v-bind:src="item.imgPath">
        </div>
        <div class="itemDetail">
          <a v-bind:href="item.url" class="itemName">
            {{ item.name }}
          </a>
          <p class="itemPoint">
            {{ item.point }} pt
          </p>
        </div>
      </div>
      <div v-if="!items.length">表示するものがありません</div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    <script src="js/main.js"></script>
  </body>
</html>

js

(function() {
  'use strict';

  var vm = new Vue({
    el: '#app',
    data: {
      newItem: '',
      items: [{
        name: "ピカチュウ",
        url: "https://zukan.pokemon.co.jp/detail/025",
        imgPath: "img/IMG_7559.jpg",
        point: 87.3
      }, {
        name: "チコリータ",
        url: "https://zukan.pokemon.co.jp/detail/152",
        imgPath: "img/IMG_7562.jpg",
        point: 77.1
      }, {
        name: "トゲピー",
        url: "https://zukan.pokemon.co.jp/detail/175",
        imgPath: "img/IMG_7563.jpg",
        point: 64.9
      }, {
        name: "ヒトカゲ",
        url: "https://zukan.pokemon.co.jp/detail/004",
        imgPath: "img/IMG_7564.jpg",
        point: 88.8
      }]
    }
  });
})();

css

(名前の左上寄せ、数値の右下寄せをもっとカッコよく書けないのでしょうか。。)

body {
  color: #333;
  font-family: "Helvetica Neue",
    Arial, 
    "Hiragino Kaku Gothic ProN", 
    "Hiragino Sans", 
    Meiryo, 
    sans-serif;
  margin: 0;
}

header {
  background-color: #eeeeee;
  padding-top: 16px;
  padding-bottom: 16px;
}

.header {
  width: 500px;
  margin-left: auto;
  margin-right: auto;
  align-items: center;
}

.item {
  width: 500px;
  padding-left: 8px;
  padding-top: 4px;
  padding-bottom: 4px;
  margin-left: auto;
  margin-right: auto;
  align-items: center;
  display: flex;
}

.itemList {
  padding-top: 16px;
  padding-bottom: 16px;
}

.itemImage {
  padding: 4px;
}

.itemImage img {
  width: 80px;
  height: 80px;
  border-radius: 50%;
}

.itemDetail {
  position: relative;
  width: 380px;
  height: 100px;
  margin: auto;
  display: flex;
}

.itemName {
  position: absolute;
  margin-top: 4px;
  margin-left: 4px;
  font-size: 24px;
}

.itemPoint {
  position: absolute;
  bottom: 0;
  right: 0;
  margin-bottom: 4px;
  margin-right: 4px;
  font-size: x-large;
}

まとめ

次は Vue Router の復習をしたい。