👏

# 6.5 Vue プロジェクト作成(Pinia / Router / Vuetify 導入)

に公開

ここからは フロントエンド(Vue 3) を構築する。
Django API と連携するために、Vue プロジェクトを新規作成し、ログイン画面を用意する。


1. Vue プロジェクト作成

cd project
mkdir frontend
cd frontend

# Vite ベースで Vue プロジェクトを作成
npm create vite@latest . -- --template vue

# 依存パッケージをインストール
npm install

👉 今回は Vue CLI ではなく Vite ベースで進める。
公式も Vue 3 以降は Vite を推奨している。


2. 開発に必要なライブラリを追加

# 状態管理(Pinia)
npm install pinia

# ルーティング(Vue Router)
npm install vue-router

# UI コンポーネント(Vuetify)
npm install vuetify
npm install sass sass-loader --save-dev
npm install @mdi/font

# API 通信(axios)
npm install axios

3. Vuetify の設定

src/plugins/vuetify.js を作成:

// src/plugins/vuetify.js
import { createVuetify } from "vuetify";
import * as components from "vuetify/components";
import * as directives from "vuetify/directives";
import { aliases, mdi } from "vuetify/iconsets/mdi";

export default createVuetify({
  components,
  directives,
  icons: {
    defaultSet: "mdi",
    aliases,
    sets: {
      mdi,
    },
  },
});

4. ルータとストアの設定

4.1 Router

src/router/index.js を作成:

import { createRouter, createWebHistory } from "vue-router";
import LoginView from "../views/LoginView.vue";

const routes = [
  { path: "/login", name: "Login", component: LoginView },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

export default router;

4.2 Store

src/store/auth.js を作成:

import { defineStore } from "pinia";

export const useAuthStore = defineStore("auth", {
  state: () => ({
    user: null,
    departments: [],
    roles: [],
  }),
  actions: {
    setUser(userData) {
      this.user = userData;
      this.departments = userData.departments || [];
      this.roles = userData.roles || [];
    },
    clearUser() {
      this.user = null;
      this.departments = [];
      this.roles = [];
    },
  },
});

5. main.js の修正

src/main.js を以下のように編集:

import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import { createPinia } from "pinia";
import vuetify from "./plugins/vuetify";

const app = createApp(App);

app.use(router);
app.use(createPinia());
app.use(vuetify);

app.mount("#app");

6. App.vue の修正

デフォルトの Vite テンプレートを削除し、Router が機能するように修正する。

<template>
  <router-view />
</template>

<script setup>
</script>

<style>
/* 共通スタイルをここに追加していく */
</style>

👉 これで /login にアクセスすると LoginView.vue が表示される。


7. axios の共通化

7.1 .env.development

frontend/.env.development を作成:

# Vue 側の環境変数は VITE_ プレフィックス必須
VITE_API_BASE_URL=http://localhost:8000/api/

👉 本番用には .env.production を用意して https://example.com/api/ などに切り替える。

補足:127.0.0.1 と localhost の違いに注意

Django 側の Cookie (sessionid, csrftoken) は 発行されたホスト名に紐づく

フロントが http://localhost:5173 で動いている場合、
VITE_API_BASE_URL を http://127.0.0.1:8000 にすると Cookie が別オリジン扱いになって送信されない

そのため、フロントとバックで 同じホスト名 (localhost) に統一 する必要がある


7.2 axios クライアント

src/api/axios.js を作成:

// src/api/axios.js
import axios from "axios";

// Cookie から csrftoken を取り出す関数
function getCsrfToken() {
  const match = document.cookie.match(/csrftoken=([\w-]+)/);
  return match ? match[1] : "";
}

const apiClient = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL,  // 例: http://127.0.0.1:8000/api/
  withCredentials: true,                       // Cookie を必ず送る
});

// リクエスト時に CSRF ヘッダを自動追加
apiClient.interceptors.request.use((config) => {
  const token = getCsrfToken();
  if (token) {
    config.headers["X-CSRFToken"] = token;
  }
  return config;
});

export default apiClient;


7.3 認証 API

src/api/auth.js を作成:

import apiClient from "./axios";

export function login(username, password) {
  return apiClient.post("auth/login/", { username, password });
}

export function logout() {
  return apiClient.post("auth/logout/");
}

export function authStatus() {
  return apiClient.get("auth/status/");
}

8. ログイン画面の作成

src/views/LoginView.vue を作成:

<template>
  <v-container>
    <v-card class="pa-6 mx-auto" max-width="400">
      <v-card-title>ログイン</v-card-title>
      <v-card-text>
        <v-form @submit.prevent="handleLogin">
          <v-text-field v-model="username" label="ユーザー名" required />
          <v-text-field v-model="password" label="パスワード" type="password" required />
          <v-btn type="submit" block color="primary">ログイン</v-btn>
        </v-form>
        <p v-if="error" class="text-error mt-2">{{ error }}</p>
      </v-card-text>
    </v-card>
  </v-container>
</template>

<script setup>
import { ref } from "vue";
import { useAuthStore } from "../store/auth";
import { login, authStatus } from "../api/auth";

const username = ref("");
const password = ref("");
const error = ref("");

const authStore = useAuthStore();

async function handleLogin() {
  try {
    await login(username.value, password.value);
    const res = await authStatus();
    authStore.setUser(res.data);
    alert("ログイン成功: " + res.data.username);
  } catch (err) {
    error.value = "ログイン失敗";
  }
}
</script>

9. 動作確認

  1. Django サーバーを起動

    python manage.py runserver
    
  2. Vue 開発サーバーを起動

    npm run dev
    
  3. http://localhost:5173/login にアクセス

  • ユーザー名・パスワード入力 → ログイン成功時にアラート表示
  • /api/auth/status/ からユーザ情報を取得して Pinia に保存

今回のゴール

  • Vue プロジェクトを新規作成(Vite ベース)
  • Pinia / Vue Router / Vuetify を導入
  • axios を共通化して env 管理に対応
  • App.vue を修正し、ログイン画面を正しく表示
  • Django API と接続してログイン確認

次の展開

次は 6.6 ログイン状態管理とメニュー画面 に進む。
ナビゲーションバーを Vuetify で実装し、ログイン状態に応じてメニューを制御する。

Discussion