概要
本スライドはVuetify Meetup #1での発表資料です。面白そうと思った方はぜひ次回以降参加してください!LTも募集中です。
自己紹介
業務内容
Twitter @Meijin_garden
名人って呼ばれてます。
株式会社NoSchoolのCTO。
中高生のための勉強質問サイトを作ってます。
自分しかフルタイムのエンジニアがいないのでAWSもFirebaseもLaravelもNuxt.jsもデザインも採用も機械学習もやってます。
経歴
奈良高専情報工学科
↓
株式会社LIFULL
↓
株式会社NoSchool
Vuetify on Nuxt.jsを採用した理由
背景
2017年からNoSchoolのサイト自体はあるが、社長がWordPress on SakuraServerで起業したのでエンジニアから見たら酷いものだった。
シードの資金調達を終えて僕がジョインした2019年3月から5月にかけて、フルリプレイスを行うこととなった。
リプレイス後の技術構成
インフラ: AWS(CloudFront, ALB, EC2, S3...)
サーバーサイド: Laravel
フロントエンド: Nuxt.js, jQuery
※2ヶ月じゃフロントまで移行やりきることができず、現在もページ単位でNuxtに移行中
Nuxt.jsの採用理由
- 僕自身がVue.jsの経験が1年強あった(前職の新規事業で1人でフルスクラッチした)
- jQueryと比較して開発速度が段違い
- ReactとVueだとあまり違いも採用力が衰えるということもなさそうなのでCTOの僕が経験があって開発速度がとにかく速いものを選べばいいやという考え
Vuetifyの採用理由
- デザイナー不在でそこそこの品質で開発をするためにUIフレームワークは必須
- Bulma、Bootstrap Vueなども見たが、Class属性ではなくコンポーネント自体を用意してくれている方が使い勝手がいいと思った
- ちなみにElementは前職で使っていたけどレスポンシブや細かいところでイライラした
実際にVuetifyで頑張ったページたち(2019/8時点)
家庭教師一覧

家庭教師詳細

質問作成
Vuetifyでまずこれだけ覚えておけば的なコンポーネントリスト
- ページ(
v-app
,v-content
等) - レイアウト(
v-layout
,v-flex
) - 余白(
.py-2
,.mx-4
等) - 装飾系(
v-card
,v-chip
,v-icon
) - 画像系(
v-img
,v-avatar
) - フォーム(
v-btn
,v-select
,v-text-field
等) - あとはドキュメントを読み込む。カスタムSlotも便利。
Vuetifyの便利コンポーネントをいくつか紹介
ページ構成(default.vue)
<template lang="pug">
v-app
my-header
v-content(app)
my-breadcrumbs(:items="breadcrumbs")
v-container(app fluid)
nuxt
my-footer
</template>
※ちょっと改変はしてます
v-app
やv-content
などは公式ドキュメントの通り。
パンくず(Breadcrumbs.vue)
パンくずはv-breadcrumbsがベースで、カスタマイズのためにv-slot:item
を利用。
<template lang="pug">
v-breadcrumbs(:items="items").px-2.py-0.grey.lighten-3
template(v-slot:item="props")
v-breadcrumbs-item(
:to="props.item.to"
:disabled="props.item.disabled"
v-if="props.item.to !== '/'"
)
span.grey--text {{ props.item.text }}
span(v-else)
a(href="/").pa-0
v-icon.body-1.primary--text mdi-{{ props.item.icon }}
template(v-slot:divider)
span >
</template>
ページネーション
<template lang="pug">
v-pagination(
v-model="pageModel"
@input="onPaginationClicked"
@next="onPaginationClicked"
@previous="onPaginationClicked"
:length="last_page"
:total-visible="7"
style="overflow-x: scroll;"
).my-4
</template>
scriptを抜粋するとこんな感じ。
<script>
export default {
...
methods: {
onPaginationClicked(event) {
this.$emit("query-updated", {
page: this.pageModel
});
}
}
...
};
</script>
要は、ページネーションのページ番号が変わったときに呼び出し元コンポーネントで再度API叩くなりする必要があるので、親にemitする設計にしている。
ページネーションの注意
total-visibleが偶数だと、中央あたりのページ番号で表示が変になるので注意。
例えば6にしたとき、全ページ数が10などのときに5ページ目付近で表示がおかしくなり、隣り合ったページが表示されない。
これは...も含めたvisibleのcountだから、偶数だと...の数が合わなくて隣り合ったページが出ない。
(気になるなら直せやOSSだろという話ですよねすみません)
Vuetifyをベースにカスタマイズしているコンポーネント
見出し系
Vuetifyの見出しはフォントサイズが大胆なのと、サイト全体で統一感を出したいので共通コンポーネント化。
import Vue from 'vue'
import Body from "@/components/layouts/Body";
import PageTitle from "@/components/parts/heading/pageTitle";
import SubTitle from "@/components/parts/heading/subTitle";
import SubTitle2 from "@/components/parts/heading/subTitle2";
Vue.component('sec-main', Body)
Vue.component('page-title', PageTitle)
Vue.component('subtitle', SubTitle)
Vue.component('subtitle2', SubTitle2)
悩ましいのは、マージンまでコンポーネントに入れるかどうか。入れるとしたらどれくらい大きいマージンを入れるか(デザインの話になってくるけど)。
ボタン
サイトを作っていく中で、outlined
なv-btn
をよく使うので共通化。
<template lang="pug">
v-btn(outlined large :color="color" :to="to" @click="clicked" :block="block").py-2.mt-2
v-icon(v-if="icon" :class="iconColor + '--text'").title.mr-2 mdi-{{ icon }}
.subtitle-1
slot
</template>
以下略
必須ラベル
最近作ったatoms的なのでかなり気に入ってる。
<template lang="pug">
v-chip(small label color="accent").px-2.font-weight-bold 必須
</template>
駆け足でしたがLTなのでこれくらいにします
最後に
NoSchoolではフロントエンドが得意で、Laravel等のサーバーサイドもゴリゴリやっていきたいWebエンジニアを募集中です!(iOSエンジニアも!)
回答最適化のために機械学習も取り入れていたり、勉強意欲の強い人が自由に暴れられると思いますので是非お声がけください〜。