mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 05:35:13 +00:00
docs: add migration/testing guide for nuxt 4 (#27128)
Co-authored-by: Damian Głowala <damian.glowala.rebkow@gmail.com>
This commit is contained in:
parent
6cc65194c2
commit
76925a4477
@ -5,11 +5,11 @@ navigation.icon: i-ph-arrow-circle-up-duotone
|
|||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
## Upgrading Nuxt 3
|
## Upgrading Nuxt
|
||||||
|
|
||||||
### Latest release
|
### Latest release
|
||||||
|
|
||||||
To upgrade Nuxt 3 to the [latest release](https://github.com/nuxt/nuxt/releases), use the `nuxi upgrade` command.
|
To upgrade Nuxt to the [latest release](https://github.com/nuxt/nuxt/releases), use the `nuxi upgrade` command.
|
||||||
|
|
||||||
```bash [Terminal]
|
```bash [Terminal]
|
||||||
npx nuxi upgrade
|
npx nuxi upgrade
|
||||||
@ -17,7 +17,253 @@ npx nuxi upgrade
|
|||||||
|
|
||||||
### Nightly Release Channel
|
### Nightly Release Channel
|
||||||
|
|
||||||
To use the latest Nuxt 3 build and test features before their release, read about the [nightly release channel](/docs/guide/going-further/nightly-release-channel) guide.
|
To use the latest Nuxt build and test features before their release, read about the [nightly release channel](/docs/guide/going-further/nightly-release-channel) guide.
|
||||||
|
|
||||||
|
## Testing Nuxt 4
|
||||||
|
|
||||||
|
Nuxt 4 is planned to be released **on or before June 14** (though obviously this is dependent on having enough time after Nitro's major release to be properly tested in the community, so be aware that this is not an exact date).
|
||||||
|
|
||||||
|
Until then, it is possible to test many of Nuxt 4's breaking changes on the nightly release channel.
|
||||||
|
|
||||||
|
### Opting in to Nuxt 4
|
||||||
|
|
||||||
|
First, opt in to the nightly release channel [following these steps](/docs/guide/going-further/nightly-release-channel#opting-in).
|
||||||
|
|
||||||
|
Then you can set your `compatibilityVersion` to match Nuxt 4 behavior:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export default defineNuxtConfig({
|
||||||
|
future: {
|
||||||
|
compatibilityVersion: 4,
|
||||||
|
},
|
||||||
|
// To re-enable _all_ Nuxt v3 behavior, set the following options:
|
||||||
|
// srcDir: '.',
|
||||||
|
// dir: {
|
||||||
|
// app: 'app'
|
||||||
|
// },
|
||||||
|
// experimental: {
|
||||||
|
// compileTemplate: true,
|
||||||
|
// templateUtils: true,
|
||||||
|
// relativeWatchPaths: true,
|
||||||
|
// defaults: {
|
||||||
|
// useAsyncData: {
|
||||||
|
// deep: true
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// unhead: {
|
||||||
|
// renderSSRHeadOptions: {
|
||||||
|
// omitLineBreaks: false
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
When you set your `compatibilityVersion` to `4`, defaults throughout your Nuxt configuration will change to opt in to Nuxt v4 behavior, but you can granularly re-enable Nuxt v3 behavior when testing, following the commented out lines above. Please file issues if so, so that we can address them in Nuxt or in the ecosystem.
|
||||||
|
|
||||||
|
### Migrating to Nuxt 4
|
||||||
|
|
||||||
|
Breaking or significant changes will be noted here along with migration steps for backward/forward compatibility.
|
||||||
|
|
||||||
|
::alert
|
||||||
|
This section is subject to change until the final release, so please check back here regularly if you are testing Nuxt 4 using `compatibilityVersion: 4`.
|
||||||
|
::
|
||||||
|
|
||||||
|
#### New Directory Structure
|
||||||
|
|
||||||
|
🚦 **Impact Level**: Significant
|
||||||
|
|
||||||
|
Nuxt now defaults to a new directory structure, with backwards compatibility (so if Nuxt detects you are using the old structure, such as with a top-level `pages/` directory, this new structure will not apply).
|
||||||
|
|
||||||
|
👉 [See full RFC](https://github.com/nuxt/nuxt/issues/26444)
|
||||||
|
|
||||||
|
##### What Changed
|
||||||
|
|
||||||
|
* the new Nuxt default `srcDir` is `app/` by default, and most things are resolved from there.
|
||||||
|
* `serverDir` now defaults to `<rootDir>/server` rather than `<srcDir>/server`
|
||||||
|
* `modules` and `public` are resolved relative to `<rootDir>` by default
|
||||||
|
* a new `dir.app` is added, which is the directory we look for `router.options.ts` and `spa-loading-template.html` - this defaults to `<srcDir>/`
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
<summary>An example v4 folder structure.</summary>
|
||||||
|
|
||||||
|
```sh
|
||||||
|
.output/
|
||||||
|
.nuxt/
|
||||||
|
app/
|
||||||
|
assets/
|
||||||
|
components/
|
||||||
|
composables/
|
||||||
|
layouts/
|
||||||
|
middleware/
|
||||||
|
pages/
|
||||||
|
plugins/
|
||||||
|
utils/
|
||||||
|
app.vue
|
||||||
|
router.options.ts
|
||||||
|
modules/
|
||||||
|
node_modules/
|
||||||
|
public/
|
||||||
|
server/
|
||||||
|
api/
|
||||||
|
middleware/
|
||||||
|
plugins/
|
||||||
|
routes/
|
||||||
|
utils/
|
||||||
|
nuxt.config.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
👉 For more details, see the [PR implementing this change](https://github.com/nuxt/nuxt/pull/27029).
|
||||||
|
|
||||||
|
##### Reasons for Change
|
||||||
|
|
||||||
|
1. **Performance** - placing all your code in the root of your repo causes issues with `.git/` and `node_modules/` folders being scanned/included by FS watchers which can significantly delay startup on non-Mac OSes.
|
||||||
|
1. **IDE type-safety** - `server/` and the rest of your app are running in two entirely different contexts with different global imports available, and making sure `server/` isn't _inside_ the same folder as the rest of your app is a big first step to ensuring you get good auto-completes in your IDE.
|
||||||
|
|
||||||
|
##### Migration Steps
|
||||||
|
|
||||||
|
1. Create a new directory called `app/`.
|
||||||
|
1. Move your `assets/`, `components/`, `composables/`, `layouts/`, `middleware/`, `pages/`, `plugins/` and `utils/` folders under it, as well as `app.vue`, `error.vue`. If you have an `app/router-options.ts` or `app/spa-loading-template.html`, these paths remain the same.
|
||||||
|
1. Make sure your `nuxt.config.ts`, `modules/`, `public/` and `server/` folders remain outside the `app/` folder, in the root of your project.
|
||||||
|
|
||||||
|
However, migration is _not required_. If you wish to keep your current folder structure, Nuxt should auto-detect it. (If it does not, please raise an issue.) You can also force a v3 folder structure with the following configuration:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export default defineNuxtConfig({
|
||||||
|
// This reverts the new srcDir default from `app` back to your root directory
|
||||||
|
srcDir: '.',
|
||||||
|
// This specifies the directory prefix for `app/router.options.ts` and `app/spa-loading-template.html`
|
||||||
|
dir: {
|
||||||
|
app: 'app'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Shallow Data Reactivity in `useAsyncData` and `useFetch`
|
||||||
|
|
||||||
|
🚦 **Impact Level**: Minimal
|
||||||
|
|
||||||
|
The `data` object returned from `useAsyncData`, `useFetch`, `useLazyAsyncData` and `useLazyFetch` is now a `shallowRef` rather than a `ref`.
|
||||||
|
|
||||||
|
##### What Changed
|
||||||
|
|
||||||
|
When new data is fetched, anything depending on `data` will still be reactive because the entire object is replaced. But if your code changes a property _within_ that data structure, this will not trigger any reactivity in your app.
|
||||||
|
|
||||||
|
##### Reasons for Change
|
||||||
|
|
||||||
|
This brings a **significant** performance improvement for deeply nested objects and arrays because Vue does not need to watch every single property/array for modification. In most cases, `data` should also be immutable.
|
||||||
|
|
||||||
|
##### Migration Steps
|
||||||
|
|
||||||
|
In most cases, no migration steps are required, but if you rely on the reactivity of the data object then you have two options:
|
||||||
|
|
||||||
|
1. You can granularly opt in to deep reactivity on a per-composable basis:
|
||||||
|
```diff
|
||||||
|
- const { data } = useFetch('/api/test')
|
||||||
|
+ const { data } = useFetch('/api/test', { deep: true })
|
||||||
|
```
|
||||||
|
1. You can change the default behavior on a project-wide basis (not recommended):
|
||||||
|
```ts
|
||||||
|
export default defineNuxtConfig({
|
||||||
|
experimental: {
|
||||||
|
defaults: {
|
||||||
|
useAsyncData: {
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Absolute Watch Paths in `builder:watch`
|
||||||
|
|
||||||
|
🚦 **Impact Level**: Minimal
|
||||||
|
|
||||||
|
##### What Changed
|
||||||
|
|
||||||
|
The Nuxt `builder:watch` hook now emits a path which is absolute rather than relative to your project `srcDir`.
|
||||||
|
|
||||||
|
##### Reasons for Change
|
||||||
|
|
||||||
|
This allows us to support watching paths which are outside your `srcDir`, and offers better support for layers and other more complex patterns.
|
||||||
|
|
||||||
|
##### Migration Steps
|
||||||
|
|
||||||
|
We have already proactively migrated the public Nuxt modules which we are aware use this hook. See [issue #25339](https://github.com/nuxt/nuxt/issues/25339).
|
||||||
|
|
||||||
|
However, if you are a module author using the `builder:watch` hook and wishing to remain backwards/forwards compatible, you can use the following code to ensure that your code works the same in both Nuxt v3 and Nuxt v4:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
+ import { relative, resolve } from 'node:fs'
|
||||||
|
// ...
|
||||||
|
nuxt.hook('builder:watch', async (event, path) => {
|
||||||
|
+ path = relative(nuxt.options.srcDir, resolve(nuxt.options.srcDir, path))
|
||||||
|
// ...
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Template Compilation Changes
|
||||||
|
|
||||||
|
🚦 **Impact Level**: Minimal
|
||||||
|
|
||||||
|
##### What Changed
|
||||||
|
|
||||||
|
Previously, Nuxt used `lodash/template` to compile templates located on the file system using the `.ejs` file format/syntax.
|
||||||
|
|
||||||
|
In addition, we provided some template utilities (`serialize`, `importName`, `importSources`) which could be used for code-generation within these templates, which are now being removed.
|
||||||
|
|
||||||
|
##### Reasons for Change
|
||||||
|
|
||||||
|
In Nuxt v3 we moved to a 'virtual' syntax with a `getContents()` function which is much more flexible and performant.
|
||||||
|
|
||||||
|
In addition, `lodash/template` has had a succession of security issues. These do not really apply to Nuxt projects because it is being used at build-time, not runtime, and by trusted code. However, they still appear in security audits. Moreover, `lodash` is a hefty dependency and is unused by most projects.
|
||||||
|
|
||||||
|
Finally, providing code serialization functions directly within Nuxt is not ideal. Instead, we maintain projects like [unjs/knitwork](http://github.com/unjs/knitwork) which can be dependencies of your project, and where security issues can be reported/resolved directly without requiring an upgrade of Nuxt itself.
|
||||||
|
|
||||||
|
##### Migration Steps
|
||||||
|
|
||||||
|
We have raised PRs to update modules using EJS syntax, but if you need to do this yourself, you have three backwards/forwards-compatible alternatives:
|
||||||
|
|
||||||
|
* Moving your string interpolation logic directly into `getContents()`.
|
||||||
|
* Using a custom function to handle the replacement, such as in https://github.com/nuxt-modules/color-mode/pull/240.
|
||||||
|
* Continuing to use `lodash`, as a dependency of _your_ project rather than Nuxt:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
+ import { readFileSync } from 'node:fs'
|
||||||
|
+ import { template } from 'lodash-es'
|
||||||
|
// ...
|
||||||
|
addTemplate({
|
||||||
|
fileName: 'appinsights-vue.js'
|
||||||
|
options: { /* some options */ },
|
||||||
|
- src: resolver.resolve('./runtime/plugin.ejs'),
|
||||||
|
+ getContents({ options }) {
|
||||||
|
+ const contents = readFileSync(resolver.resolve('./runtime/plugin.ejs'), 'utf-8')
|
||||||
|
+ return template(contents)({ options })
|
||||||
|
+ },
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, if you are using the template utilities (`serialize`, `importName`, `importSources`), you can replace them as follows with utilities from `knitwork`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { genDynamicImport, genImport, genSafeVariableName } from 'knitwork'
|
||||||
|
|
||||||
|
const serialize = (data: any) => JSON.stringify(data, null, 2).replace(/"{(.+)}"(?=,?$)/gm, r => JSON.parse(r).replace(/^{(.*)}$/, '$1'))
|
||||||
|
|
||||||
|
const importSources = (sources: string | string[], { lazy = false } = {}) => {
|
||||||
|
return toArray(sources).map((src) => {
|
||||||
|
if (lazy) {
|
||||||
|
return `const ${genSafeVariableName(src)} = ${genDynamicImport(src, { comment: `webpackChunkName: ${JSON.stringify(src)}` })}`
|
||||||
|
}
|
||||||
|
return genImport(src, genSafeVariableName(src))
|
||||||
|
}).join('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
const importName = genSafeVariableName
|
||||||
|
```
|
||||||
|
|
||||||
## Nuxt 2 vs Nuxt 3
|
## Nuxt 2 vs Nuxt 3
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user