Vue 3.x + Vuetify 3.x + Vue Routerでいい感じのアプリレイアウトをを実装する

これはなに Link to this heading

Vuetify 3で下図のようなアプリのひな型を作る。

完成図

環境 Link to this heading

  • Vue 3.2.45
  • Vuetify 3.1.1
  • TypeScript 4.9.4
  • Vue Router 4.1.6
  • Vite 3.2.5

Vuetify 3、TypeScript、Vue Routerは導入済みとする。

とりあえず使いたい場合 Link to this heading

ひな形として使えるVuetify 3のプロジェクトをGitHubに置いています 。 クローンするなどご自由にどうぞ。

流れ Link to this heading

  1. ヘッダ(v-app-bar)を作る
  2. ナビゲーションメニューを作る
  3. ページ切り替え部を作る
  4. フッタを作る
  5. すべてのパーツを組み合わせる

ヘッダを作る Link to this heading

アプリのヘッダとなる上部のバーを作る。<v-app-bar>で作成できる。

<v-app-bar>には、<v-app-bar-nav-icon><v-app-bar-title>、そして<v-btn>を配置する。

コンポーネント名役割
v-app-bar-nav-icon後で出てくるv-navigation-drawerを制御するためのアイコンボタン
v-app-bar-titleヘッダに表示するタイトル文字列
v-btnダークテーマ制御用のアイコンボタン
components/app/AppBar.vue
<script setup lang="ts">
  // props
  interface Props {
    title: string;
    isDarkTheme: boolean;
  }
  withDefaults(defineProps<Props>(), {
    title: 'title',
    isDarkTheme: false,
  });
  // emits
  interface Emits {
    (e: 'clickBarNavIcon'): void;
    (e: 'clickThemeIcon'): void;
  }
  const emits = defineEmits<Emits>();
  const onClickBarNavIcon = (): void => {
    emits('clickBarNavIcon');
  };
  const onClickThemeIcon = (): void => {
    emits('clickThemeIcon');
  };
</script>

<template>
  <v-app-bar density="compact" elevation="1">
    <template #prepend>
      <v-app-bar-nav-icon @click="onClickBarNavIcon()" />
    </template>

    <v-app-bar-title>{{ title }}</v-app-bar-title>

    <template #append>
      <v-btn icon @click="onClickThemeIcon()">
        <v-icon v-if="isDarkTheme">mdi-weather-night</v-icon>
        <v-icon v-else>mdi-weather-sunny</v-icon>
      </v-btn>
    </template>
  </v-app-bar>
</template>

ちなみに、これだけをApp.vueに配置すると、下図のようになる。

ヘッダだけ配置した図

ナビゲーションメニューを作る Link to this heading

<v-navigation-drawer>で作成できる。

<v-navigation-drawer><v-list>および<v-list-item>を配置することで、メニュー項目を作成できる。 作成するメニュー項目は<script>で配列として定義し、v-forで生成する。 こうするとメニューの追加や変更が楽になる。

components/app/NavigationDrawer.vue
<script setup lang="ts">
  // params
  const navs: {
    prependIcon: string;
    title: string;
    value: string;
    to: string;
  }[] = [
    {
      prependIcon: 'mdi-home',
      title: 'Home',
      value: 'home',
      to: '/',
    },
    {
      prependIcon: 'mdi-information',
      title: 'About',
      value: 'about',
      to: '/about',
    },
  ];
</script>

<template>
  <v-navigation-drawer>
    <v-list nav density="comfortable">
      <template v-for="nav in navs" :key="nav.value">
        <v-list-item
          density="compact"
          :prepend-icon="nav.prependIcon"
          :title="nav.title"
          :value="nav.value"
          :to="nav.to"
        />
      </template>
    </v-list>
  </v-navigation-drawer>
</template>

ヘッダとナビゲーションメニューを置くと、下図のようになる。

ヘッダとナビゲーションメニューを置いた図

ページ切り替え部を作る Link to this heading

Vue Routerによるページ切り替え部を作る。 Vue Routerによる切り替え部分は<router-view />だけで足りる。 これを<v-main>で囲うと、ヘッダ、ナビゲーションメニュー、フッタによる表示サイズ変更を切り替え部分に適用できる。

components/app/MainView.vue
<script setup lang="ts">
  //
</script>

<template>
  <v-main>
    <router-view />
  </v-main>
</template>

フッタを作る Link to this heading

<v-footer>app属性を追加して作成する。 <v-footer>order属性をorder="-1"とすると、ナビゲーションメニューよりも上にフッタを表示できる。

components/app/Footer.vue
<script setup lang="ts">
  // props
  interface Props {
    ownerName: string;
    publishedYear: number;
  }
  defineProps<Props>();

  const nowYear = new Date().getFullYear();
</script>

<template>
  <v-footer app order="-1" elevation="2">
    <v-row justify="center" no-gutters>
      <v-col class="text-center" cols="12">
        <span>© </span>
        <span v-if="publishedYear < nowYear"> {{ publishedYear }}  </span>
        <span class="mr-2">{{ nowYear }}</span>
        <span>
          <strong>{{ ownerName }}</strong>
        </span>
      </v-col>
    </v-row>
  </v-footer>
</template>

すべてのパーツを組み合わせる Link to this heading

すべてのパーツを組み合わせて、全体を完成させる。

ナビゲーションメニューの表示を切り替えるため、変数drawer<NavigationDrawer>にバインドし、<AppBar>のボタンで変更している。

また、ダークテーマへの切り替えのため、変数theme<v-app>にバインドし、<AppBar>のボタンで変更している。

App.vue
<script setup lang="ts">
  import { ref } from 'vue';

  import AppBar from '@/components/app/AppBar.vue';
  import NavigationDrawer from '@/components/app/NavigationDrawer.vue';
  import MainView from '@/components/app/MainView.vue';
  import Footer from '@/components/app/Footer.vue';

  // params
  const title: string = 'Example title';
  const ownerName: string = 'Example owner mame';
  const publishedYear: number = 2023;

  // refs
  const drawer = ref(false);
  const THEMES = {
    Light: 'light',
    Dark: 'dark',
  };
  const theme = ref(THEMES.Light);

  // functions
  const switchTheme = () => {
    theme.value = theme.value === THEMES.Light ? THEMES.Dark : THEMES.Light;
  };
</script>

<template>
  <v-app :theme="theme">
    <AppBar
      :title="title"
      color="primary"
      @click-bar-nav-icon="drawer = !drawer"
      @click-theme-icon="switchTheme"
    />
    <NavigationDrawer v-model="drawer" />
    <MainView />
    <Footer
      :owner-name="ownerName"
      :published-year="publishedYear"
      color="primary"
    />
  </v-app>
</template>

以上で、アプリのひな型が完成する。

完成図

参考文献・URL Link to this heading

Licensed under CC BY-NC-SA 4.0
最終更新 5月 21, 2023
Hugo で構築されています。
テーマ StackJimmy によって設計されています。