Chapter 03

[準備編] Vue コンポーネント の作成

本章でやること

それでは、 Storybook でカタログ化する Vue コンポーネントを作成していきます。

ここでは、 npx sb init した際に自動で生成されるコンポーネントを作成に、以下の3種類を作成していきます。

  • Button
  • Header
  • Page

なお、本筋とは逸れますが、ここではコンポーネントのスタイリングを簡便にするため、sass 及び sass-loader を追加でインストールしています。

$ yarn add -D sass sass-loader

Button

Button コンポーネントは、以下の props を受け取ります。

props名 役割
label String ボタンに表示するテキスト
primary Boolean 青を基調とした配色にする
secondary Boolean 灰を基調とした配色にする
size String ボタンサイズを "large" "medium" "small" から指定する
backgroundColor String 背景色を上書きする

また、ボタンをクリックすることで onClick イベントが emit されます。

上記ご認識いただければ、下記コードは参考程度で読み流しても大丈夫です。

src/components/Button.vue
<template>
  <button
    type="button"
    :class="['button', { primary, secondary }, size]"
    @click="$emit('onClick')"
    :style="style"
    v-text="label"
  />
</template>

<script>
export default {
  props: {
    label: {
      type: String,
      required: true
    },
    primary: {
      type: Boolean,
      default: false
    },
    secondary: {
      type: Boolean,
      default: false
    },
    size: {
      type: String,
      default: 'medium',
      validator: function(value) {
        return ['small', 'medium', 'large'].indexOf(value) !== -1
      }
    },
    backgroundColor: {
      type: String,
      required: false
    }
  },
  computed: {
    style() {
      return {
        backgroundColor: this.backgroundColor
      }
    }
  }
}
</script>

<style lang="scss" scoped>
.button {
  font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-weight: 700;
  border: 0;
  border-radius: 3em;
  cursor: pointer;
  display: inline-block;
  line-height: 1;

  &.primary {
    color: white;
    background-color: #1ea7fd;
  }
  &.secondary {
    color: #333;
    background-color: transparent;
    box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset;
  }

  &.large {
    font-size: 16px;
    padding: 12px 24px;
  }
  &.medium {
    font-size: 14px;
    padding: 11px 20px;
  }
  &.small {
    font-size: 12px;
    padding: 10px 16px;
  }
}
</style>

Header

続けて Header コンポーネントを作成します。 props は以下のとおりです。

props名 役割
user Object ログイン中のユーザがオブジェクト

Header には、ユーザーがログイン中であれば ログアウトボタン を、そうでなければ ログインボタン 及び 会員登録ボタン を、前章の Button コンポーネントを用いて描画し、それぞれクリックされた際に、 onLogout onLogin onSignUp イベントがemitされます。

src/components/Header.vue
<template>
  <header>
    <h1>Vue × Storybook 6 Sample App</h1>
    <div class="buttons">
      <template v-if="user.id">
        <MyButton size="small" label="ログアウト" @onClick="$emit('onLogout', user)" />
      </template>
      <template v-else>
        <MyButton size="small" label="ログイン" @onClick="$emit('onLogin')" />
        <MyButton size="small" label="会員登録" @onClick="$emit('onSignUp')" />
      </template>
    </div>
  </header>
</template>

<script>
import MyButton from './Button'
export default {
  components: { MyButton },

  props: {
    user: {
      type: Object,
      required: true
    }
  }
}
</script>

<style lang="scss" scoped>
header {
  font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
  padding: 15px 20px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 45px;
  h1 {
    font-weight: 900;
    font-size: 20px;
    line-height: 1;
    margin: 6px 0 6px 10px;
    display: inline-block;
    vertical-align: top;
  }
  button + button {
    margin-left: 10px;
  }
  @media (max-width: (480px)) {
    button {
      display: none !important;
    }
  }
}
</style>

Page

Page コンポーネントは、 Header コンポーネントを描画し、そこに渡すユーザの管理を行います。

props は受け取りませんが、 Vuex を使ってグローバルな状態を管理したり、ユーザ認証を行ったりと、ビジネスロジックに踏み込んだ何かを行っているイメージです。

src/components/Page.vue
<template>
  <div>
    <MyHeader :user="user" @onLogin="login" @onLogout="logout" @onSignUp="signUp" />
    <main>
      <div class="content-wrapper">
        <div class="content" :key="i" v-for="i in 10">
          {{ 'コンテンツがここに表示されます'.repeat(20) }}
        </div>
      </div>
    </main>
  </div>
</template>

<script>
import MyHeader from './Header'
export default {
  components: { MyHeader },
  data() {
    return {
      user: {}
    }
  },
  methods: {
    login() {
      this.user = { id: 1, name: 'sasaki' }
    },
    logout() {
      this.user = {}
    },
    signUp() {
      // 会員登録ページが開かれるイメージ
      window.open('https://github.com/Sa2Knight/Curriculum-Vitae')
    }
  }
}
</script>

<style lang="scss">
main {
  .content-wrapper {
    padding: 25px;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    column-gap: 25px;
    row-gap: 1em;
  }
  @media screen and (max-width: 480px) {
    .content-wrapper {
      grid-template-columns: 1fr;
    }
  }
}
</style>

動作確認

vue-cli でボイラーテンプレートを作成しているので、 src/App.vue を書き換えることですぐに動作確認することができます。

src/App.vue
<template>
  <div id="app">
    <MyPage />
  </div>
</template>

<script>
import MyPage from './components/Page.vue'

export default {
  components: { MyPage }
}
</script>

開発環境を立ち上げて localhost:8080 にアクセスすると

$ yarn serve

それらしい画面が出来上がりました。

ログイン ボタンを押下すると、 ログイン の代わりに ログアウト 会員登録 ボタンが表示されることも確認して、下準備は全て完了です。