Vue3のComposition API(script setup)でダッシュボードレイアウト作成
Vueでアプリケーションを構築するとユーザまたは管理者用のダッシュボードが必要になる場合があります。有料・無料を問わずネット上にはVueを利用して作成されたダッシュボードが公開されていますが本文書ではViteでVue3のプロジェクトを作成し、Composition API(script setup)を利用してスクラッチからダッシュボードのレイアウトを作成してきます。Tailwind CSSを利用してレスポンシブデザインに対応しているだけではなくダークモードへの切り替え機能の実装も行っています。ダッシュボードのレイアウトの作成のチュートリアルなのでページ上で表示する内容については現時点では触れていません。今後内容は増やしていく予定です。
ダッシュボードのレイアウトを作成を通して以下の知識を習得することができます。
- Viteを使ったVueプロジェクトの作成方法
- VueのComposition APIのscript setupでのコードの記述方法
- TailwindCSSによりレスポンシブデザインとダークモード切り替え
- ドロップダウンメニュー、アコーディオンメニューの実装方法
- 表示・非表示の変化に対する各種アニメーション設定
- script setupでのライフサイクルフック(onMounted, onUnmounted)の利用方法
- Vue Routerの設定
最終的に作成するダッシュボードレイアウトは下記の通りです。レスポンシブデザインとダークモードに対応しています。
目次
Viteによるプロジェクトの作成
Viteを利用してVueのプロジェクトを作成するる場合はnpm init viteコマンドを利用します。本文書ではプロジェクト名にvue-dashboardという任意の名前をつけています。各自好きな名前をつけてください。実行するとフレームワークの選択を行うことができるのでvueを選択してください。TypeScriptを選択することができますが本文書ではTypeScriptを利用していません。
% npm init vite@latest vue-dashboard
Need to install the following packages:
create-vite@latest
Ok to proceed? (y) y
? Select a framework: › - Use arrow-keys. Return to submit.
vanilla
❯ vue
react
preact
lit
svelte
プロジェクトの作成が完了すると作成したプロジェクトフォルダに移動してnpm install, npm run devコマンドを実行します。
% cd vue-dashboard
% npm install
% npm run dev
> vue-dashboard@0.0.0 dev
> vite
Pre-bundling dependencies:
vue
(this will be run only when your dependencies or config have changed)
vite v2.6.13 dev server running at:
> Local: http://localhost:3000/
> Network: use `--host` to expose
ready in 461ms.
localhost:3000にブラウザからアクセスすると以下の画面が表示されます。ViteによるVueプロジェクトの作成は完了です。
ブラウザ上に表示されている内容はsrcフォルダのApp.vueファイルに記述されています。ファイルの中身を確認すると大半の人が見慣れているSFC(Single File Components)とは異なりscript, template, styleの順番で記述されています。またscriptタグの中にはsetupと追加されています。
<script setup>
// This starter template is using Vue 3 <script setup> SFCs
// Check out https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup
import HelloWorld from './components/HelloWorld.vue'
</script>
<template>
<img alt="Vue logo" src="./assets/logo.png" />
<HelloWorld msg="Hello Vue 3 + Vite" />
</template>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
script setupの下に記述されているコメントのhttps://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setupにアクセスするとscript setupを利用した場合のコードの記述方法が記載されています。script setupはSignle File Components(SFC)の中でComposition APIを使ってコードを記述する際に推奨される方法でComposition APIをより簡潔にコードを記述することができます。この時点でscript setupの記述方法がわからなくても本文書を通して記述方法を理解することができるようになると思うので安心してください。
Visual Studio Codeの拡張機能 Volar
Visual Studio Codeを利用している場合は拡張機能のVolar(Vue Language Features)をインストールしておきます。
Tailwind CSSのインストール
CSSの設定にはTailwind CSSを利用します。Tailwind CSSのドキュメントを参考にVite環境でのTailwind CSSのインストールを行います。
% npm install -D tailwindcss@latest postcss@latest autoprefixer@latest
パッケージのインストール後にTailwind CSSの設定ファイルを作成するために以下のコマンドを実行します。実行後にtailwind.config.jsとpostcss.config.jsファイルが作成されます。
% npx tailwindcss init -p
Created Tailwind CSS config file: tailwind.config.js
Created PostCSS config file: postcss.config.js
tailwind.config.jsファイルを開いてpurgeオプションの設定を行います。ダークモードの設定はtailwind.config.jsファイルで行うことができます。設定は後ほどdarkModeオプションを利用して行います。
module.exports = {
purge: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
}
srcフォルダにindex.cssを作成してtailwindのディレクティブを追加します。
@tailwind base;
@tailwind components;
@tailwind utilities;
作成したindex.cssファイルはsrcフォルダのmain.jsファイルでimportします。
import { createApp } from 'vue';
import App from './App.vue';
import './index.css';
createApp(App).mount('#app');
設定後App.vueファイルでTailwind CSSのutility classが適用できるか確認します。
<script setup></script>
<template>
<div class="font-semibold">ダッシュボード</div>
</template>
太文字のダッシュボードがブラウザ上に表示されたらTailwind CSSの設定は問題なく完了しています。
ダッシュボードのレイアウトの中でアイコンを利用するため、アイコン用にheroiconsのライブラリのインストールを行っておきます。
% npm install @heroicons/vue
レイアウトの設定
ダッシュボード用のレイアウトを作成するためにsrcフォルダにlayoutsフォルダを作成し、Default.vueファイルを作成します。
App.vueファイルでは作成したDefault.vueファイルをimportします。ダッシュボードの文字列をSlotを利用してDefaultコンポーネントに渡しています。
<script setup>
import Default from './layouts/Default.vue';
</script>
<template>
<Default>ダッシュボード</Default>
</template>
下記の図のように主にPCのような広い画面ではサイドバーが表示されメニューアイコンをクリックするとサイドバーが表示・非表示になるような設定を行なっていきます。
メニューアイコンをクリックするとサイドバーが非表示になります。再度メニューアイコンをクリックするとサイドバーが表示されます。
サイドバーの表示・非表示の設定を行っていくためサイドバーとメインの領域を作成します。この時サイドバーはpositionをfixedに設定して固定しメインの領域はpadding-leftにサイドバーの広さと同じ値を設定しています。
<script setup>
</script>
<template>
<div class="relative">
<div class="fixed top-0 w-64 h-screen bg-white z-20">サイドバー</div>
<div class="bg-gray-100 h-screen overflow-hidden pl-64"></div>
</div>
</template>
右側の薄いグレーの領域をメイン領域と呼んでいます。
サイドバーを開閉するためのメイン領域にメニューバーを追加し、その中にメニューアイコンを配置します。アイコンはインストールを行ったheroiconsのライブラリからimportします。メイン領域にはslotも追加します。
<script setup>
import { MenuIcon } from '@heroicons/vue/outline';
</script>
<template>
<div class="relative">
<div class="fixed top-0 w-64 h-screen bg-white z-20">サイドバー</div>
<div class="bg-gray-100 h-screen overflow-hidden pl-64">
<div class="bg-white rounded shadow m-4 p-4">
<MenuIcon class="h-6 w-6 text-gray-600 cursor-pointer" />
</div>
<div>
<slot />
</div>
</div>
</div>
</template>
メニューバーをダッシュボードの文字列の上に表示されている背景が白の領域です。中にはメニューアイコンがはいっています。
メニューアイコンが表示されたのでメニューアイコンをクリックするとサイドバーが開閉できるように設定を行います。開閉を制御するshow変数を追加します。show変数はrefを利用して定義します。
<script setup>
import { ref } from 'vue';
import { MenuIcon } from '@heroicons/vue/outline';
const show = ref(true);
</script>
script setupでは、show変数はそのままtemplateタグの中で利用することができます。MenuIconにclickイベントを追加します。アイコンをクリックするとshowの値がtrueの場合はfalse, falseの場合はtrueに変わります。
<MenuIcon
class="h-6 w-6 text-gray-600 cursor-pointer"
@click="show = !show"
/>
メイン領域ではpadding-leftの設定でサイドバーの領域分を確保していましたがクリックを押すとpadding-leftの適用を解除します。
<template>
<div class="relative">
<div class="fixed top-0 w-64 h-screen bg-white z-20">サイドバー</div>
<div
class="bg-gray-100 h-screen overflow-hidden"
:class="{ 'pl-64': show }"
>
<div class="bg-white rounded shadow m-4 p-4">
<MenuIcon
class="h-6 w-6 text-gray-600 cursor-pointer"
@click="show = !show"
/>
</div>
<div>
<slot />
</div>
</div>
</div>
</template>
クリックするとpl-64が非適用になるためメイン領域がサイドバーの下に入り込みます。
サイドバーも非表示になるようにshowの値とtransform translateXを利用してサイドバーの広さ分左側に移動させます。
<div
class="fixed top-0 w-64 h-screen bg-white z-20 transform"
:class="{ '-translate-x-full': !show }"
>
サイドバー
</div>
クリックするとサイドバーが左側に消えてメイン領域が画面いっぱいに広がります。
PCのように画面が横長の場合はここまでの設定で問題がありませんがモバイルのように横幅が狭い場合は以下のようになりサイドバーによってメイン領域の中にコンテンツを収めるのが難しくなります。
ブラウザの幅が狭い場合はメイン領域をサイドバーに下に入り込むように設定を行います。Tailwind CSSのbreakpointsを利用してxl(1280px)より小さい場合はメイン領域をサイドバーの下に入り込むように設定します。
実現するためにpl-64の前にxl:を追加します。1280px以上の場合のみshowの値によってpl-64が適用され、1280pxより小さい場合はpl-64がshowの値がどちらであれ適用されることはありません。
<div
class="bg-gray-100 h-screen overflow-hidden"
:class="{ 'xl:pl-64': show }"
>
設定通りブラウザの幅が狭い場合にはサイドバーの下にメインの領域が入り込みます。
しかしこの状態ではメニューアイコンがサイドバーの下に隠れてしまったためメニューアイコンがクリックできずサイドバーを非表示にすることができません。メニューアイコンの代わりとなるdiv要素をサイドバー要素の下に新たに追加します。このdiv要素はpositionをfiexedで画面全体に広がるようにinset-0(top:0px,righ:0px,left:0px,bottom:0px)を設定し背景書にはopacity-50(50%)を設定しサイドバーのz-indexよりも小さい10を設定しています。showがtrueの場合のみ表示されxl(1280px)以上では非表示にしています。
<div
class="fixed top-0 w-64 h-screen bg-white z-20 transform"
:class="{ '-translate-x-full': !show }"
>
サイドバー
</div>
<div
class="fixed xl:hidden inset-0 bg-gray-900 opacity-50 z-10"
@click="show = !show"
v-show="show"
></div>
サイドバーが表示されている時には半透明の幕が表示されます。サイドバーとメイン領域の間に半透明の幕が表示されていることになります。半透明の幕が表示されている間メインの領域にアクセスすることはできません。
半透明の幕をクリックするとサイドバーが消え、半透明の幕も非表示になりブラウザ上にはメイン領域のみ表示されます。
アニメーションの設定
メニューボタンまたは半透明の幕をクリックするとサイドバーの開閉がすぐに切り替わるのでアニメーションの追加を行います。サイドバーのdivとメインのdivにduration-300を設定します。
<div
class="fixed top-0 w-64 h-screen bg-white z-20 transform duration-300"
:class="{ '-translate-x-full': !show }"
>
サイドバー
</div>
<div
class="bg-gray-100 h-screen overflow-hidden duration-300"
:class="{ 'xl:pl-64': show }"
>
設定後サイドメニューを表示する場合はは左からゆっくりと表示され、非表示にする場合は右側にゆっくりと消えていきます。
アクセス時のサイドバーの表示/非表示設定
デフォルトではshowはtrueに設定されているのでブラウザの幅が広い場合も狭い場合もサイドバーが表示された状態で表示されます。特に幅の狭いモバイルの場合は最初からサイドバーが開いている必要はありません。
アクセス時のブラウザの横幅に合わせてサイドバーを表示するかしないかを設定します。
innerWidth変数を追加し、windowオプジェクトから横幅の情報を取得します。innerWidthの値によってshowの値をfalseにするかtrueにするか決めています。1280という値はbreakpointのxlの1280pxに合わせています。
const innerWidth = ref(window.innerWidth);
const show = ref(innerWidth.value >= 1280 ? true : false);
設定後はブラウザの幅が1280px以上の場合はサイドバーが表示された状態で表示され、1280pxより小さい場合はサイドバーが非表示の状態で表示させるようになります。
リサイズ時のサイドバーの表示/非表示設定
ブラウザの横幅をリサイズした際に1280px以上であれば自動でサイドバーが表示され1280px以上のサイズからリサイズして1280pxより小さい横幅になった場合にサイドバーを非表示にするといったことも可能です。
script setupタグの中にcheckWindowSize関数を追加します。
const checkWindowSize = () => {
if (window.innerWidth >= 1280) {
if (show.value === false && innerWidth.value < 1280) show.value = true;
} else {
if (show.value === true) show.value = false;
}
innerWidth.value = window.innerWidth;
};
ブラウザの横幅の変更はresizeイベントを利用して検知します。resizeイベントをイベントリスナーに追加します。イベントリスナーはVueのライフサイクルフックのonMountedで追加し、onUnmountedで削除しています。checkWindowSizeイベントの発生を減らすためにlodashのdebounceを利用しています。
import { ref, onMounted, onUnmounted } from 'vue';
import { MenuIcon } from '@heroicons/vue/outline';
import { debounce } from 'lodash';
const innerWidth = ref(window.innerWidth);
const show = ref(innerWidth.value >= 1280 ? true : false);
const checkWindowSize = () => {
if (window.innerWidth >= 1280) {
if (show.value === false && innerWidth.value < 1280) show.value = true;
} else {
if (show.value === true) show.value = false;
}
innerWidth.value = window.innerWidth;
};
onMounted(() => {
window.addEventListener('resize', debounce(checkWindowSize, 100));
});
onUnmounted(() => {
window.removeEventListener('resize', checkWindowSize);
});
設定後はリサイズによってサイドバーの表示・非表示が制御できるようになります。
ダークモードの設定
Tailwind CSSのDark Modeの機能を利用してダークモードの設定を行います。ダークモードの設定はTailwind CSSの設定ファイルであるtailwind.config.jsファイルにあるdarkModeオプションを利用します。デフォルトではfalseになっておりダークモードは利用できません。darkModeオプションではmediaまたはclassを設定することができます。mediaはOS(Operating System)のダークモードの設定から情報を取得してダークモードへ変更することができます。OSのダークモードの設定についてはmacOSであればシステム環境設定→一般から設定することができます。
classの場合は手動でダークモードへの切り替えを行うことができます。本文書では両方の設定で動作確認を行います。
mediaによるダークモード設定
macOSの場合はシステム環境設定→一般から外観モードを”ダーク”に設定しておきます。
tailwind.config.jsのdarkModeオプションをmediaに変更します。
module.exports = {
purge: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
darkMode: 'media', // or 'media' or 'class'
//略
後はダークモードに設定したいclassにdark:をつけていきます。サイドバーの背景色をダークモードの場合にbg-gray-800にしたい場合はdark:を先頭についてdark:bg-gray-800と設定します。
<div
class="fixed top-0 w-64 h-screen bg-white dark:bg-gray-800 z-20 transform duration-300"
:class="{ '-translate-x-full': !show }"
>
サイドバー
</div>
ブラウザで確認するとサイドバーの領域のみ背景色がダークカラーになっていることが確認できます。mediaを設定した場合はこれだけの設定でダークモードに変更することができます。OSから情報を取得する処理はTailwind CSSが行ってくれているので何か特別の設定を行う必要がありません。
システム環境設定→一般から外観モードを”ライト”の設定に戻すとdark:で設定した値が適用されないことを確認してください。
再度OSの外観モードをダークに設定して他の要素にもdark:の設定を行なっていきます。各要素の背景色とテキストの文字にダークモード設定を行います。
<template>
<div class="relative">
<div
class="
fixed
top-0
w-64
h-screen
bg-white
dark:bg-gray-800
z-20
transform
duration-300
dark:text-gray-300
"
:class="{ '-translate-x-full': !show }"
>
サイドバー
</div>
<div
class="fixed xl:hidden inset-0 bg-gray-900 z-10 opacity-50"
@click="show = !show"
v-show="show"
></div>
<div
class="bg-gray-100 dark:bg-gray-900 h-screen overflow-hidden duration-300"
:class="{ 'xl:pl-64': show }"
>
<div class="bg-white dark:bg-gray-800 rounded shadow m-4 p-4">
<MenuIcon
class="h-6 w-6 text-gray-600 dark:text-gray-300 cursor-pointer"
@click="show = !show"
/>
</div>
<div class="dark:text-gray-300">
<slot />
</div>
</div>
</div>
</template>
表示されているページ全体がダークモードに対応することができるようになります。
classによるダークモード設定
tailwind.config.jsのdarkModeオプションを'class'に設定します。
module.exports = {
purge: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
darkMode: 'class', // or 'media' or 'class'
//略
default.vueはdarkModeオプションを'media'に設定した場合に作成した内容をそのまま利用します。'class'の場合はhtmlタグにclass="dark"を追加することでダークモードに変更させることができます。
index.htmlファイルを開いてhtmlタグにclass="dark"を追加してください。追加後保存するとダークモードへと変更されます。
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
ダークモードに変更することができましたがこのままでは手動で切り替えることはできません。一度htmlタグに追加したclass="dark"を削除します。
切り替えを行うために変数themeを追加します。themeは'light'と'dark'の値を持ちデフォルトは'light'に設定しておきます。
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
//略
const theme = ref('light');
//略
</script>
ダークモードを切り替えるためのアイコンを追加します。アイコンは上部のメニューバーに追加します。themeの値によってダークモードの場合はSunIcon(太陽)のアイコン、ライトモードの場合はMoonIcon(月)のアイコンが表示されます。flexを利用してメニューアイコンは左側、ダークモードの切り替えアイコンは右側に表示させています。
//略
import { MenuIcon, MoonIcon, SunIcon } from '@heroicons/vue/outline';
//略
<div
class="flex items-center justify-between bg-white dark:bg-gray-800 rounded shadow m-4 p-4"
>
<MenuIcon
class="h-6 w-6 text-gray-600 dark:text-gray-300 cursor-pointer"
@click="show = !show"
/>
<div>
<MoonIcon
class="w-7 h-7 text-gray-600 cursor-pointer"
v-if="theme === 'light'"
/>
<SunIcon class="w-7 h-7 text-gray-300 cursor-pointer" v-else />
</div>
</div>
太陽のアイコン、月のアイコンをクリックしたらモードが変わるようにそれぞれにclickイベントを設定します。clickイベントにはchangeMode関数を設定し、引数を取れるように設定しておきます。
<MoonIcon
class="w-7 h-7 text-gray-600 cursor-pointer"
@click="changeMode('dark')"
v-if="theme === 'light'"
/>
<SunIcon
class="w-7 h-7 text-gray-300 cursor-pointer"
@click="changeMode('light')"
v-else
/>
script setupタグの中にchageMode関数を追加します。refで定義した値を更新したい場合はtheme.valueで行います。
const theme = ref('light');
const changeMode = (mode) => {
theme.value = mode;
};
アイコンをクリックするたびに太陽のアイコンと月のアイコンが交互に表示されることを確認してください。動作が確認できたらdocument.documentElementでhtml要素を取得してclass="dark"を追加、削除させることでダークモードの切り替えを行います。
const changeMode = (mode) => {
theme.value = mode;
theme.value === 'light'
? document.documentElement.classList.remove('dark')
: document.documentElement.classList.add('dark');
};
月のアイコンをクリックしたらダークモードに変更されることを確認します。ダークモードに変更後は太陽のアイコンになっていることが確認できます。
再度太陽のアイコンをクリックすると元の状態に戻ることを確認してください。これでdarkModeオプションの'class'によるダークモードの切り替えを実装することができました。
一度モードを切り替えたら次回アクセス時もそのモードを維持できるようにブラウザのlocalStoragethemeを利用してモードの値を保存します。
changeMode関数の中にlocalstorage.theme = modeを追加します。
const changeMode = (mode) => {
theme.value = mode;
theme.value === 'light'
? document.documentElement.classList.remove('dark')
: document.documentElement.classList.add('dark');
localStorage.theme = mode;
};
ローカルストレージに保存されている値はデベロッパーツールのアプリケーションから確認することができます。
アクセス後にローカルストレージを確認するコードを追加します。
//略
const theme = ref('light');
if (localStorage.theme === 'dark') {
document.documentElement.classList.add('dark');
theme.value = 'dark';
} else {
document.documentElement.classList.remove('dark');
theme.value = 'light';
}
const changeMode = ( mode ) => {
//略
コードを追加後にダークモードでブラウザを閉じ、再度アクセスするとダークモードで表示されることが確認できます。ここまでの設定でダークモードを設定できるようになりました。
ドロップダウンメニュー
一般的なダッシュボードでは画面右上にアイコンがあり、そのアイコンをクリックするとメニューが表示されます。本文書では上部のメニューバーに人物画像を設定して画像をクリックするとドロップダウンメニューが表示されるように設定を行います。人物画像はassetsフォルダにavatar.jpgで保存しています。画像ファイルは各自準備してください。
componentsフォルダにDropdownMenu.vueファイルを作成します。
<script setup></script>
<template>
<div>
<img
src="../assets/avatar.jpg"
class="rounded-full w-10 h-10 cursor-pointer"
/>
</div>
</template>
作成したDropdownMenu.vueファイルをDefault.vueファイルでimportします。
import DropdownMenu from '../components/DropdownMenu.vue';
importしたDropdownMenuコンポーネントをダークモード切り替えアイコンの横に追加します。flexを利用してダークモード切り替えアイコンと横並びにして間にはスペース(space-x-4)を設定しています。
<div class="flex items-center space-x-4">
<MoonIcon
class="w-7 h-7 text-gray-600 cursor-pointer"
@click="changeMode('dark')"
v-if="theme === 'light'"
/>
<SunIcon
class="w-7 h-7 text-gray-300 cursor-pointer"
@click="changeMode('light')"
v-else
/>
<DropdownMenu />
</div>
設定後メニューバーの右端に画像が表示されます。
画像をクリックすると表示されるドロップダウンメニューを作成します。画像の親要素のdivにpositionのrelativeを設定し、ドロップダウンメニューはabsolute設定で画像の親要素を基準にtop-16, right-0を設定しています。ログアウトとプロファイルのリンクを設定していますがこれらのページは存在しないのでアプリケーションに合わせてメニューのリストを変更する必要があります。
<script setup>
import { UserIcon, LogoutIcon } from '@heroicons/vue/outline';
</script>
<template>
<div class="relative">
<img
src="../assets/avatar.jpg"
class="rounded-full w-10 h-10 cursor-pointer"
/>
<div
class="absolute top-16 right-0 z-10 w-40 py-2 bg-white rounded-sm shadow"
>
<ul>
<li class="text-gray-700 hover:bg-blue-100 hover:text-blue-600 p-2">
<a href="/#" class="flex items-center space-x-2">
<UserIcon class="w-5 h-5" />
<span class="text-sm font-bold">プロファイル</span></a
>
</li>
<li class="text-gray-700 hover:bg-blue-100 hover:text-blue-600 p-2">
<a href="/#" class="flex items-center space-x-2">
<LogoutIcon class="w-5 h-5" />
<span class="text-sm font-bold">ログアウト</span></a
>
</li>
</ul>
</div>
</div>
</template>
ブラウザで確認すると画像の下にメニューが表示されます。
メニューの上にマウスがのせるとhoverによって文字色と背景色が変わるように設定しています。
画像をクリックすることでメニューの表示・非表示を行えるようにimg要素にclickイベントを設定して関数toggleを設定します。
<img
src="../assets/avatar.jpg"
class="rounded-full w-10 h-10 cursor-pointer"
@click="toggle"
/>
変数showを追加し、showの値によってメニューの表示・非表示を切り替えます。showの値の切り替えにはtoggle関数を利用します。toggle関数ではshowの値がfalseの場合はtrueに、trueの場合はfalseに更新します。
import { ref } from 'vue';
import { UserIcon, LogoutIcon } from '@heroicons/vue/outline';
const show = ref(false);
const toggle = () => {
show.value = !show.value;
};
v-showディレクティブをドロップダウンメニューの要素に設定します。
<div
class="absolute top-16 right-0 z-10 w-40 py-2 bg-white rounded-sm shadow"
v-show="show" //追加
>
<ul>
<li class="text-gray-700 hover:bg-blue-100 hover:text-blue-600 p-2">
<a href="/#" class="flex items-center space-x-2">
<UserIcon class="w-5 h-5" />
<span class="text-sm font-bold">プロファイル</span></a
>
</li>
<li class="text-gray-700 hover:bg-blue-100 hover:text-blue-600 p-2">
<a href="/#" class="flex items-center space-x-2">
<LogoutIcon class="w-5 h-5" />
<span class="text-sm font-bold">ログアウト</span></a
>
</li>
</ul>
</div>
ドロップダウンメニューの設定は完了し画像をクリックするとメニューの表示・非表示を切り替えることができるようになりました。
アニメーションの設定
ドロップダウンメニューを追加しましたが画像をクリックすると一瞬でメニューが表示され再度クリックするとメニューが非表示になります。この表示・非表示の切り替え時にアニメーションの設定を行います。
ドロップダウンメニューではVueのtransitionを利用してアニメーションの設定を行います。Vueのtransitionを利用したい場合はv-if, v-showディレクティブを持つ要素に対してtranstionタグで包みます。
アニメーションの変化はstyleタグにcssを記述して行う方法だけではなくclass属性も利用することができます。ドロップダウンメニューではclass属性を利用します。表示・非表示のみの切り替えなのでopacityを利用してアニメーション設定を行います。
Vueのtransitionの設定ではEnterとLeaveにおける状態の理解が必要となります。
v-enter-fromが初期の状態で今回のドロップダウンメニューであれば非表示の状態(show=false)です。非表示なのでopacityは0に設定します。v-enter-toは非表示から表示に切り替わった後の状態で表示されているのでopacityは1になります。v-enter-activeでは非表示から表示までの表示中の遷移の状態を設定します。遷移にかかる時間の設定durationやeasingを設定します。easingはアニメーションの速度の調整に利用します。
v-leave-fromは表示されている状態を表しておりopacityは1です。今回の例では表示された状態からクリックを押すと非表示になります。非表示の状態を表すのがv-leave-toでopacityは0に設定します。v-leave-activeは表示から非表示中の遷移の状態を設定します。
Tailwind CSSのclassを利用してtransitionを設定します。設定を行うと先ほどまでとは異なりゆっくりと表示・非表示に繰り返されるようになります。
<transition
enter-active-class="transition-opacity"
enter-from-class="opacity-0"
leave-active-class="transition-opacity"
leave-to-class="opacity-0"
>
<div
class="
absolute
top-16
right-0
z-10
w-40
py-2
bg-white
rounded-sm
shadow
"
v-show="show"
>
<ul>
//略
</ul>
</div>
</transition>
さらにゆっくりと表示・非表示を繰り返すためにdurationを設定することができます。duration-1000を設定すると1秒をかけて非表示から表示、表示から非表示に切り替わります。
<transition
enter-active-class="transition-opacity duration-1000"
enter-from-class="opacity-0"
leave-active-class="transition-opacity duration-1000"
leave-to-class="opacity-0"
>
durationの設定値による違いが確認できたら本文書ではduration-300に設定します。
opacity以外に縦方向の移動をわずかに加えたい場合は以下のようにtransform、translateを利用します。enter-active-classではopacityだけの変化にアニメーションを加えるためtransition-opacityを設定していましがtransformの変化にもアニメーションを加えるためtransitionに変更します。
<transition
enter-active-class="transition duration-300"
enter-from-class="transform opacity-0 -translate-y-2 "
leave-active-class="transition duration-300"
leave-to-class="transform opacity-0 -translate-y-2"
>
Vueのtransitionでは表示・非表示のアニメーションに加えてclassを追加することでいろいろな変化を加えることができます。
ダークモードの設定
メニューの要素にはダークモードの設定を行っていないので月のアイコンをクリックするダークモードに切り替わってもメニューだけはそのままのカラーになります。
ドロップダウンメニューにもダークモードを設定します。メニューの中の要素にdark:を追加します。hover:bg-blue-100を利用していますがほかのclassの設定方法と同様にdark:を追加しdark:hover:bg-blue-100でダークモードを設定することができます。
<div
class="
absolute
top-16
right-0
z-10
w-40
py-2
bg-white
rounded-sm
shadow
dark:bg-gray-800
"
v-show="show"
>
<ul>
<li
class="
text-gray-700
dark:text-gray-300
hover:bg-blue-100
dark:hover:bg-gray-700
hover:text-blue-600
dark:hover:text-blue-600
p-2
"
>
<a href="/#" class="flex items-center space-x-2">
<UserIcon class="w-5 h-5" />
<span class="text-sm font-bold">プロファイル</span></a
>
</li>
<li
class="
text-gray-700
dark:text-gray-300
hover:bg-blue-100
dark:hover:bg-gray-700
hover:text-blue-600
dark:hover:text-blue-600
p-2
"
>
<a href="/#" class="flex items-center space-x-2">
<LogoutIcon class="w-5 h-5" />
<span class="text-sm font-bold">ログアウト</span></a
>
</li>
</ul>
</div>
メニューの要素に対してダークモードが設定されました。
画像クリック以外での表示・非表示設定
メニューの表示・非表示を切り替えるためには必ず画像をクリックする必要があります。表示されているメニューの外側をクリックしても表示から非表示に切り替わるように設定を行います。イベントリスナーを登録・削除するのでVueのライフサイクルフックのonMountedとonUnmountedを利用します。
DropdownMenuコンポーネント全体の要素の情報を保存するために変数rootを追加します。
import { ref } from 'vue';
import { UserIcon, LogoutIcon } from '@heroicons/vue/outline';
const show = ref(false);
const root = ref(null);
const toggle = () => {
show.value = !show.value;
};
要素を取得するために要素に対して直接アクセスできるtemplate refsを利用します。
<template>
<div class="relative" ref="root">
<img
メニュー要素の外側をクリックしたかどうか判断するためにクリックした要素がrefを設定したdiv要素の内側にあるかどうかチェックする関数clickOutsideを追加します。
const clickOutside = (e) => {
if (!root.value.contains(e.target) && show.value) {
show.value = false;
}
};
clickOutside関数はイベントリスナーに登録します。イベントリスナーへの登録はライフサイクルフックを利用します。コンポーネントのマウント時にclickOutside関数を追加し、アンマウント時に削除しています。
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { UserIcon, LogoutIcon } from '@heroicons/vue/outline';
const show = ref(false);
const root = ref(null);
const toggle = () => {
show.value = !show.value;
};
const clickOutside = (e) => {
if (!root.value.contains(e.target) && show.value) {
show.value = false;
}
};
onMounted(() => document.addEventListener('click', clickOutside));
onUnmounted(() => document.removeEventListener('click', clickOutside));
</script>
clickOutside関数追加後に表示したメニューの外側をクリックするとメニューが非表示になることを確認してください。
これでドロップダウンメニューの実装は完了です。
サイドバーの設定
ここまでサイドバーには何も設定を行っていませんでしたが、サイドバーにロゴとメニューリストを追加します。表示するメニューリストにはアコーディオンメニューで実装します。
ロゴの設定
アプリケーションのロゴを設定する場所を作成するためにcomponentsフォルダにSidebar.vueファイルを作成します。
<script setup></script>
<template>
<div class="p-4">
<div class="font-bold text-lg text-blue-600">LOGO</div>
</div>
</template>
作成したSidebar.vueはDefault.vueファイルでimportします。
import Sidebar from '../components/Sidebar.vue';
文字列のサイドバーからimportしたDefaultコンポーネントに変更します。
<template>
<div class="relative">
<div
class="
fixed
top-0
w-64
h-screen
bg-white
dark:bg-gray-800
z-20
transform
duration-300
dark:text-gray-300
"
:class="{ '-translate-x-full': !show }"
>
<Sidebar />
</div>
ブラウザで確認すると左上に青文字でLOGOという文字が表示されます。ここでは文字列でLOGOにしていますが文字列またはSVG, 画像などでロゴを設定してください。
メニューリストの設定
サイドバーに表示させるメニューリストを設定するためcomponentsフォルダにList.vueファイルを作成します。List.vueファイルの中にはメニューリストの情報を保持するためにreactiveを利用してlistsを定義します。
v-forディレクティブでlistsを展開してnameを表示させます。lists変数の中ではsublistsによってメニューリストは階層化されていますが最初は無視します。
<script setup>
import { reactive } from 'vue';
const lists = reactive([
{
name: 'ダッシュボード',
icon: 'TemplateIcon',
link: '/',
},
{
name: 'EC',
icon: 'ShoppingCartIcon',
link: '/#',
sublists: [
{
name: '商品一覧',
link: '/#',
},
{
name: '注文一覧',
link: '/#',
},
],
},
]);
</script>
<template>
<ul class="text-gray-700">
<li class="mb-1" v-for="list in lists" :key="list.name">
<a
:href="list.link"
class="block p-2 rounded-sm hover:text-white hover:bg-blue-400"
>
<span>{{ list.name }}</span>
</a>
</li>
</ul>
</template>
作成したList.vueファイルをSidebar.vueファイルでimportします。
<script setup>
import List from './List.vue';
</script>
<template>
<div class="p-4">
<div class="font-bold text-lg text-blue-600">LOGO</div>
<div class="mt-8">
<List />
</div>
</div>
</template>
ブラウザで確認するとサイドバーにメニューリストが表示されます。
メニューリストの一つにマウスを乗せるとhoverの設定により背景色と文字列が変わります。
Dynamic Componentsによるアイコン設定
メニューリストの左側にlistsに含まれているiconを表示させるための設定を行います。listsに含まれるiconの値を動的に設定するためにDynamic Componentsを利用します。Dynamic Componentsはcomponentタグを利用します。is属性に表示させたいコンポーネント名を指定します。
<component :is="component_name"></component>
componentタグを追加してis属性にアイコンのコンポーネント名が含まれるlist.iconを設定します。list.nameと横並びになるように親要素にflexを追加しています。Iconのimportも必須なので忘れないで行ってください。
import { TemplateIcon, ShoppingCartIcon } from '@heroicons/vue/outline';
//略
<template>
<ul class="text-gray-700">
<li class="mb-1" v-for="list in lists" :key="list.name">
<a
:href="list.link"
class="
flex
items-center
block
p-2
rounded-sm
hover:text-white hover:bg-blue-400
"
>
<component :is="list.icon" class="w-6 h-6 mr-2"></component>
<span>{{ list.name }}</span>
</a>
</li>
</ul>
</template>
ブラウザで確認するとアイコンの領域は確保されていますがアイコンは表示されません。デベロッパーツールでどのような状態になっているか確認するとtemplateicon、shoppingcarticonタグが表示されていることは確認することができます。しかしこの状態ではアイコンは表示されません。
script setupではdynamic componentsを利用するためにはアイコン名の対応が記述されているオブジェクトを事前に設定しておく必要があります。
const icons = {
TemplateIcon: TemplateIcon,
ShoppingCartIcon: ShoppingCartIcon,
};
このオブジェクトを利用してコードを更新します。
<component :is="icons[list.icon]" class="w-6 h-6 mr-2"></component>
設定後ブラウザで確認するとアイコンが表示されます。デベロッパーツール上では先ほどのようにtempateicon, shppingcarticonタグではくsvgタグを確認することができます。
リストの階層化
階層化されたサブリストを展開できるようにコードを更新します。サブリスト(sublists)を持っているリストと持っていないリストをv-ifディレクティブで分岐を行っています。サブリストを持っている場合はさらにv-forでsublistsの展開を行っています。
<template>
<ul class="text-gray-700">
<li class="mb-1" v-for="list in lists" :key="list.name">
<a
v-if="!list.sublists"
:href="list.link"
class="
flex
items-center
block
p-2
rounded-sm
hover:text-white hover:bg-blue-400
"
>
<component :is="icons[list.icon]" class="w-6 h-6 mr-2"></component>
<span>{{ list.name }}</span>
</a>
<div
v-else
class="
flex
items-center
p-2
cursor-pointer
rounded-sm
hover:bg-blue-400 hover:text-white
"
>
<component :is="icons[list.icon]" class="w-6 h-6 mr-2"></component>
<span>{{ list.name }}</span>
</div>
<ul class="mt-1">
<li class="mb-1" v-for="list in list.sublists" :key="list.name">
<a
:href="list.link"
class="block p-2 rounded-sm hover:bg-blue-400 hover:text-white"
>
<span class="pl-8">{{ list.name }}</span>
</a>
</li>
</ul>
</li>
</ul>
</template>
ブラウザで確認するとECはサブリストを持っているのでその下に展開した商品一覧と注文一覧が表示されます。
アコーディオンメニューの設定
sublistsを開閉できるようにsublistsを持つリストにshowプロパティを追加します。デフォルトではfalseに設定します。listsにreactivityを持たせるためにreactiveで設定した意味がここにあります。
const lists = reactive([
{
name: 'ダッシュボード',
icon: 'TemplateIcon',
link: '/',
},
{
name: 'EC',
icon: 'ShoppingCartIcon',
link: '/#',
show: false,//追加
sublists: [
{
name: '商品一覧',
link: '/#',
},
{
name: '注文一覧',
link: '/#',
},
],
},
]);
showがfalseの場合は非表示になるようにsublistsの要素にv-showを設定します。
<ul class="mt-1" v-show="list.show">
<li class="mb-1" v-for="list in list.sublists" :key="list.name">
<a
:href="list.link"
class="block p-2 rounded-sm hover:bg-blue-400 hover:text-white"
>
<span class="pl-8">{{ list.name }}</span>
</a>
</li>
</ul>
v-showを設定するとshowはデフォルトでfalseに設定されているのでsublistsは非表示に状態になります。
リストをクリックするとsublistsが表示されるようにtoggle関数を追加します。どのリストがクリックされたかを識別するために引数にリストの名前を設定します。
<div
v-else
class="
flex
items-center
p-2
cursor-pointer
rounded-sm
hover:bg-blue-400 hover:text-white
"
@click="toggle(list.name)"
>
<component :is="icons[list.icon]" class="w-6 h-6 mr-2"></component>
<span>{{ list.name }}</span>
</div>
script setupタグの中にtoggle関数を追加します。findメソッドを利用して引数のnameを持つリストを取得してそのリストのshowの値を更新しています。
const toggle = (name) => {
const list = lists.find((list) => list.name === name);
list.show = !list.show;
};
設定後リストをクリックすることで表示・非表示を繰り返すことが可能になりました。
メニューリストのアニメーション設定
表示されている”ダッシュボード”、”EC”のリストを見てもどちらがsublistsを持っているかわかりません。sublistsを持っているか識別できるようにアイコンを設定します。
<div
v-else
class="
flex
items-center
justify-between
p-2
cursor-pointer
rounded-sm
hover:bg-blue-400 hover:text-white
"
@click="toggle(list.name)"
>
<div class="flex items-center">
<component
v-bind:is="icons[list.icon]"
class="w-6 h-6 mr-2"
></component>
<span>{{ list.name }}</span>
</div>
<ChevronDownIcon class="w-4 h-4" />
</div>
ChervonDownIconのimportも忘れずに行います。
import {
TemplateIcon,
ShoppingCartIcon,
ChevronDownIcon,
} from '@heroicons/vue/outline';
ECのリストのみ下矢印が表示されるようになります。
表示と非表示を矢印の向きで区別できるようにclassを設定しリストのshowの値とtransformのrotateを利用します。アニメーションの設定も一緒に行います。
<ChevronDownIcon
class="w-4 h-4 transform duration-300"
:class="!list.show ? 'rotate-0' : '-rotate-180'"
/>
リストをクリックするとアニメーションにより矢印が右回転で180度回転して下向きから上向きに表示されます。
アコーディオンメニューのアニメーション設定
サブリストの表示・非表示が一瞬で切り替わるためドロップダウンメニューの場合と同様にvueのtransitionを利用してアニメーションを設定します。
sublistsのul要素をtransitionタグで囲みます。ul要素のclassにoverflow-hiddenを追加しています。overflow-hiddenを追加しない場合、下にリストがある場合にアニメーションの動作時に下のリストと文字が被るなど正しい動作になりません。
<transition>
<ul class="mt-1 overflow-hidden" v-show="list.show">
<li class="mb-1" v-for="list in list.sublists" :key="list.name">
<a
:href="list.link"
class="block p-2 rounded-sm hover:bg-blue-400 hover:text-white"
>
<span class="pl-8">{{ list.name }}</span>
</a>
</li>
</ul>
</transition>
styleタグの中でclassの設定を行います。しかし下記の設定ではアニメーションとして動作しません。
<style scoped>
.v-enter-from,
.v-leave-to {
height: 0;
}
.v-enter-active,
.v-leave-active {
transition: height 0.3s;
}
</style>
アニメーションを動作させるためにはサブリストが開いた後の高さが必要となりますがサブリストのリスト数によって高さは変わるので1つのheightの固定値は指定することができません。1つの固定値を設定した場合にはアニメーションが動作するのか動作するためにheight:100pxを設定します。
<style scoped>
.v-enter-from,
.v-leave-to {
height: 0;
}
.v-enter-active,
.v-leave-active {
transition: height 0.3s;
}
.v-enter-to,
.v-leave-from {
height: 100px;
}
</style>
height:100pxを設定するとアニメーションが動作しゆっくりと開閉を行います。heightを固定値の100pxに設定しているのでもう一つsublistsにカテゴリー一覧を追加してみましょう。また下にリストがあっても正常に動作するのか確認するためにダッシュボードのリストを下にも追加します。
const lists = reactive([
{
name: 'ダッシュボード',
icon: 'TemplateIcon',
link: '/',
},
{
name: 'EC',
icon: 'ShoppingCartIcon',
link: '/#',
show: false,
sublists: [
{
name: '商品一覧',
link: '/#',
},
{
name: '注文一覧',
link: '/#',
},
{
name: 'カテゴリー一覧',
link: '/#',
},
],
},
{
name: 'ダッシュボード',
icon: 'TemplateIcon',
link: '/',
},
]);
sublistsを追加するとカテゴリー一覧の表示のアニメーションの動作が正常に動作しません。カテゴリー一覧が表示される場所が100pxを超えているためです。固定値を設定するとアニメーションは動作するがさまざまな高さを持つ場合には対応できないことがわかりました。
リストの数に依存せずアニメーションを動作させるためにはtransitionの動作時にheightを取得する必要があります。Vueのtransitionはclassだけではなく関数を利用することもできるためその機能を利用します。
styleタグの中には.v-enter-active, .v-leave-activeのみ残して他は削除します。
<style scoped>
.v-enter-active,
.v-leave-active {
transition: height 0.3s;
}
</style>
transitionタグへの関数の設定方法についてはVueのドキュメントではtransitionのJavaScript Hookに記載されています。transitionタグにenter、leaveを追加します。
<transition @enter="enter" @leave="leave">
<ul class="mt-1 overflow-hidden" v-show="list.show">
<li class="mb-1" v-for="list in list.sublists" :key="list.name">
<a
:href="list.link"
class="block p-2 rounded-sm hover:bg-blue-400 hover:text-white"
>
<span class="pl-8">{{ list.name }}</span>
</a>
</li>
</ul>
</transition>
script setupの中に指定したenter、leave関数を追加します。
const enter = (element) => {
element.style.height = "auto";
const height = getComputedStyle(element).height;
element.style.height = 0;
getComputedStyle(element);
setTimeout(() => {
element.style.height = height;
});
};
const leave = (element) => {
element.style.height = getComputedStyle(element).height;
getComputedStyle(element);
setTimeout(() => {
element.style.height = 0;
});
};
2つの関数を追加後、heightが関数内で取得されるためsublistsのリスト数によらずsublistsの開閉がアニメーションによってスムーズに行われることがわかります。
アニメーションが設定されたアコーディオンメニューを実装することができました。
ダークモード設定
メニューリストもダークモードに設定すると文字が見えなくなってしまうのでdark:の設定を行います。
テキストの文字の色をダークモードの場合はtext-gray-300に設定します。
<template>
<ul class="text-gray-700 dark:text-gray-300">
サイドバーもダークモードに対応できるようになりました。
Vue Routerの設定
ダッシュボードのレイアウトの作成が完了したのでリンクを使ってページが移動できるようにVue Routerをインストールします。Vue Routerを利用することでシングルページアプリケーションを構築することができます。
Vue Routerのインストールはnpmコマンドで行います。
% npm install vue-router@4
インストール完了後、srcフォルダにrouterフォルダを作成しその中にindex.jsファイルを作成します。index.jsファイルの中でルーティングの設定を行っていきます。
ドロップダウンメニューのプロファイルページ、サイドバーのダッシュボード、注文一覧、商品一覧のルーティングを追加します。各ルーティングに対応するコンポーネントはsrcフォルダの下に作成するpagesフォルダの中に保存します。Vue Routerの設定が完了するとindex.jsファイル内のルーティングの設定を元に/profileにアクセスがあるとProfileコンポーネントの内容が表示されることになります。
import { createRouter, createWebHistory } from 'vue-router';
import Dashboard from '../pages/Dashboard.vue';
import Profile from '../pages/Profile.vue';
import Order from '../pages/Order.vue';
import Product from '../pages/Product.vue';
const routes = [
{
path: '/',
name: 'Dashboard',
component: Dashboard,
},
{
path: '/profile',
name: 'Profile',
component: Profile,
},
{
path: '/order',
name: 'Order',
component: Order,
},
{
path: '/product',
name: 'Product',
component: Product,
},
];
const router = createRouter({
history: createWebHistory(),
routes,
});
export default router;
pagesフォルダの下にDashboar.vue, Profile.vue, Order.vue, Product.vueファイルを作成します。Dashboard.vueファイルの内容は下記の通りです。そのほかのファイルも下記を参考にダッシュモードの文字のみ変更を行います。
<script setup></script>
<template>
<div>ダッシュボード</div>
</template>
作成したrouterの設定をVueに登録するためmain.jsファイルを更新します。
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import './index.css';
createApp(App).use(router).mount('#app');
/(ルート)にアクセスした場合にDashboardコンポーネントの内容を表示させるためにApp.vueファイルにrounter-viewタグを追加します。router-viewの場所にルーティングで設定したコンポーネントの内容が表示されることになります。
<script setup>
import Default from './layouts/Default.vue';
</script>
<template>
<Default><router-view></router-view></Default>
</template>
ここまでの設定でブラウザを開いてlocalhost:3000にアクセスしてダッシュボードの文字列が表示され、localhost:3000/profileにアクセスしてプロファイルの文字列が表示されればVue Routerの設定は問題なく行われています。/order, /productにアクセスすると対応するコンポーネントに記述した内容が表示されるかも確認を行ってください。
リンクの設定
ドロップダウンメニューのプロファイルをクリックすると/profileに移動できるように設定を行います。これまではリンクの設定ではaタグを利用してリンク先はすべて/#としていました。
Vue Routerではリンクにはrouter-linkタグを利用します。
DropDownMenu.vueファイルを開いたaタグをrouter-linkに変更し、hrefをtoに変更し値を/profileに設定します。
<router-link to="/profile" class="flex items-center space-x-2">
<UserIcon class="w-5 h-5" />
<span class="text-sm font-bold">プロファイル</span></router-link
>
設定後ダッシュボードのページから画像をクリックしてドロップダウンメニューからプロファイルを選択してください。ページ全体の再読み込みが行われることなくメイン領域の内容がダッシュボードからプロファイルに更新されることが確認できます。
サイドバーのメニューのリストのリンク先を設定するためにList.vueファイルを開いてlistsのlinkの値を設定します。
const lists = reactive([
{
name: 'ダッシュボード',
icon: 'TemplateIcon',
link: '/',
},
{
name: 'EC',
icon: 'ShoppingCartIcon',
link: '/#',
show: false,
sublists: [
{
name: '商品一覧',
link: '/product',
},
{
name: '注文一覧',
link: '/order',
},
],
},
]);
templateタグ内のaタグをrouter-linkタグに変更します。
<router-link
v-if="!list.sublists"
:to="list.link"
class="
flex
items-center
block
p-2
rounded-sm
hover:text-white hover:bg-blue-400
"
>
<component :is="icons[list.icon]" class="w-6 h-6 mr-2"></component>
<span>{{ list.name }}</span>
</router-link>
設定後はサイドメニューのリストを選択してもページのリロードなしにページ移動を行うことができます。
現在表示しているページの情報
現在表示しているページの情報を取得したい場合はuseRouteを利用することができます。list.vueファイルでvue-routerからuseRouteをimportします。
import { reactive } from "vue";
import { useRoute } from 'vue-router';
どのような情報が含まれているか確認したい場合はconsole.log(useRoute())で確認できます。現在のアクセスしているパスの情報はuserRoute().fullPathで確認できます。ページを移動する度にfullPathを取得するためにはcomputedプロパティを利用します。script setupではcomputedプロパティを利用するためにimportが必要になります。
import { reactive, computed } from 'vue';
import { useRoute } from 'vue-router';
//略
const currentRoute = computed(() => {
return useRoute().fullPath;
});
表示しているページがどのページなのかサイドバーのメニューリストでわかるようにuseRoute().fullPathを利用します。currentRouteの値とlist.linkの値が一致した場合にbg-blue-600とtext-whiteを適用します。
<router-link
v-if="!list.sublists"
:to="list.link"
class="
flex
items-center
block
p-2
rounded-sm
hover:text-white hover:bg-blue-400
"
:class="{
'bg-blue-600 text-white': currentRoute === list.link,
}"
>
商品一覧ページが表示されている場合は下記のように表示されます。
ここまでの作業でダッシュボードのレイアウトの作成は完成でダッシュボード上に表示させる内容についてはrouter/index.jsで指定したルーティングに対応するコンポーネントの中に記述することになります。
ダッシュボードのレイアウト作成後はユーザ認証機能の実装を行います。