Vue.js+Vuetifyでポートフォリオを制作した（少し前の話）

# はじめに
エンジニアもポートフォリオを作る時代！
ということで、私もポートフォリオを制作しました。

出来上がったサイトはこちら。Vue.js＋Vuetifyで制作し、Netlifyでホスティングしています。
https://shozzy.netlify.com/


Netlifyでの公開方法は、別記事にまとめてあります。

- NetlifyでWebサイトを公開する方法
 - https://qiita.com/shozzy/items/dadea4181d6219d2d326

---
実は、ポートフォリオを制作・公開したのは2019年3月。半年ほど前でした（この記事を公開したのは2019年9月）。この記事は下書き状態のまま、だいぶ寝かせてしまいました。
この記事を公開する直前に、個人開発したアプリ「disp barcode」をリリースしました。
（ https://dispbarcode.com/app/ ）

---

# 制作内容のポイント
- シンプルに、カードが並んでいるデザイン。カードは固定サイズで、内容が長い場合は自動で適宜カット。
- カードをクリックしたらダイアログで全文表示。それも長い場合はスクロールが入る。
- xsサイズ時にはヘッダも変化させ、Twitter,GitHub,Qiitaへのリンクをプルダウン方式に。


# 作業開始
とりあえずここを参考に作り始めます。Vue.js界隈は公式サイトが結構充実しているように感じます。
https://vuetifyjs.com/ja/getting-started/quick-start


# 単一ファイルコンポーネント
カード部分はコンポーネントに切り出してあります。親（App.vue）内でv-forをしてうまく並ぶように処理。
カード内のデータはApp.vue内に配列としてベタで持たせてあります。[^2]

[^2]: 本当はAPIとかで外部から取得すると良さそうですが、更新頻度が低く現状の仕様でも事足りているので、「YAGNI」と判断しました。

部分的なコードですが、こんな感じです。
2019年3月当時の内容なので、Vuetifyは1.5系です。

```vue:src/components/Card.vue
<template>
  <div class="card pre-wrap">

    <v-card min-width="200px" height="100%" @click.stop="dialog = true">
      <v-card-title class="font-weight-bold">{{ title }}</v-card-title>
      <v-divider></v-divider>
      <v-card-text>{{ shortenedContent }}</v-card-text>
    </v-card>

    <v-dialog v-model="dialog" scrollable max-width="600px">
      <v-card>
        <v-card-title class="font-weight-bold">{{ title }}</v-card-title>
        <v-divider></v-divider>
        <v-card-text height="300px" class="pre-wrap">{{ content }}</v-card-text>
        <v-divider></v-divider>
        <v-card-actions>
          <v-spacer></v-spacer>
          <v-btn color="primary" flat small @click="dialog = false">CLOSE</v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>
  </div>
</template>

<script>
export default {
  name: 'CardTest',
  props: {
    msg: String,
    title: String,
    content: String
  },
  data(){
    return {
      dialog: false
    }
  },
  computed: {
    shortenedContent: function(){
      let maxlength = 100;
      if(this.content.length <= maxlength){
        return this.content;
      }
      else{
        return this.content.substr(0,maxlength-10)+'...(続きを読む）'
      }
    }
  },
  methods: {
    alert: function(msg){
      alert(msg);
    }
  }
}
</script>

<style scoped>
.cardtest {
  margin: 10px;
  height: 90%;
}

.pre-wrap {
  white-space: pre-wrap;
}

.gradient {
  background: linear-gradient( white 0%, white 90%, rgb(89, 198, 248) 100%);
}

h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>
```

```vue:src/App.vue（抜粋）
<template>
  <v-app>
  <!-- ヘッダ部分等　中略 -->
    <v-content>
      <v-container fluid>
        <v-layout row wrap>
          <v-flex v-for="item in items" v-bind:key="item.id" xs12 sm6 md4>
            <Card v-bind:title="item.title" v-bind:content="item.content"/>
          </v-flex>
        </v-layout>
      </v-container>
    </v-content>
  <!-- 後略 -->
  </v-app>
</template>

<script>
import Card from './components/Card.vue'

export default {
  name: 'App',
  components: {
    Card
  },
  data () {
    return {
      items: [
      { title: 'このサイトについて', content: 'ソフトウェアエンジニア"shozzy"のポートフォリオサイトです。\n技術的には、Vue.js＋Vuetifyで作成し、GitHubで管理して、Netlifyにホスティングして公開しています。' },
      { title: 'カード02', content: 'カード02の内容です' },
      { title: 'カード03', content: 'カード03の内容です' },
      ]
    }
  }
}
</script>
```


# アップする前にビルドの確認
ローカルでは、npm run serve でサーバを動かしてブラウザから動作確認を進めていましたが、これがができていても npm run build をするとエラーになることがあります。（なりました）

今回は、v-for をしている箇所で :key を指定しておらず、「custom elements in iteration require v-bind key」というエラーになっていたので、 :key="item.id" を設定しました。

これでビルドも通るようになりました。

# アップロード
事前設定をしておけば、Githubへpushするだけです。あとはNetlifyが自動でビルドしてくれます。（超簡単）