1. 結論(この記事で得られること)
Nuxt2からNuxt3への移行で必ずと言っていいほど遭遇する「vue-loaderが見つからない」「Module not found: Can't resolve 'vue-loader'」系のエラー。
この記事では、私が過去5つのプロダクトで移行を経験して分かったエラーの原因パターンと具体的な解決法を体系化しました。
得られるもの:
- vue-loaderエラーが発生する5つの根本原因パターン
- 原因切り分けのための診断フローチャート
- パターン別の具体的な解決コード
- AIを使った最速デバッグ手法(Claude/GPT活用)
- 本番環境での安全な検証手順とテスト戦略
正直、私も最初の移行案件では3日溶かしました。でもパターンさえ押さえれば30分で解決できます。
2. 前提(環境・読者層)
想定読者
- Nuxt2プロジェクトをNuxt3へ移行中のエンジニア
- vue-loader関連のエラーで手が止まっている方
- 移行作業の工数を正確に見積もりたいリードエンジニア
環境前提
// 移行前(Nuxt2)
{
"nuxt": "^2.15.8",
"vue": "^2.6.14",
"vue-loader": "^15.9.8",
"webpack": "^4.46.0"
}
// 移行後(Nuxt3)
{
"nuxt": "^3.10.0",
"vue": "^3.4.0"
// vue-loaderは不要になる
}
重要な前提知識
Nuxt3は内部的にViteをデフォルトのビルドツールとして採用しています。つまり:
- webpackは標準では使われない
- vue-loaderも不要(Viteが直接.vueファイルを処理)
- ただし互換モードでwebpackを使うことも可能
この「ビルドツールの根本的な変更」が理解できていないと、エラーの原因が見えてきません。
3. Before:よくあるつまずきポイント
パターン1:package.jsonにvue-loaderが残っている
Nuxt2時代の依存関係をそのまま残してしまうケース。
// ❌ Nuxt3なのにこれが残っている
{
"dependencies": {
"nuxt": "^3.10.0",
"vue-loader": "^15.9.8" // これが原因
}
}
エラーメッセージ:
Module not found: Error: Can't resolve 'vue-loader'
パターン2:nuxt.config.tsでwebpack設定を引き継いでいる
Nuxt2の「nuxt.config.js」をそのまま「.ts」にリネームしただけのケース。
// ❌ Nuxt2時代の設定が残っている
export default defineNuxtConfig({
build: {
extend(config, ctx) {
// vue-loaderの設定を触ろうとしている
const vueLoader = config.module.rules.find(
rule => rule.loader === 'vue-loader'
)
// Viteモードではこの設定自体が無意味
}
}
})
パターン3:カスタムwebpackプラグインがvue-loaderに依存
独自のwebpackプラグインやローダーを使っている場合。
// ❌ modules/custom-loader.js
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = function() {
this.extendBuild((config) => {
config.plugins.push(new VueLoaderPlugin())
})
}
私も最初の案件で、社内共通の「@company/nuxt-custom-module」がvue-loaderに依存していて、ここで2日詰まりました。
パターン4:Nuxt Bridgeからの段階移行で設定が混在
Nuxt Bridgeを経由して段階的に移行した場合の設定の混在。
// ❌ Bridge設定とNuxt3設定が混ざっている
export default defineNuxtConfig({
bridge: {
vite: true, // Bridge時代の名残
},
vite: {
// Nuxt3の正式な設定
}
})
パターン5:monorepo構成でworkspace依存関係が壊れている
yarn workspacesやpnpm workspaces環境での依存解決の問題。
packages/
├── admin/ (Nuxt3)
├── front/ (Nuxt3)
└── shared/ (共通コンポーネント、vue-loaderへの依存が残っている)
4. After:基本的な解決パターン
診断フローチャート
vue-loaderエラー発生
↓
[1] package.jsonにvue-loader/webpack関連が残っているか?
YES → パターン1の解決へ
NO ↓
[2] nuxt.config.tsでwebpack.extend使っているか?
YES → パターン2の解決へ
NO ↓
[3] modulesディレクトリに独自拡張があるか?
YES → パターン3の解決へ
NO ↓
[4] Bridge経由で移行したか?
YES → パターン4の解決へ
NO ↓
[5] monorepo構成か?
YES → パターン5の解決へ
パターン1の解決:依存関係のクリーンアップ
# 1. 不要な依存を削除
npm uninstall vue-loader webpack webpack-cli
# 2. Nuxt2時代のwebpack関連も全削除
npm uninstall @nuxt/webpack \
webpack-bundle-analyzer \
webpackbar \
sass-loader \
node-sass
# 3. node_modulesとロックファイルをクリーン
rm -rf node_modules package-lock.json
npm install
削除すべき依存関係チェックリスト:
{
"devDependencies": {
// ❌ これら全て削除
"vue-loader": "*",
"vue-template-compiler": "*",
"@nuxt/webpack": "*",
"webpack": "*",
"webpack-cli": "*",
"webpack-dev-middleware": "*"
}
}
パターン2の解決:nuxt.config.tsの書き換え
// ❌ Before(Nuxt2風の設定)
export default defineNuxtConfig({
build: {
extend(config, { isClient, isDev }) {
if (isDev && isClient) {
config.module.rules.push({
enforce: 'pre',
test: /\.(js|vue)$/,
loader: 'eslint-loader',
exclude: /(node_modules)/
})
}
}
}
})
// ✅ After(Nuxt3 + Vite)
export default defineNuxtConfig({
vite: {
plugins: [
// ESLintはViteプラグインで
// npm install vite-plugin-eslint --save-dev
eslintPlugin({
include: ['src/**/*.vue', 'src/**/*.ts']
})
]
}
})
Sass/SCSS使う場合:
// Nuxt3ではシンプルにこれだけ
export default defineNuxtConfig({
css: ['~/assets/scss/main.scss'],
vite: {
css: {
preprocessorOptions: {
scss: {
additionalData: '@import "@/assets/scss/_variables.scss";'
}
}
}
}
})