• #nuxt
  • #vuetify
  • #migration
  • #vue3
未分類

Eurekapu 移行計画: Nuxt 2 → Nuxt 3 + Vuetify 3

概要

項目現在移行先
フレームワークNuxt 2.15.8Nuxt 3
UIライブラリVuetify 1.xVuetify 3
VueVue 2 (Options API)Vue 3 (Composition API)
状態管理VuexPinia
Vueファイル数1052-

移行戦略

段階的移行 - 両プロジェクトを並行運用しながらセクションごとに移行


Phase 0: 準備(基盤構築)

0-1. 新規リポジトリ作成

cd C:\Users\numbe\Git_repo
npx nuxi@latest init eurekapu-nuxt3
cd eurekapu-nuxt3
pnpm install

0-2. Vuetify 3 インストール

pnpm add -D vuetify vite-plugin-vuetify
pnpm add @mdi/font

0-3. nuxt.config.ts 設定

// nuxt.config.ts
import vuetify, { transformAssetUrls } from 'vite-plugin-vuetify'

export default defineNuxtConfig({
  build: {
    transpile: ['vuetify'],
  },
  modules: [
    (_options, nuxt) => {
      nuxt.hooks.hook('vite:extendConfig', (config) => {
        // @ts-expect-error
        config.plugins.push(vuetify({ autoImport: true }))
      })
    },
  ],
  vite: {
    vue: {
      template: {
        transformAssetUrls,
      },
    },
  },
})

0-4. Vuetify プラグイン作成

// plugins/vuetify.ts
import '@mdi/font/css/materialdesignicons.css'
import 'vuetify/styles'
import { createVuetify } from 'vuetify'

export default defineNuxtPlugin((app) => {
  const vuetify = createVuetify({
    theme: {
      defaultTheme: 'light',
    },
  })
  app.vueApp.use(vuetify)
})

0-5. Pinia インストール

pnpm add pinia @pinia/nuxt
// nuxt.config.ts に追加
modules: ['@pinia/nuxt'],

Phase 1: コアコンポーネント移行

対象ファイル(約20ファイル)

  • layouts/default.vue
  • components/BaseComponents/*.vue
  • components/Menu.vue
  • components/List.vue
  • components/Breadcrumbs.vue

変換パターン

グリッドシステム

<!-- 旧 Vuetify 2 -->
<v-layout row class="justify-center">
  <v-flex px-3 pt-1 xs12 sm6 md4>
    内容
  </v-flex>
</v-layout>

<!-- 新 Vuetify 3 -->
<v-row class="justify-center">
  <v-col cols="12" sm="6" md="4" class="px-3 pt-1">
    内容
  </v-col>
</v-row>

カードコンポーネント

<!-- 旧 Vuetify 2 -->
<v-card outlined flat>

<!-- 新 Vuetify 3 -->
<v-card variant="outlined" flat>

ルーターリンク

<!-- 旧 Nuxt 2 -->
<nuxt-link :to="path">

<!-- 新 Nuxt 3 -->
<NuxtLink :to="path">

ページスロット

<!-- 旧 layouts/default.vue -->
<nuxt />

<!-- 新 layouts/default.vue -->
<slot />

Phase 2: 状態管理

Vuex → Pinia 変換

// 旧: store/index.js (Vuex)
export const state = () => ({
  user: null,
  isLoggedIn: false,
})

export const getters = {
  isAuthenticated: (state) => state.user,
  isLoggedIn: (state) => state.isLoggedIn,
  user: (state) => state.user,
}

export const mutations = {
  SET_USER(state, { user }) {
    state.user = user
    state.isLoggedIn = true
  },
}

export const actions = {
  setUser({ commit }, payload) {
    commit('SET_USER', payload)
  },
}
// 新: stores/auth.ts (Pinia)
import { defineStore } from 'pinia'

export const useAuthStore = defineStore('auth', () => {
  const user = ref<User | null>(null)
  const isLoggedIn = computed(() => !!user.value)

  function setUser(newUser: User) {
    user.value = newUser
  }

  function logout() {
    user.value = null
  }

  return {
    user,
    isLoggedIn,
    setUser,
    logout,
  }
})

Phase 3: ページ移行(セクション別)

移行優先順位

優先度セクションファイル数備考
1pages/lessons/financial-statements/42メイン教材
2pages/lessons/intro-to-accounting/~50基礎コンテンツ
3pages/lessons/excel/~60独立性高い
4pages/lessons/excel-shortcuts/~40独立性高い
5pages/lessons/boki3/~300大規模
6pages/lessons/bookkeeping/~100-
7pages/lessons/consolidated/~50-
8その他残り順次対応

ページ変換例

<!-- 旧: pages/lessons/financial-statements/no0-introduction.vue -->
<template>
  <div>
    <v-layout row class="justify-center">
      <v-flex px-3 pt-1 xs12 sm12 md10 lg7>
        <h1>タイトル</h1>
        <nuxt-link :to="items[2].children[0].url">
          リンク
        </nuxt-link>
      </v-flex>
    </v-layout>
  </div>
</template>

<script>
export default {
  data() {
    return {
      items: [],
    }
  },
  computed: {
    someValue() {
      return this.items.length
    },
  },
  methods: {
    handleClick() {
      // ...
    },
  },
}
</script>
<!-- 新: pages/lessons/financial-statements/no0-introduction.vue -->
<template>
  <div>
    <v-row class="justify-center">
      <v-col cols="12" md="10" lg="7" class="px-3 pt-1">
        <h1>タイトル</h1>
        <NuxtLink :to="items[2].children[0].url">
          リンク
        </NuxtLink>
      </v-col>
    </v-row>
  </div>
</template>

<script setup lang="ts">
const items = ref([])

const someValue = computed(() => items.value.length)

function handleClick() {
  // ...
}
</script>

自動変換スクリプト

scripts/migrate-vuetify.mjs

import fs from 'fs'
import path from 'path'
import { glob } from 'glob'

const replacements = [
  // グリッドシステム
  [/<v-layout\s+row/g, '<v-row'],
  [/<\/v-layout>/g, '</v-row>'],
  [/<v-flex\s+([^>]*?)xs(\d+)/g, '<v-col $1cols="$2"'],
  [/<v-flex\s+([^>]*?)sm(\d+)/g, '<v-col $1sm="$2"'],
  [/<v-flex\s+([^>]*?)md(\d+)/g, '<v-col $1md="$2"'],
  [/<v-flex\s+([^>]*?)lg(\d+)/g, '<v-col $1lg="$2"'],
  [/<v-flex/g, '<v-col'],
  [/<\/v-flex>/g, '</v-col>'],

  // カード
  [/\soutlined(?=[\s>])/g, ' variant="outlined"'],
  [/\sflat(?=[\s>])/g, ' flat'],

  // ルーター
  [/<nuxt-link/g, '<NuxtLink'],
  [/<\/nuxt-link>/g, '</NuxtLink>'],

  // レイアウト
  [/<nuxt\s*\/>/g, '<slot />'],
]

async function migrateFile(filePath) {
  let content = fs.readFileSync(filePath, 'utf-8')
  let modified = false

  for (const [pattern, replacement] of replacements) {
    if (pattern.test(content)) {
      content = content.replace(pattern, replacement)
      modified = true
    }
  }

  if (modified) {
    fs.writeFileSync(filePath, content, 'utf-8')
    console.log(`Migrated: ${filePath}`)
  }
}

async function main() {
  const files = await glob('**/*.vue', { ignore: 'node_modules/**' })
  for (const file of files) {
    await migrateFile(file)
  }
}

main()

使用方法

cd eurekapu-nuxt3
node scripts/migrate-vuetify.mjs

チェックリスト

Phase 0: 準備

  • 新規リポジトリ作成
  • Nuxt 3 初期設定
  • Vuetify 3 インストール・設定
  • Pinia インストール・設定
  • 基本レイアウト動作確認

Phase 1: コアコンポーネント

  • layouts/default.vue 移行
  • components/Menu.vue 移行
  • components/List.vue 移行
  • components/Breadcrumbs.vue 移行
  • BaseComponents/*.vue 移行

Phase 2: 状態管理

  • Pinia store 作成
  • persistedstate 移行

Phase 3: ページ移行

  • financial-statements/ (42ファイル)
  • intro-to-accounting/
  • excel/
  • excel-shortcuts/
  • boki3/
  • その他

参考リンク


注意事項

  1. 並行運用期間: 移行中は両プロジェクトを並行運用し、セクション単位でリダイレクト設定
  2. テスト: 各フェーズ完了後に手動テスト必須
  3. バックアップ: 移行前に必ずgit commitでスナップショット作成
  4. 段階的リリース: 完成したセクションから順次本番反映

関連情報

元プロジェクト

プロジェクト統計

  • Vueファイル数: 1052
  • ページ数: ~500
  • コンポーネント数: ~100
  • データファイル: 35個 (JSON/JS)