Nuxt 4ã®æ°æ©èœã培åºè§£èª¬ïŒãã£ã¬ã¯ããªæ§é ã®å·æ°ãšNuxt 3ããã®ç§»è¡ã¬ã€ãïŒ2026幎çïŒ
Nuxt 4ã§å°å ¥ãããapp/ãã£ã¬ã¯ããªæ§é ãã·ã³ã°ã«ãã³ããŒã¿ãã§ããå±€ãshallow reactivityãTypeScriptåå²ã³ã³ããã¹ãã«ã€ããŠãã³ãŒãäŸã亀ããŠè©³ãã解説ããŸãã

Nuxt 4ã¯ãã¢ããªã±ãŒã·ã§ã³ã³ãŒããšèšå®ãã¡ã€ã«ãæç¢ºã«åé¢ããæ°ãããã£ã¬ã¯ããªæ§é ãå°å ¥ããŸããã2025幎7æã«ãªãªãŒã¹ããã2026幎4ææç¹ã§ããŒãžã§ã³4.4ã«å°éãããã®ã¡ãžã£ãŒã¢ããããŒãã¯ãã·ã³ã°ã«ãã³ããŒã¿ãã§ããå±€ãshallowReactivityã®ããã©ã«ãåãTypeScriptã³ã³ããã¹ãã®åå²ãªã©ãéçºäœéšãå€§å¹ ã«æ¹åããæ©èœãåããŠããŸããNuxt 2ããNuxt 3ãžã®ç§»è¡ãšæ¯èŒãããšãä»åã®ã¢ããã°ã¬ãŒããã¹ã¯æ Œæ®µã«ã¹ã ãŒãºã§ãã
NuxtããŒã ã¯Codemodãšé£æºããã»ãšãã©ã®ç§»è¡ã¹ããããèªååããŠããŸããnpx codemod@latest nuxt/4/migration-recipe ãå®è¡ãããšããã£ã¬ã¯ããªåæ§ç¯ãããŒã¿ãã§ããã®æŽæ°ãéæšå¥šAPIã®çœ®æãèªåçã«åŠçãããŸãã
Nuxt 4ã®app/ãã£ã¬ã¯ããªæ§é
Nuxt 4ã§ã¯ããã¹ãŠã®ã¢ããªã±ãŒã·ã§ã³ãœãŒã¹ã³ãŒããããã©ã«ãã§app/ãã£ã¬ã¯ããªã«é
眮ãããŸãããã®å€æŽã¯å®éã®éçºäžã®èª²é¡ã解決ãããã®ã§ããLinuxãWindowsã®ãã¡ã€ã«ãŠã©ããã£ãŒã¯ãã¢ããªã±ãŒã·ã§ã³ã³ãŒããnode_modules/ã.git/ãèšå®ãã¡ã€ã«ãšæ··åšãããå°çšã®ãµããã£ã¬ã¯ããªã«æ ŒçŽãããŠããå Žåã倧å¹
ã«ããã©ãŒãã³ã¹ãåäžããŸãã
æ°ãããã£ã¬ã¯ããªã¬ã€ã¢ãŠãã¯ä»¥äžã®éãã§ãã
my-nuxt-app/
ââ app/
â ââ assets/
â ââ components/
â ââ composables/
â ââ layouts/
â ââ middleware/
â ââ pages/
â ââ plugins/
â ââ utils/
â ââ app.vue
â ââ app.config.ts
â ââ error.vue
ââ content/
ââ public/
ââ shared/ # æ°èŠïŒappãšserveréã§å
±æããã³ãŒã
ââ server/
ââ nuxt.config.tsshared/ãã£ã¬ã¯ããªã¯ç¹çãã¹ã远å èŠçŽ ã§ããshared/ã«é
眮ãããcomposableããŠãŒãã£ãªãã£ã¯ãVueã¢ããªãšNitroãµãŒããŒã®äž¡æ¹ã§èªåã€ã³ããŒããããŸããããªããŒã·ã§ã³ã¹ããŒããåå®çŸ©ããŠãŒãã£ãªãã£é¢æ°ãã³ã³ããã¹ãéã§å
±æããéã«ãæåã®importæãèšè¿°ããå¿
èŠããªããªããŸãã
Nuxt 3ããNuxt 4ãžã®ã¹ããããã€ã¹ãããç§»è¡
ã¢ããã°ã¬ãŒãããã»ã¹ã¯1ã€ã®ã³ãã³ãããå§ãŸããŸããNuxtã¯æ¢åã®ãã©ããæ§é ãæ€åºãã倿Žãªãã§åäœãç¶ç¶ãããããæ®µéçãªç§»è¡ãå¯èœã§ãã
# Nuxtã®ã¢ããã°ã¬ãŒããšäŸåé¢ä¿ã®éè€è§£æ¶
npx nuxt upgrade --dedupeããã±ãŒãžã®ã¢ããã°ã¬ãŒãåŸãã¢ããªã±ãŒã·ã§ã³ãã¡ã€ã«ãapp/ãã£ã¬ã¯ããªã«ç§»åããŸãã
# ãã£ã¬ã¯ããªåæ§ç¯ã®èªåå
npx codemod@latest nuxt/4/file-structureãã®codemodã«ãããassets/ãcomponents/ãcomposables/ãlayouts/ãmiddleware/ãpages/ãplugins/ãutils/ãapp.vueãerror.vueãapp.config.tsãapp/ã«ç§»åãããŸããã«ãŒãã«æ®ãã¹ããã¡ã€ã«ïŒnuxt.config.tsãserver/ãpublic/ãcontent/ïŒã¯ãã®ãŸãŸç¶æãããŸãã
ãã£ã¬ã¯ããªåæ§ç¯ãããã«è¡ããªãå Žåã¯ããœãŒã¹ãã£ã¬ã¯ããªãæç€ºçã«èšå®ã§ããŸãã
export default defineNuxtConfig({
srcDir: '.',
dir: { app: 'app' },
})ãã®èšå®ã«ãããNuxt 4ã¯ãããžã§ã¯ãã«ãŒããããã¡ã€ã«ã解決ããNuxt 3ãšåãåäœãç¶æããŸãã
ã·ã³ã°ã«ãã³ããŒã¿ãã§ããå±€ãšãªã¢ã¯ãã£ãããŒ
Nuxt 4ã§ã¯ãuseAsyncDataãšuseFetchã®ããŒã¿ç®¡çæ¹æ³ãæ ¹æ¬çã«å€æŽãããŸãããåãããŒã䜿çšããè€æ°ã®ã³ã³ããŒãã³ãã¯ãç¬ç«ããã³ããŒãä¿æãã代ããã«ãåäžã®ãªã¢ã¯ãã£ãåç
§ãå
±æããããã«ãªããŸãã
export function useProductData(productId: string) {
return useAsyncData(
`product-${productId}`,
() => $fetch(`/api/products/${productId}`),
{
getCachedData: (key, nuxtApp, ctx) => {
// ctx.causeã§ãã§ããã®çç±ãå€å¥å¯èœ
if (ctx.cause === 'refresh:manual') return undefined
return nuxtApp.payload.data[key]
},
},
)
}ãã®æ°ããããŒã¿å±€ã«ã¯3ã€ã®éèŠãªå€æŽç¹ããããŸãã
- å
±æref: 2ã€ã®ã³ã³ããŒãã³ãã§
useProductData('abc')ãåŒã³åºããšãåãdataãerrorãstatusã®refãè¿ãããŸããäžæ¹ãæŽæ°ãããšãããäžæ¹ã«ãå³åº§ã«åæ ãããŸãã - èªåã¯ãªãŒã³ã¢ãã: ããããŒã䜿çšããæåŸã®ã³ã³ããŒãã³ããã¢ã³ããŠã³ãããããšãé¢é£ããŒã¿ãã¡ã¢ãªããè§£æŸãããŸãã
- ãªã¢ã¯ãã£ãããŒ: ããŒãcomputedãŸãã¯refã§ã©ãããããšãå€ã®å€æŽæã«èªåçã«ããŒã¿ãåãã§ãããããŸãã
getCachedDataã³ãŒã«ããã¯ã¯ãcauseããããã£ïŒ'initial'ã'refresh:hook'ã'refresh:manual'ã'watch'ïŒãæã€ã³ã³ããã¹ããªããžã§ã¯ããåãåãããã«ãªãããã£ãã·ã¥ããŒã¿ã®æäŸãšæ°èŠãã§ããã®äœ¿ãåãããã现ããå¶åŸ¡ã§ããŸãã
useAsyncData/useFetchã®dataãšerrorã®ããã©ã«ãå€ãnullããundefinedã«å€æŽãããŸããã=== nullãã§ãã¯ã=== undefinedã«æŽæ°ããããç·©ãç䟡æŒç®åã䜿çšããå¿
èŠããããŸãã
Shallow Reactivityã«ããããã©ãŒãã³ã¹æé©å
Nuxt 4ã§ã¯ãuseAsyncDataãšuseFetchã®dataãrefã§ã¯ãªãshallowRefãããã©ã«ãã§äœ¿çšããããã«ãªããŸãããVueã¯ãã¹ããããããããã£ãååž°çã«è¿œè·¡ããªããªããããæ·±ããã¹ãããããªããžã§ã¯ããå€§èŠæš¡ãªé
åãå«ãAPIã¬ã¹ãã³ã¹ã«å¯ŸããŠãèšæž¬å¯èœãªããã©ãŒãã³ã¹æ¹åãåŸãããŸãã
<script setup lang="ts">
// dataã¯ããã©ã«ãã§shallowRefã«ãªã
const { data: metrics } = await useFetch('/api/dashboard/metrics')
// çŽæ¥çãªããããã£å€æŽã¯ãªã¢ã¯ãã£ããã£ãããªã¬ãŒããªã
// metrics.value.visits = 100 // åã¬ã³ããªã³ã°ãçºçããªã
// å€å
šäœã眮æãããšæŽæ°ãããªã¬ãŒããã
metrics.value = { ...metrics.value, visits: 100 }
// ç¹å®ã®åŒã³åºãã§ãã£ãŒããªã¢ã¯ãã£ããã£ãæå¹å
const { data: settings } = await useFetch('/api/settings', {
deep: true,
})
</script>ããã·ã¥ããŒããååäžèЧãèšäºããŒãžãªã©ãã»ãšãã©ã®èªã¿åãå°çšããŒã¿è¡šç€ºã§ã¯ãshallow reactivityã¯ã³ãŒã倿Žãªãã§åäœããŸãããã©ãŒã ããã¹ããããããããã£ãçŽæ¥å€æŽããã€ã³ã¿ã©ã¯ãã£ãUIã«ã¯ãdeep: trueãªãã·ã§ã³ãåŒãç¶ãå©çšã§ããŸãã
Vue.js / Nuxt.jsã®é¢æ¥å¯Ÿçã¯ã§ããŠããŸããïŒ
ã€ã³ã¿ã©ã¯ãã£ããªã·ãã¥ã¬ãŒã¿ãŒãflashcardsãæè¡ãã¹ãã§ç·Žç¿ããŸãããã
TypeScriptã³ã³ããã¹ãåå²ãšåå®å šæ§ã®åŒ·å
Nuxt 4ã§ã¯ããããžã§ã¯ãå ã®åã³ã³ããã¹ãã«å¯ŸããŠåå¥ã®TypeScriptèšå®ãçæãããŸãã
.nuxt/tsconfig.app.jsonâ Vueã¢ããªã±ãŒã·ã§ã³ã³ãŒãçš.nuxt/tsconfig.server.jsonâ NitroãµãŒããŒã³ãŒãçš.nuxt/tsconfig.shared.jsonâ å ±æãŠãŒãã£ãªãã£çš.nuxt/tsconfig.node.jsonâ ãã«ãæèšå®çš
ãã®åé¢ã«ãããIDEãã¯ã©ã€ã¢ã³ãã³ãŒãå
ã§ãµãŒããŒå°çšAPIãææ¡ãããããã®éãèµ·ãããããããšããªããªããŸãããããžã§ã¯ãã«ãŒãã®tsconfig.jsonã¯ã4ã€ãã¹ãŠã®èšå®ãåç
§ããæ§æã«ãªããŸãã
{
"files": [],
"references": [
{ "path": "./.nuxt/tsconfig.app.json" },
{ "path": "./.nuxt/tsconfig.server.json" },
{ "path": "./.nuxt/tsconfig.shared.json" },
{ "path": "./.nuxt/tsconfig.node.json" }
]
}CIã§ã®åãã§ãã¯ã倿ŽããããŸããvue-tscã³ãã³ãã¯ããããžã§ã¯ãåç
§ãæ£ããåŠçããããã«-bãã©ã°ïŒãã«ãã¢ãŒãïŒãå¿
èŠã«ãªããŸãã
# 倿ŽåïŒNuxt 3ïŒ
nuxt prepare && vue-tsc --noEmit
# 倿ŽåŸïŒNuxt 4ïŒ
nuxt prepare && vue-tsc -b --noEmitãã1ã€ã®TypeScriptã®å€æŽç¹ãšããŠãcompilerOptions.noUncheckedIndexedAccessãããã©ã«ãã§trueã«èšå®ãããããã«ãªããŸãããé
åèŠçŽ ããªããžã§ã¯ãããããã£ãžã®ã€ã³ããã¯ã¹ã¢ã¯ã»ã¹ã¯T | undefinedãè¿ãããã«ãªããã³ã³ãã€ã«æã«æœåšçãªã©ã³ã¿ã€ã ãšã©ãŒãæ€åºã§ããŸãã
ã³ã³ããŒãã³ãåã®æ£èŠåãšVue Router v5
Nuxt 4.3ã§ã¯Vue Router v5ã«ã¢ããã°ã¬ãŒããããunplugin-vue-routerãžã®äŸåãè§£æ¶ãããŸãããã»ãšãã©ã®ã¢ããªã±ãŒã·ã§ã³ã§ã¯ããã®ã¢ããã°ã¬ãŒãã¯ééçã«è¡ãããŸãã
ã³ã³ããŒãã³ãã®åœåèŠåãæšæºåãããŸãããcomponents/dashboard/MetricsCard.vueã«ããã³ã³ããŒãã³ãã¯ã<KeepAlive>ãVue DevToolsããã¹ããŠãŒãã£ãªãã£ãå«ããã¹ãŠã®ã³ã³ããã¹ãã§äžè²«ããŠDashboardMetricsCardãšããååãä»ããããŸãã
<!-- app/pages/dashboard.vue -->
<template>
<NuxtPage :keepalive="{
include: ['DashboardMetricsCard', 'DashboardRecentActivity'],
}" />
</template>ã³ã³ããŒãã³ãåãã£ã«ã¿ãŒã䜿çšãã<KeepAlive>ãå©çšããŠãããããžã§ã¯ãã§ã¯ããã®æ°ããåœåèŠåã«åãããæŽæ°ãå¿
èŠã§ããã³ã³ããã¹ãã«ãã£ãŠååãç°ãªã以åã®åäœã¯é©çšãããªããªããŸããã
Head管çã«ãããç Žå£ç倿Žãžã®å¯Ÿå¿
Nuxt 4ã«ã¯Unhead v2ã忢±ãããŠãããuseHeadãšuseSeoMetaããããã€ãã®éæšå¥šããããã£ãåé€ãããŠããŸãã
<script setup lang="ts">
const route = useRoute()
const { data: product } = await useFetch(`/api/products/${route.params.id}`)
// Unhead v2ïŒvmidãhidãchildrenãbodyããããã£ãåé€
useSeoMeta({
title: () => product.value?.name ?? 'Product',
ogTitle: () => product.value?.name ?? 'Product',
description: () => product.value?.description ?? '',
ogImage: () => product.value?.imageUrl ?? '',
})
</script>ãã³ãã¬ãŒããã©ã¡ãŒã¿ããšã€ãªã¢ã¹ãœãŒãã«äŸåããŠãããããžã§ã¯ãã§ã¯ãããããæç€ºçãªãã©ã°ã€ã³ãšããŠã€ã³ã¹ããŒã«ããå¿ èŠããããŸãã
import { TemplateParamsPlugin, AliasSortingPlugin } from '@unhead/vue/plugins'
export default defineNuxtPlugin({
setup() {
const unhead = injectHead()
unhead.use(TemplateParamsPlugin)
unhead.use(AliasSortingPlugin)
},
})Nuxt 3ã¯2026幎7æ31æ¥ãŸã§ã»ãã¥ãªãã£ã¢ããããŒããšéèŠãªãã°ä¿®æ£ãåãç¶ããŸãããã®æ¥ä»¥éãNuxt 3ã¯ãµããŒã察象å€ãšãªããŸããEOLãã¬ãŒã ã¯ãŒã¯äžã§æ¬çªã¢ããªã±ãŒã·ã§ã³ãéçšããããšãé¿ãããããä»ã®ãã¡ã«ç§»è¡èšç»ãç«ãŠãããšãæšå¥šãããŸãã
ç§»è¡ãã§ãã¯ãªã¹ããšæ³šæãã¹ããã€ã³ã
èªåcodemodãã»ãšãã©ã®å€æŽãåŠçããŸãããæå察å¿ãå¿ èŠãªé ç®ããããŸãã
window.__NUXT__ã®åé€:useNuxtApp().payloadã«çœ®ãæããå¿ èŠã§ãããã®ã°ããŒãã«ãªããžã§ã¯ãã¯Nuxt 4ã§ã¯ãã€ãã¬ãŒã·ã§ã³åŸã«åé€ãããŸããpages:extendããã¯: ããŒãžã¡ã¿ã¹ãã£ã³åŸã«å®è¡ãããæ°ããpages:resolvedããã¯ã«åãæ¿ããå¿ èŠã§ããdedupeããŒãªã¢ã³:refresh({ dedupe: true })ãrefresh({ dedupe: 'cancel' })ã«ãfalseã'defer'ã«çœ®ãæããŸãã- ã€ã³ã©ã€ã³ã¹ã¿ã€ã«: ããã©ã«ãã§ã¯Vueã³ã³ããŒãã³ãã¹ã¿ã€ã«ã®ã¿ãã€ã³ã©ã€ã³åãããã°ããŒãã«CSSã¯å¥ãã¡ã€ã«ãšããŠèªã¿èŸŒãŸããŸããNuxt 3ã®åäœã«æ»ãã«ã¯
features: { inlineStyles: true }ã远å ããŸãã clearNuxtState:undefinedã§ã¯ãªãåæå€ã«ãªã»ãããããããã«ãªããŸããã以åã®åäœãå¿ èŠãªå Žåã¯clearNuxtState('key', { reset: false })ã䜿çšããŸãã
æ¬çªã¢ããªã±ãŒã·ã§ã³åãã®å®è·µçãªç§»è¡æé ã¯ä»¥äžã®éãã§ãã
npx nuxt upgrade --dedupeãå®è¡ããã¢ããªã±ãŒã·ã§ã³ã®ãã«ãã確èª- codemodãå®è¡:
npx codemod@latest nuxt/4/migration-recipe - ãã¡ã€ã«ã
app/ã«ç§»åïŒfile-structure codemodã«ããèªååïŒ - ããŒã¿ãã§ããããžãã¯ã®
nullãã§ãã¯ãundefinedã«æŽæ° - æ£èŠåãããååã§
<KeepAlive>ã³ã³ããŒãã³ãããã¹ã - CIã®åãã§ãã¯ã
vue-tsc -b --noEmitã«æŽæ° - ãã¹ãã¹ã€ãŒãå
šäœãå®è¡ãã
noUncheckedIndexedAccessã«ãã£ãŠæ€åºãããTypeScriptãšã©ãŒãä¿®æ£
VueãšNuxtã®æŠå¿µãããæ·±ãçè§£ããã«ã¯ãSharpSkillã®Vue/Nuxt颿¥å¯Ÿçåé¡ãåç §ããããNuxt 4ã«ãåŒãç¶ãããã¬ã³ããªã³ã°æŠç¥ã®èæ¯ãšããŠSSRãšéççæã¬ã€ãã確èªããŠãã ããã
ãŸãšã
- Nuxt 4.4ïŒ2026幎4ææç¹ã®ææ°çïŒã¯ã
app/ãã£ã¬ã¯ããªæ§é ãã·ã³ã°ã«ãã³ããŒã¿ãã§ãããåå²TypeScriptã³ã³ããã¹ããæ¬çªç°å¢å¯Ÿå¿ã®ããã©ã«ããšããŠç¢ºç«ããŠããŸã npx codemod@latest nuxt/4/migration-recipeã³ãã³ãã«ããããã£ã¬ã¯ããªåæ§ç¯ãéæšå¥šAPIã®çœ®æãããŒã¿ãã§ããã®æŽæ°ãèªååãããŸãshallowRefã«ããshallow reactivityã¯ãã»ãšãã©ã®ã±ãŒã¹ã§ã³ãŒã倿ŽäžèŠã®ãŸãŸããã©ãŒãã³ã¹ãæ¹åããŸã- ã³ã³ããã¹ãããšã®åå¥TypeScriptèšå®ïŒappãserverãsharedãnodeïŒã«ãããã³ã³ããã¹ãéã®åãªãŒã¯ãè§£æ¶ãããIDEã®è£å®ç²ŸåºŠãåäžããŸã
- Nuxt 3ã¯2026幎7æ31æ¥ã«ãµããŒãçµäºãè¿ããããããã以åã®ç§»è¡ããµããŒã察象ããŒãžã§ã³ã®ç¶æã«å¿ èŠã§ã
- Vue Router v5ãšUnhead v2ã¯ããæŽçãããAPIãæäŸããŸãããéæšå¥šããããã£ã®åé€ã«äŒŽãç§»è¡æã®ç¢ºèªãæ±ããããŸã
ä»ããç·Žç¿ãå§ããŸãããïŒ
颿¥ã·ãã¥ã¬ãŒã¿ãŒãšæè¡ãã¹ãã§ç¥èããã¹ãããŸãããã
ã¿ã°
å ±æ
é¢é£èšäº

2026幎ã®Vue 3ãšTypeScript: åå®å šãªPropsã»Emitsã»ã³ã³ããŒã¶ãã«
TypeScriptã§åå®å šãªVue 3ã³ã³ããŒãã³ããç¿åŸ: ãžã§ããªãã¯definePropsãã¿ãã«åœ¢åŒã®defineEmitsãåä»ãã³ã³ããŒã¶ãã«ãdefineModelãInjectionKeyã颿¥è³ªåãŸã§è§£èª¬ã

2026幎ã®Vue 3ãã¹ãå ¥éïŒVitestãVue Test UtilsããããŠé¢æ¥è³ªå
2026幎ã®Vueãã¹ããå®è·µçã«è§£èª¬ããŸããVitestã®èšå®ãVue Test Utilsã«ããã³ã³ããŒãã³ãã®ããŠã³ããã³ã³ããŒã¶ãã«ãPiniaã¹ãã¢ã®ãã¹ããAPIã®ã¢ãã¯ãã«ãã¬ããžèšæž¬ããããŠæ¡çšããŒã ãå®éã«å°ãã颿¥è³ªåãŸã§ãç¶²çŸ ããŸãã

Vue 3 ã³ã³ããŒã¶ãã«äžçŽã¬ã€ãïŒåå©çšå¯èœãªãã¿ãŒã³ãšæè¡é¢æ¥ã®è³ªå 2026
Vue 3ã®é«åºŠãªã³ã³ããŒã¶ãã«ãã¿ãŒã³ãç¶²çŸ çã«è§£èª¬ãéåæåŠçãäŸåæ§æ³šå ¥ããã©ãŒã ããªããŒã·ã§ã³ããã¹ãææ³ã2026å¹Žã®æè¡é¢æ¥ã§é »åºãã質åãšåçã玹ä»ããŸãã