Nuxt.js+Vuetifyの機能を理解しながらヘッダーとスライドメニューをカスタマイズした記録
..
フロントエンド学習の一環として、Nuxt.js+VuetifyでWebサイトを制作しています。
現時点ではヘッダー等の共通パーツまで形になっており、その部分をどう作ったか記録していきます。
やったこと
- ヘッダーコンポーネント(
v-app-bar
)を導入- スタイルを調整
- ボタンを配置
-
v-toolbar-title
- 任意のフォントを適用
- トップページへのリンクを追加
- スライドメニューコンポーネント(
v-navigation-drawer
)を導入- スタイルを調整
- メニュー用テキストのフォントを変更
- SNSアカウントへのリンク付きアイコンを配置
編集対象のファイル
/layouts/default.vue
nuxt.config.js
default.vue
は全て空っぽにしてから始めています。
<template>
</template>
<script>
export default {}
</script>
ヘッダー部分
Vuetifyのv-app-bar
コンポーネントを使用。
コンポーネントを呼び出し
<template>
<v-app>
<v-app-bar
app
>
</v-app-bar>
<v-main>
<nuxt />
</v-main>
</v-app>
</template>
app
属性のみ付与しています。app
属性のついた要素はアプリケーションのレイアウトの一部であるとみなされ、自動的にposition: fixed;
を指定して配置を固定すると同時に、v-app-bar
の高さと同じpadding-top
をv-main
(コンテンツ)要素に自動的に付与します。
###v-toolbar-title
でサイト名を表示
<template>
<v-app>
<v-app-bar
app
>
<v-toolbar-title>
<h1>サイトのタイトル</h1>
</v-toolbar-title>
</v-app-bar>
<v-main>
<nuxt />
</v-main>
</v-app>
</template>
中央に寄せる+トップへのリンクを付与する
<template>
<v-app>
<v-app-bar
app
>
<v-toolbar-title
class="mx-auto"
>
<h1>
<nuxt-link to="/">サイトのタイトル</nuxt-link>
</h1>
</v-toolbar-title>
</v-app-bar>
<v-main>
<nuxt />
</v-main>
</v-app>
</template>
Vuetifyで用意されているmx-auto
クラスをつけることで、自動的に横方向のmargin
をauto
にするCSSを当ててくれます。またa
タグ同様、nuxt-link
タグにリンクを記載して囲うことで、中のテキストにリンクをつけることができます。
a
タグのスタイルについてはCSSを書く必要があるので、例えばassets
内にscss
ディレクトリを作り、その中にcommon.scss
を作成して、サイト全体の共通スタイルとしてCSSを記述します。
a {
color: white !important;
text-decoration: none;
&:hover {
opacity: 0.7;
}
}
このCSSを読み込むために、nuxt.config.js
を編集。
css: [
{ src: '~/assets/scss/common.scss' },
],
キャッシュごと更新すると、指定したスタイルが反映されたことが確認できました。
/inspire
ページでタイトルロゴを押すと、ちゃんとトップに移動するようにもなっています。
フォントを変更する
Vuetifyで読み込んでいるRoboto
フォント
日本語フォントやGoogle Fontsを使うためには、設定ファイルで読み込ませる必要があります。
- デフォルトのフォントをシステムフォントに変更
特に指定しなかった場合のフォントを変える場合は、variables.scss
にて指定します。
Vuetifyが用意しているスタイルの中にはSASS変数化されているものがあり、その変数を上書きすることで任意のスタイルを当てることができます。フォントを変更する場合は$body-font-family
を変更します。
+ $body-font-family: -apple-system, system-ui, "Hiragino Kaku Gothic ProN", "Hiragino Sans", Meiryo, sans-serif, "Segoe UI Emoji";
(これ以外にどんな変数があるかはこちらのページから)
設定ファイルのvuetify
部分に追記。
vuetify: {
+ customVariables: ['~/assets/variables.scss'],
+ treeShake: true,
・
・
・
}
SASS変数を上書きした結果をローカル環境で閲覧するためにtreeShake
を有効化しています。
デベロッパーツールで確認すると、ちゃんとフォントが読み込まれています。
今回は追加でシステムフォントを読み込んだだけですが、始めからデフォルトのRobotoを読み込ませない設定にすることもできます。
- Google Fontsを使用する
Webフォントを読み込むためのライブラリを追加。
yarn add nuxt-webfontloader
設定ファイルのmodules
以下を編集。
// Modules: https://go.nuxtjs.dev/config-modules
modules: [
'nuxt-webfontloader'
],
// GoogleFont読み込み
webfontloader: {
google: {
families: ['Raleway:400']
}
},
あとはCSSで指定するだけで使用可能です。
【備忘録】ビルドについて
treeShake
を有効化すると、ビルドにかかる時間がけっこう増えます。
設定次第で短縮させられるようです。
// Build Configuration: https://go.nuxtjs.dev/config-build
build: {
parallel: true, // マルチスレッドでビルド
cache: true, // ビルド時のloader情報をキャッシュ化
hardSource: true, // キャッシュ管理して二回目以降を高速化
}
ここでキャッシュを残すようにしたことで、ビルド時にエラーが出るようになることがあります。
その対策としてpackage.json
にキャッシュをクリアするスクリプトを追加し、エラーがあればyarn clear-hard-source-cache
を都度実行します。
"scripts": {
"dev": "HOST=0.0.0.0 PORT=4000 nuxt",
"build": "nuxt build",
"start": "nuxt start",
"generate": "nuxt generate",
"test": "jest",
+ "clear-hard-source-cache": "rm -rf node_modules/.cache/hard-source/"
},
ヘッダーにスライドメニューの開閉ボタンを追加
<template>
<v-app>
<v-app-bar
app
>
<v-toolbar-title
class="mx-auto"
>
<h1><nuxt-link to="/">サイトのタイトル</nuxt-link></h1>
</v-toolbar-title>
+ <v-app-bar-nav-icon
@click.stop="drawer = !drawer"
>
+ </v-app-bar-nav-icon>
</v-app-bar>
<v-main>
<nuxt />
</v-main>
</v-app>
</template>
あとは、このボタンによって呼び出されるスライドメニューを実装します。
スライドメニュー部分
v-navigation-drawer
コンポーネントを使用。
コンポーネントを呼び出し
<template>
<v-app>
+ <v-navigation-drawer app>
+ </v-navigation-drawer>
<v-app-bar
app
>
<v-toolbar-title
class="mx-auto"
>
<h1><nuxt-link to="/">サイトのタイトル</nuxt-link></h1>
</v-toolbar-title>
<v-app-bar-nav-icon
@click.stop="drawer = !drawer"
>
</v-app-bar-nav-icon>
</v-app-bar>
<v-main>
<nuxt />
</v-main>
</v-app>
</template>
###クリックイベントとスライドメニューを紐付け
<template>
・
・
<v-navigation-drawer
app
v-model="drawer"
>
</v-navigation-drawer>
・
・
</template>
<script>
export default {
data() {
return {
drawer: false,
}
}
}
</script>
メニューの開閉は、data
プロパティでdrawer
を保持し、クリックイベントでこの値を書き換えることで行います。
デフォルトではこのように、メニューの横幅の分main
の幅を縮小して表示する形式になっています。
いちいちコンテンツの並びに影響するのはやや鬱陶しいので、temporary
で覆い被さる形の表示になるよう指定しました。
temporary
が指定されると、展開されたスライドメニューは要素の一番上に配置されます。同時に半透明の色でオーバーレイをかけ、画面のどこかを押すと閉じるようにしてくれます。
###メニューバーの中身を作っていく
- ページを追加してリンクを配置する
「このサイトについて」みたいなページを作って、トップへのリンクと併せてメニューバーからアクセスできるようにします。
<script>
export default {
data() {
return {
drawer: false,
+ items: [
+ {
+ title: 'home',
+ link: '/'
+ },
+ {
+ title: 'about',
+ link: '/about'
+ },
+ ],
}
}
}
</script>
data
内に文字列とパスの配列を作り、
<template>
<v-container>
<h1>About Page</h1>
<p>このサイトについて</p>
</v-container>
</template>
<script>
export default {
}
</script>
pages
内にコンポーネントを作成し、アバウトページを作成します。
そしてページリンクの配列から、v-for
ディレクティブとv-list
コンポーネントでメニューの中身を出力します。
<v-navigation-drawer
app
v-model="drawer"
temporary
>
+ <v-list>
+ <v-list-item
+ v-for="item in items"
+ :key="item.title"
+ :to="item.link"
+ >
+ <v-list-item-content>
+ <v-list-item-title>
+ {{ item.title }}
+ </v-list-item-title>
+ </v-list-item-content>
+ </v-list-item>
+ </v-list>
</v-navigation-drawer>
スタイルの調整
- フォントの変更
v-list-item
にidを付与し、先ほど読み込んだGoogle FontsのRalewayを適用します。
<v-list-item-title id="menu-text">
{{ item.title }}
</v-list-item-title>
・
・
・
+ <style scoped>
+ #menu-text {
+ font-family: 'Raleway', sans-serif;
+ }
+ </style>
-
v-list-item
にmargin-bottom
を追加する
<v-list>
<v-list-item
v-for="item in items"
:key="item.title"
:to="item.link"
+ class="mb-4"
>
<v-list-item-content>
<v-list-item-title id="menu-text">
{{ item.title }}
</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
Vuetifyで用意されているヘルパークラスを付与することで、1=4px
換算なのでmargin-bottom: 16px;
となります。
メニュー下部に横並びのSNSアイコンを追加する
<script>
data() {
return {
・
・
・
+ icons: [
+ {
+ path: require("@/assets/img/twitter.svg"),
+ link: "https://twitter.com/hogehoge"
+ },
+ {
+ path: require("@/assets/img/wantedly.svg"),
+ link: "https://www.wantedly.com/id/hogehoge"
+ },
+ {
+ path: require("@/assets/img/github.png"),
+ link: "https://github.com/hogehoge"
+ }
+ ],
}
}
</script>
今回作成しているのがブログサイトなので、スライドメニュー内に各SNSアカウントへのリンク付きアイコンを配置したいと考えました。アイコン画像は公式のブランドリソースをダウンロードし、リンクと画像のパスを抱き合わせたオブジェクトの配列をdata
から返します。
・
・
・
<template v-slot:append>
<div id="sns-icons">
<div
v-for="icon in icons"
:key="icon.path"
>
<v-btn
depressed
icon
>
<a class="sns-icon" v-bind:href="icon.link" target="_blank" rel="noopener noreferrer">
<img v-bind:src="icon.path">
</a>
</v-btn>
</div>
</div>
</template>
</v-navigation-drawer>
<template v-slot:append></template>
を使うと、スライドメニューの底部にコンポーネントを差し込むことができます。
そこにv-btn
コンポーネントをv-for
ディレクティブで表示します。
<style scoped>
・
・
・
+ img {
+ width: 24px;
+ height: 24px;
+ }
+ .sns-icon {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ }
+ #sns-icons {
+ display: flex;
+ gap: 0 3%;
+ margin: 40px 52px;
+ }
</style>
そうして配置したアイコンを、CSSでサイズを調整したりフレックスボックスで均等に並べます。
綺麗にアイコンが並びました。
ソース全体
<template>
<v-app>
<v-navigation-drawer
app
v-model="drawer"
temporary
>
<v-list>
<v-list-item
v-for="item in items"
:key="item.title"
:to="item.link"
class="mb-4"
>
<v-list-item-content>
<v-list-item-title id="menu-text">
{{ item.title }}
</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
<template v-slot:append>
<div id="sns-icons">
<div
v-for="icon in icons"
:key="icon.path"
>
<v-btn
depressed
icon
>
<a class="sns-icon" v-bind:href="icon.link" target="_blank" rel="noopener noreferrer">
<img v-bind:src="icon.path">
</a>
</v-btn>
</div>
</div>
</template>
</v-navigation-drawer>
<v-app-bar
app
>
<v-toolbar-title
class="mx-auto"
>
<h1><nuxt-link to="/">サイトのタイトル</nuxt-link></h1>
</v-toolbar-title>
<v-app-bar-nav-icon
@click.stop="drawer = !drawer"
>
</v-app-bar-nav-icon>
</v-app-bar>
<v-main>
<nuxt />
</v-main>
</v-app>
</template>
<script>
export default {
data() {
return {
drawer: false,
items: [
{
title: 'home',
link: '/'
},
{
title: 'about',
link: '/about'
},
],
icons: [
{
path: require("@/assets/img/twitter.svg"),
link: "https://twitter.com/nawa3ng"
},
{
path: require("@/assets/img/wantedly.svg"),
link: "https://www.wantedly.com/id/ryoga_nawa"
},
{
path: require("@/assets/img/github.png"),
link: "https://github.com/nawawa"
}
],
}
}
}
</script>
<style scoped>
#menu-text {
font-family: 'Raleway', sans-serif;
}
img {
width: 24px;
height: 24px;
}
.sns-icon {
display: flex;
justify-content: center;
align-items: center;
}
#sns-icons {
display: flex;
gap: 0 15%;
margin: 40px 52px;
}
</style>
これ以外にスタイルやパーツなど、いくらでもコードが増えることはあると思います。
その際はヘッダー・メニュー・SNSアイコンなどの単位で小さいコンポーネントに分けて、default.vue
で読み込む形にするとスッキリすると思います。
まとめ
- 現時点までの、Vuetifyを使ってみた感想
UIフレームワークということで、ちょっとした記述でUIをマテリアルデザイン調にできるのは楽でした。今後の個人開発等では大活躍する予感です。
ただその分自由度は高くなく、作り込んだデザインカンプを用意してマークアップするような場面には全く合わないのだと思いました。事前デザインはワイヤーフレーム程度でおさえて、あとはUIコンポーネントフル活用でササっと形にしてしまうような使い方をするものなんだな、ということがわかります。
逆にみっちり画面デザインをして、世界観を作り込んだ上での開発もやってみたいところではあるので、今度はCSSフレームワークというやつを試してみたいとも思いました。流行ってるらしいのでTailwindCSSなど使ってみたいです。
勉強は退屈なのでさっさと手を動かしてしまいましたが、徐々に機能を理解してリファクタリングしていくつもりです。