mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-24 14:45:15 +00:00
Merge remote-tracking branch 'origin/main' into fix/14275-nuxt-3-does-not-respect-layouts-in-separate-folders
This commit is contained in:
commit
78939769c9
9
.devcontainer/Dockerfile
Normal file
9
.devcontainer/Dockerfile
Normal file
@ -0,0 +1,9 @@
|
||||
FROM node:lts
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -fy libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdbus-1-3 libdrm2 libxkbcommon0 libatspi2.0-0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libasound2 && \
|
||||
apt-get clean autoclean && \
|
||||
apt-get autoremove --yes && \
|
||||
rm -rf /var/lib/{apt,dpkg,cache,log}
|
||||
|
||||
RUN corepack enable && npx playwright install
|
20
.devcontainer/devcontainer.json
Normal file
20
.devcontainer/devcontainer.json
Normal file
@ -0,0 +1,20 @@
|
||||
// https://code.visualstudio.com/docs/devcontainers/containers
|
||||
// https://containers.dev/implementors/json_reference/
|
||||
{
|
||||
"name": "nuxt-devcontainer",
|
||||
"dockerFile": "Dockerfile",
|
||||
"features": {},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {},
|
||||
"extensions": [
|
||||
"ms-azuretools.vscode-docker",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"github.vscode-github-actions",
|
||||
"vue.volar"
|
||||
]
|
||||
}
|
||||
},
|
||||
"postStartCommand": "pnpm install && pnpm build:stub",
|
||||
"mounts": ["type=volume,target=${containerWorkspaceFolder}/node_modules"]
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
dist
|
||||
node_modules
|
||||
schema
|
||||
**/*.tmpl.*
|
||||
sw.js
|
54
.eslintrc
54
.eslintrc
@ -1,11 +1,17 @@
|
||||
{
|
||||
"ignorePatterns": [
|
||||
"dist",
|
||||
"node_modules",
|
||||
"packages/schema/schema",
|
||||
"**/*.tmpl.*",
|
||||
"sw.js"
|
||||
],
|
||||
"$schema": "https://json.schemastore.org/eslintrc",
|
||||
"globals": {
|
||||
"NodeJS": true,
|
||||
"$fetch": true
|
||||
},
|
||||
"plugins": [
|
||||
"jsdoc"
|
||||
],
|
||||
"plugins": ["jsdoc", "no-only-tests"],
|
||||
"extends": [
|
||||
"plugin:jsdoc/recommended",
|
||||
"@nuxtjs/eslint-config-typescript",
|
||||
@ -18,8 +24,9 @@
|
||||
"ignoreDeclarationSort": true
|
||||
}
|
||||
],
|
||||
"no-only-tests/no-only-tests": "error",
|
||||
"unicorn/prefer-node-protocol": "error",
|
||||
"no-console": "off",
|
||||
"no-console": "warn",
|
||||
"vue/multi-word-component-names": "off",
|
||||
"vue/one-component-per-file": "off",
|
||||
"vue/require-default-prop": "off",
|
||||
@ -41,6 +48,10 @@
|
||||
{
|
||||
"pattern": "@nuxt/test-utils",
|
||||
"group": "external"
|
||||
},
|
||||
{
|
||||
"pattern": "#vue-router",
|
||||
"group": "external"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -73,6 +84,14 @@
|
||||
"disallowTypeAnnotations": false
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/ban-ts-comment": [
|
||||
"error",
|
||||
{
|
||||
"ts-expect-error": "allow-with-description",
|
||||
"ts-ignore": true
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/prefer-ts-expect-error": "error",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
@ -80,8 +99,35 @@
|
||||
"varsIgnorePattern": "^_",
|
||||
"ignoreRestSiblings": true
|
||||
}
|
||||
],
|
||||
"jsdoc/check-tag-names": [
|
||||
"error",
|
||||
{
|
||||
"definedTags": ["__NO_SIDE_EFFECTS__"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["packages/schema/**"],
|
||||
"rules": {
|
||||
"jsdoc/no-undefined-types": "off",
|
||||
"jsdoc/valid-types": "off",
|
||||
"jsdoc/check-tag-names": [
|
||||
"error",
|
||||
{
|
||||
"definedTags": ["experimental"]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["packages/nuxt/src/app/**", "test/**", "**/runtime/**"],
|
||||
"rules": {
|
||||
"no-console": "off"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"jsdoc": {
|
||||
"ignoreInternal": true,
|
||||
|
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@ -10,7 +10,7 @@ body:
|
||||
|
||||
Please use a template below to create a minimal reproduction
|
||||
👉 https://stackblitz.com/github/nuxt/starter/tree/v3-stackblitz
|
||||
👉 https://codesandbox.io/p/github/nuxt/starter/v3-codesandbox
|
||||
👉 https://codesandbox.io/s/github/nuxt/starter/v3-codesandbox
|
||||
- type: textarea
|
||||
id: bug-env
|
||||
attributes:
|
||||
|
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -4,7 +4,7 @@ contact_links:
|
||||
url: https://nuxt.com/docs/
|
||||
about: Check the documentation for usage of Nuxt 3
|
||||
- name: 📚 Nuxt 2 Documentation
|
||||
url: https://nuxtjs.org/
|
||||
url: https://v2.nuxt.com/
|
||||
about: Check the documentation for usage of Nuxt 2
|
||||
- name: 💬 Discussions
|
||||
url: https://github.com/nuxt/nuxt/discussions
|
||||
|
2
.github/ISSUE_TEMPLATE/z-bug-report-2.yml
vendored
2
.github/ISSUE_TEMPLATE/z-bug-report-2.yml
vendored
@ -10,7 +10,7 @@ body:
|
||||
|
||||
Please use a template below to create a minimal reproduction
|
||||
👉 https://stackblitz.com/github/nuxt/starter/tree/v2
|
||||
👉 https://codesandbox.io/p/github/nuxt/starter/v2
|
||||
👉 https://codesandbox.io/s/github/nuxt/starter/v2
|
||||
- type: textarea
|
||||
id: bug-env
|
||||
attributes:
|
||||
|
1
.github/assets/banner.svg
vendored
Normal file
1
.github/assets/banner.svg
vendored
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 10 KiB |
2
.github/assets/twitter.svg
vendored
2
.github/assets/twitter.svg
vendored
@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="39.4" height="32" viewBox="0 0 1231.051 1000"><path fill="#A1A1AA" d="M1231.051 118.453q-51.422 76.487-126.173 130.403q.738 14.46.738 32.687q0 101.273-29.53 202.791q-29.53 101.519-90.215 194.343q-60.685 92.824-144.574 164.468q-83.889 71.644-201.677 114.25q-117.788 42.606-252.474 42.606q-210.2 0-387.147-113.493q31.406 3.495 60.242 3.495q175.605 0 313.687-108.177q-81.877-1.501-146.654-50.409q-64.777-48.907-89.156-124.988q24.097 4.59 47.566 4.59q33.782 0 66.482-8.812q-87.378-17.5-144.975-87.04q-57.595-69.539-57.595-160.523v-3.126q53.633 29.696 114.416 31.592q-51.762-34.508-82.079-89.999q-30.319-55.491-30.319-120.102q0-68.143 34.151-126.908q95.022 116.607 230.278 186.392q135.258 69.786 290.212 77.514q-6.609-27.543-6.621-57.485q0-104.546 73.994-178.534Q747.623 0 852.169 0q109.456 0 184.392 79.711q85.618-16.959 160.333-61.349q-28.785 90.59-110.933 139.768q75.502-8.972 145.088-39.677z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><g><path fill="#A1A1AA" d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"></path></g></svg>
|
||||
|
Before Width: | Height: | Size: 947 B After Width: | Height: | Size: 285 B |
32
.github/reproduire/needs-reproduction.md
vendored
Normal file
32
.github/reproduire/needs-reproduction.md
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
Would you be able to provide a [reproduction](https://nuxt.com/docs/community/reporting-bugs/#create-a-minimal-reproduction)? 🙏
|
||||
|
||||
<details>
|
||||
<summary>More info</summary>
|
||||
|
||||
### Why do I need to provide a reproduction?
|
||||
|
||||
Reproductions make it possible for us to triage and fix issues quickly with a relatively small team. It helps us discover the source of the problem, and also can reveal assumptions you or we might be making.
|
||||
|
||||
### What will happen?
|
||||
|
||||
If you've provided a reproduction, we'll remove the label and try to reproduce the issue. If we can, we'll mark it as a bug and prioritize it based on its severity and how many people we think it might affect.
|
||||
|
||||
If `needs reproduction` labeled issues don't receive any substantial activity (e.g., new comments featuring a reproduction link), we'll close them. That's not because we don't care! At any point, feel free to comment with a reproduction and we'll reopen it.
|
||||
|
||||
### How can I create a reproduction?
|
||||
|
||||
We have a couple of templates for starting with a minimal reproduction:
|
||||
|
||||
👉 https://stackblitz.com/github/nuxt/starter/tree/v3-stackblitz
|
||||
👉 https://codesandbox.io/s/github/nuxt/starter/v3-codesandbox
|
||||
|
||||
A public GitHub repository is also perfect. 👌
|
||||
|
||||
Please ensure that the reproduction is as **minimal** as possible. See more details [in our guide](https://nuxt.com/docs/community/reporting-bugs/#create-a-minimal-reproduction).
|
||||
|
||||
You might also find these other articles interesting and/or helpful:
|
||||
|
||||
- [The Importance of Reproductions](https://antfu.me/posts/why-reproductions-are-required)
|
||||
- [How to Generate a Minimal, Complete, and Verifiable Example](https://stackoverflow.com/help/mcve)
|
||||
|
||||
</details>
|
11
.github/workflows/autofix-docs.yml
vendored
11
.github/workflows/autofix-docs.yml
vendored
@ -5,6 +5,9 @@ on:
|
||||
paths:
|
||||
- "docs/**"
|
||||
- ".github/workflows/docs.yml"
|
||||
push:
|
||||
branches:
|
||||
- "renovate/**"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@ -14,11 +17,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
@ -27,4 +30,4 @@ jobs:
|
||||
- name: Lint (docs)
|
||||
run: pnpm lint:docs:fix
|
||||
|
||||
- uses: autofix-ci/action@8bc06253bec489732e5f9c52884c7cace15c0160
|
||||
- uses: autofix-ci/action@d3e591514b99d0fca6779455ff8338516663f7cc
|
||||
|
13
.github/workflows/autofix.yml
vendored
13
.github/workflows/autofix.yml
vendored
@ -13,18 +13,18 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Dedupe dependencies
|
||||
if: ${{ contains(github.ref_name, 'renovate') }}
|
||||
if: ${{ contains(github.head_ref, 'renovate') }}
|
||||
run: pnpm dedupe
|
||||
|
||||
- name: Build (stub)
|
||||
@ -36,7 +36,10 @@ jobs:
|
||||
- name: Test (unit)
|
||||
run: pnpm test:unit -u
|
||||
|
||||
- name: Run build
|
||||
run: pnpm build
|
||||
|
||||
- name: Update bundle size
|
||||
run: pnpm vitest run bundle -u
|
||||
|
||||
- uses: autofix-ci/action@8bc06253bec489732e5f9c52884c7cace15c0160
|
||||
- uses: autofix-ci/action@d3e591514b99d0fca6779455ff8338516663f7cc
|
||||
|
37
.github/workflows/changelogensets.yml
vendored
Normal file
37
.github/workflows/changelogensets.yml
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 2.x
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.number || github.sha }}
|
||||
cancel-in-progress: ${{ github.event_name != 'push' }}
|
||||
|
||||
jobs:
|
||||
update-changelog:
|
||||
if: github.repository_owner == 'nuxt' && !contains(github.event.head_commit.message, 'v3.')
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
with:
|
||||
node-version: 20
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- run: pnpm jiti ./scripts/update-changelog.ts
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
45
.github/workflows/check-links.yml
vendored
Normal file
45
.github/workflows/check-links.yml
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
name: Check links with Lychee
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "docs/**"
|
||||
- "*.md"
|
||||
branches:
|
||||
- main
|
||||
|
||||
# Remove default permissions of GITHUB_TOKEN for security
|
||||
# https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
link-checker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Cache lychee results (e.g. to avoid hitting rate limits)
|
||||
- name: Restore lychee cache
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
with:
|
||||
path: .lycheecache
|
||||
key: cache-lychee-${{ github.sha }}
|
||||
restore-keys: cache-lychee-
|
||||
|
||||
# check links with Lychee
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
|
||||
- name: Lychee link checker
|
||||
uses: lycheeverse/lychee-action@2ac9f030ccdea0033e2510a23a67da2a2da98492 # for v1.8.0
|
||||
with:
|
||||
# arguments with file types to check
|
||||
args: >-
|
||||
--cache
|
||||
--verbose
|
||||
--no-progress
|
||||
--max-cache-age=1d
|
||||
'./**/*.md'
|
||||
'./**/*.html'
|
||||
# fail the action on broken links
|
||||
fail: true
|
||||
env:
|
||||
# to be used in case rate limits are surpassed
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
155
.github/workflows/ci.yml
vendored
155
.github/workflows/ci.yml
vendored
@ -4,13 +4,16 @@ on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- "docs/**"
|
||||
- "*.md"
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "docs/**"
|
||||
- "*.md"
|
||||
branches:
|
||||
- main
|
||||
- "!v[0-9]*"
|
||||
|
||||
# https://github.com/vitejs/vite/blob/main/.github/workflows/ci.yml
|
||||
env:
|
||||
@ -35,11 +38,11 @@ jobs:
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
@ -55,37 +58,82 @@ jobs:
|
||||
run: pnpm build
|
||||
|
||||
- name: Cache dist
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||
with:
|
||||
retention-days: 3
|
||||
name: dist
|
||||
path: packages/*/dist
|
||||
|
||||
typecheck:
|
||||
codeql:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
needs:
|
||||
- build
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@fdcae64e1484d349b3366718cdfef3d404390e85 # v2.22.1
|
||||
with:
|
||||
languages: javascript
|
||||
queries: +security-and-quality
|
||||
|
||||
- name: Restore dist cache
|
||||
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
|
||||
with:
|
||||
name: dist
|
||||
path: packages
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@fdcae64e1484d349b3366718cdfef3d404390e85 # v2.22.1
|
||||
with:
|
||||
category: "/language:javascript"
|
||||
|
||||
typecheck:
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 10
|
||||
needs:
|
||||
- build
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
module: ['bundler', 'node']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
with:
|
||||
node-version: 20
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Restore dist cache
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
|
||||
with:
|
||||
name: dist
|
||||
path: packages
|
||||
|
||||
- name: Test (types)
|
||||
run: pnpm test:types
|
||||
env:
|
||||
MODULE_RESOLUTION: ${{ matrix.module }}
|
||||
|
||||
lint:
|
||||
# autofix workflow will be triggered instead for PRs
|
||||
@ -94,11 +142,11 @@ jobs:
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
@ -121,17 +169,21 @@ jobs:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
env: ['dev', 'built']
|
||||
builder: ['vite', 'webpack']
|
||||
node: [16]
|
||||
context: ['async', 'default']
|
||||
manifest: ['manifest-on', 'manifest-off']
|
||||
node: [18]
|
||||
exclude:
|
||||
- env: 'dev'
|
||||
builder: 'webpack'
|
||||
- manifest: 'manifest-off'
|
||||
builder: 'webpack'
|
||||
|
||||
timeout-minutes: 10
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
cache: "pnpm"
|
||||
@ -155,7 +207,7 @@ jobs:
|
||||
echo "PLAYWRIGHT_VERSION=$env:PLAYWRIGHT_VERSION" >> $env:GITHUB_ENV
|
||||
|
||||
- name: Cache Playwright's binary
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
with:
|
||||
key: ${{ runner.os }}-playwright-bin-v1-${{ env.PLAYWRIGHT_VERSION }}
|
||||
path: ${{ env.PLAYWRIGHT_BROWSERS_PATH }}
|
||||
@ -163,11 +215,10 @@ jobs:
|
||||
${{ runner.os }}-playwright-bin-v1-
|
||||
|
||||
- name: Install Playwright
|
||||
# does not need to explicitly set chromium after https://github.com/microsoft/playwright/issues/14862 is solved
|
||||
run: pnpm playwright install chromium
|
||||
run: pnpm playwright-core install chromium
|
||||
|
||||
- name: Restore dist cache
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
|
||||
with:
|
||||
name: dist
|
||||
path: packages
|
||||
@ -175,13 +226,21 @@ jobs:
|
||||
- name: Test (unit)
|
||||
run: pnpm test:unit
|
||||
|
||||
- name: Test (runtime unit)
|
||||
run: pnpm test:runtime
|
||||
|
||||
- name: Test (fixtures)
|
||||
run: pnpm test:fixtures
|
||||
env:
|
||||
TEST_ENV: ${{ matrix.env }}
|
||||
TEST_BUILDER: ${{ matrix.builder }}
|
||||
TEST_MANIFEST: ${{ matrix.manifest }}
|
||||
TEST_CONTEXT: ${{ matrix.context }}
|
||||
SKIP_BUNDLE_SIZE: ${{ github.event_name != 'push' || matrix.env == 'dev' || matrix.builder == 'webpack' || matrix.context == 'default' || runner.os == 'Windows' }}
|
||||
|
||||
build-release:
|
||||
permissions:
|
||||
id-token: write
|
||||
if: |
|
||||
github.event_name == 'push' &&
|
||||
!contains(github.event.head_commit.message, '[skip-release]') &&
|
||||
@ -191,30 +250,24 @@ jobs:
|
||||
- lint
|
||||
- build
|
||||
- test-fixtures
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
node: [16]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
node-version: 20
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Restore dist cache
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
|
||||
with:
|
||||
name: dist
|
||||
path: packages
|
||||
@ -223,3 +276,43 @@ jobs:
|
||||
run: ./scripts/release-edge.sh
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{secrets.NODE_AUTH_TOKEN}}
|
||||
NPM_CONFIG_PROVENANCE: true
|
||||
|
||||
release-pr:
|
||||
permissions:
|
||||
id-token: write
|
||||
pull-requests: write
|
||||
if: |
|
||||
github.event_name == 'pull_request' &&
|
||||
contains(github.event.pull_request.labels.*.name, '🧷 edge release')
|
||||
needs:
|
||||
- lint
|
||||
- build
|
||||
- test-fixtures
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
with:
|
||||
node-version: 20
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Restore dist cache
|
||||
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
|
||||
with:
|
||||
name: dist
|
||||
path: packages
|
||||
|
||||
- name: Release Edge
|
||||
run: ./scripts/release-edge.sh pr-${{ github.event.issue.number }}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{secrets.NODE_AUTH_TOKEN}}
|
||||
NPM_CONFIG_PROVENANCE: true
|
||||
|
22
.github/workflows/dependency-review.yml
vendored
Normal file
22
.github/workflows/dependency-review.yml
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
# Dependency Review Action
|
||||
#
|
||||
# This Action will scan dependency manifest files that change as part of a Pull Request,
|
||||
# surfacing known-vulnerable versions of the packages declared or updated in the PR.
|
||||
# Once installed, if the workflow run is marked as required,
|
||||
# PRs introducing known-vulnerable packages will be blocked from merging.
|
||||
#
|
||||
# Source repository: https://github.com/actions/dependency-review-action
|
||||
name: 'Dependency Review'
|
||||
on: [pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
dependency-review:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@6c5ccdad469c9f8a2996bfecaec55a631a347034 # v3.1.0
|
32
.github/workflows/docs-e2e.yml
vendored
32
.github/workflows/docs-e2e.yml
vendored
@ -1,32 +0,0 @@
|
||||
name: docs-e2e
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
url:
|
||||
required: false
|
||||
description: The URL to run the test suite against.
|
||||
type: string
|
||||
deployment_status:
|
||||
|
||||
jobs:
|
||||
crawl-docs:
|
||||
environment:
|
||||
name: ${{ github.event.deployment.environment || 'Production' }}
|
||||
url: ${{ github.event.inputs.url || github.event.deployment.payload.web_url || github.event.deployment_status.target_url }}
|
||||
if: github.event.deployment_status.state == 'success' || github.event_name == 'workflow_dispatch'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- run: node ./scripts/crawl.mjs
|
||||
env:
|
||||
BASE_URL: ${{ github.event.inputs.url || github.event.deployment.payload.web_url || github.event.deployment_status.target_url }}
|
8
.github/workflows/docs.yml
vendored
8
.github/workflows/docs.yml
vendored
@ -5,9 +5,11 @@ on:
|
||||
paths:
|
||||
- "docs/**"
|
||||
- ".github/workflows/docs.yml"
|
||||
- "*.md"
|
||||
# autofix workflow will be triggered instead for PRs
|
||||
branches:
|
||||
- main
|
||||
- "!v[0-9]*"
|
||||
|
||||
# Remove default permissions of GITHUB_TOKEN for security
|
||||
# https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs
|
||||
@ -18,11 +20,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
|
96
.github/workflows/ecosystem-ci-trigger.yml
vendored
Normal file
96
.github/workflows/ecosystem-ci-trigger.yml
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
name: ecosystem-ci trigger
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
trigger:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'nuxt/nuxt' && github.event.issue.pull_request && startsWith(github.event.comment.body, '/ecosystem-ci run')
|
||||
steps:
|
||||
- uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1
|
||||
with:
|
||||
script: |
|
||||
const user = context.payload.sender.login
|
||||
console.log(`Validate user: ${user}`)
|
||||
|
||||
let hasTriagePermission = false
|
||||
try {
|
||||
const { data } = await github.rest.repos.getCollaboratorPermissionLevel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
username: user,
|
||||
});
|
||||
hasTriagePermission = data.user.permissions.triage
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
|
||||
if (hasTriagePermission) {
|
||||
console.log('Allowed')
|
||||
await github.rest.reactions.createForIssueComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: context.payload.comment.id,
|
||||
content: '+1',
|
||||
})
|
||||
} else {
|
||||
console.log('Not allowed')
|
||||
await github.rest.reactions.createForIssueComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: context.payload.comment.id,
|
||||
content: '-1',
|
||||
})
|
||||
throw new Error('not allowed')
|
||||
}
|
||||
- uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1
|
||||
id: get-pr-data
|
||||
with:
|
||||
script: |
|
||||
console.log(`Get PR info: ${context.repo.owner}/${context.repo.repo}#${context.issue.number}`)
|
||||
const { data: pr } = await github.rest.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.issue.number
|
||||
})
|
||||
return {
|
||||
num: context.issue.number,
|
||||
branchName: pr.head.ref,
|
||||
repo: pr.head.repo.full_name
|
||||
}
|
||||
- id: generate-token
|
||||
uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2.1.0
|
||||
with:
|
||||
app_id: ${{ secrets.ECOSYSTEM_CI_GITHUB_APP_ID }}
|
||||
private_key: ${{ secrets.ECOSYSTEM_CI_GITHUB_APP_PRIVATE_KEY }}
|
||||
repository: "${{ github.repository_owner }}/ecosystem-ci"
|
||||
- uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1
|
||||
id: trigger
|
||||
env:
|
||||
COMMENT: ${{ github.event.comment.body }}
|
||||
with:
|
||||
github-token: ${{ steps.generate-token.outputs.token }}
|
||||
result-encoding: string
|
||||
script: |
|
||||
const comment = process.env.COMMENT.trim()
|
||||
const prData = ${{ steps.get-pr-data.outputs.result }}
|
||||
|
||||
const suite = comment.split('\n')[0].replace(/^\/ecosystem-ci run/, '').trim()
|
||||
|
||||
await github.rest.actions.createWorkflowDispatch({
|
||||
owner: context.repo.owner,
|
||||
repo: 'ecosystem-ci',
|
||||
workflow_id: 'ecosystem-ci-from-pr.yml',
|
||||
ref: 'main',
|
||||
inputs: {
|
||||
prNumber: '' + prData.num,
|
||||
branchName: prData.branchName,
|
||||
repo: prData.repo,
|
||||
suite: suite === '' ? '-' : suite
|
||||
}
|
||||
})
|
8
.github/workflows/introspect.yml
vendored
8
.github/workflows/introspect.yml
vendored
@ -11,15 +11,19 @@ on:
|
||||
- ".github/workflows/**"
|
||||
branches:
|
||||
- main
|
||||
- "!v[0-9]*"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
lint-workflows:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
# From https://github.com/rhysd/actionlint/blob/main/docs/usage.md#use-actionlint-on-github-actions
|
||||
- name: Check workflow files
|
||||
run: |
|
||||
bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
|
||||
bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/590d3bd9dde0c91f7a66071d40eb84716526e5a6/scripts/download-actionlint.bash) 1.6.25
|
||||
./actionlint -color -shellcheck=""
|
||||
|
20
.github/workflows/nuxt2-edge.yml
vendored
20
.github/workflows/nuxt2-edge.yml
vendored
@ -5,20 +5,31 @@ on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
# https://github.com/vitejs/vite/blob/main/.github/workflows/ci.yml
|
||||
env:
|
||||
# 7 GiB by default on GitHub, setting to 6 GiB
|
||||
# https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources
|
||||
NODE_OPTIONS: --max-old-space-size=6144
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
nightly:
|
||||
if: github.repository_owner == 'nuxt'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
with:
|
||||
ref: '2.x'
|
||||
fetch-depth: 0 # All history
|
||||
- name: fetch tags
|
||||
run: git fetch --depth=1 origin "+refs/tags/*:refs/tags/*"
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
with:
|
||||
node-version: 14
|
||||
node-version: 16
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
- name: install
|
||||
run: yarn --check-files --frozen-lockfile --non-interactive
|
||||
@ -28,8 +39,6 @@ jobs:
|
||||
run: yarn run audit
|
||||
- name: build
|
||||
run: yarn test:fixtures -i
|
||||
env:
|
||||
NODE_OPTIONS: "--max_old_space_size=4096"
|
||||
- name: lint app
|
||||
run: yarn lint:app
|
||||
- name: test types
|
||||
@ -48,4 +57,5 @@ jobs:
|
||||
run: ./scripts/workspace-run npm publish -q
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{secrets.NODE_AUTH_TOKEN}}
|
||||
NPM_CONFIG_PROVENANCE: true
|
||||
|
||||
|
65
.github/workflows/release-pr.yml
vendored
Normal file
65
.github/workflows/release-pr.yml
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
name: release
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
env:
|
||||
# 7 GiB by default on GitHub, setting to 6 GiB
|
||||
NODE_OPTIONS: --max-old-space-size=6144
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
release-pr:
|
||||
if: github.repository == 'nuxt/nuxt' && github.event.issue.pull_request && github.event.comment.body == '/trigger release'
|
||||
permissions:
|
||||
id-token: write
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
|
||||
steps:
|
||||
- name: Ensure action is by maintainer
|
||||
uses: octokit/request-action@89697eb6635e52c6e1e5559f15b5c91ba5100cb0 # v2.1.9
|
||||
id: check_role
|
||||
with:
|
||||
route: GET /repos/nuxt/nuxt/collaborators/${{ github.event.comment.user.login }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
with:
|
||||
ref: refs/pull/${{ github.event.issue.number }}/merge
|
||||
fetch-depth: 0
|
||||
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
with:
|
||||
node-version: 20
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
|
||||
- name: Release Edge
|
||||
run: ./scripts/release-edge.sh pr-${{ github.event.issue.number }}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
|
||||
NPM_CONFIG_PROVENANCE: true
|
||||
|
||||
- name: Post comment
|
||||
uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: `:rocket: Release triggered! You can now install [nuxt@npm:nuxt-nightly@pr-${{ github.event.issue.number }}](https://www.npmjs.com/package/nuxt-nightly/v/pr-${{ github.event.issue.number }})`
|
||||
})
|
24
.github/workflows/reproduire-close.yml
vendored
Normal file
24
.github/workflows/reproduire-close.yml
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
name: Close incomplete issues
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '30 1 * * *' # run every day
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@1160a2240286f5da8ec72b1c0816ce2481aabf84 # v8.0.0
|
||||
with:
|
||||
days-before-stale: -1 # Issues and PR will never be flagged stale automatically.
|
||||
stale-issue-label: 'needs reproduction' # Label that flags an issue as stale.
|
||||
only-labels: 'needs reproduction' # Only process these issues
|
||||
days-before-issue-close: 7
|
||||
ignore-updates: true
|
||||
remove-stale-when-updated: false
|
||||
close-issue-message: This issue was closed because it was open for 7 days without a reproduction.
|
||||
close-issue-label: closed-by-bot
|
||||
operations-per-run: 300 #default 30
|
16
.github/workflows/reproduire.yml
vendored
Normal file
16
.github/workflows/reproduire.yml
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
name: Reproduire
|
||||
on:
|
||||
issues:
|
||||
types: [labeled]
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
reproduire:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
- uses: Hebilicious/reproduire@4b686ae9cbb72dad60f001d278b6e3b2ce40a9ac # v0.0.9-mp
|
||||
with:
|
||||
label: needs reproduction
|
71
.github/workflows/scorecards.yml
vendored
Normal file
71
.github/workflows/scorecards.yml
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
# This workflow uses actions that are not certified by GitHub. They are provided
|
||||
# by a third-party and are governed by separate terms of service, privacy
|
||||
# policy, and support documentation.
|
||||
|
||||
name: Scorecard supply-chain security
|
||||
on:
|
||||
# For Branch-Protection check. Only the default branch is supported. See
|
||||
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
|
||||
branch_protection_rule:
|
||||
# To guarantee Maintained check is occasionally updated. See
|
||||
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
|
||||
schedule:
|
||||
- cron: '20 7 * * 2'
|
||||
push:
|
||||
branches: ["main"]
|
||||
|
||||
# Declare default permissions as read only.
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
analysis:
|
||||
name: Scorecard analysis
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# Needed to upload the results to code-scanning dashboard.
|
||||
security-events: write
|
||||
# Needed to publish results and get a badge (see publish_results below).
|
||||
id-token: write
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: "Run analysis"
|
||||
uses: ossf/scorecard-action@483ef80eb98fb506c348f7d62e28055e49fe2398 # v2.3.0
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
|
||||
# - you want to enable the Branch-Protection check on a *public* repository, or
|
||||
# - you are installing Scorecards on a *private* repository
|
||||
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
|
||||
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
|
||||
|
||||
# Public repositories:
|
||||
# - Publish results to OpenSSF REST API for easy access by consumers
|
||||
# - Allows the repository to include the Scorecard badge.
|
||||
# - See https://github.com/ossf/scorecard-action#publishing-results.
|
||||
# For private repositories:
|
||||
# - `publish_results` will always be set to `false`, regardless
|
||||
# of the value entered here.
|
||||
publish_results: true
|
||||
|
||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||
# format to the repository Actions tab.
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
retention-days: 5
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@fdcae64e1484d349b3366718cdfef3d404390e85 # v2.22.1
|
||||
with:
|
||||
sarif_file: results.sarif
|
40
.github/workflows/semantic-pull-requests.yml
vendored
Normal file
40
.github/workflows/semantic-pull-requests.yml
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
name: Semantic pull request
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- edited
|
||||
- synchronize
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
main:
|
||||
permissions:
|
||||
pull-requests: read # for amannn/action-semantic-pull-request to analyze PRs
|
||||
statuses: write # for amannn/action-semantic-pull-request to mark status of analyzed PR
|
||||
if: github.repository == 'nuxt/nuxt' && !startsWith(github.head_ref, 'v')
|
||||
runs-on: ubuntu-latest
|
||||
name: Semantic pull request
|
||||
steps:
|
||||
- name: Validate PR title
|
||||
uses: amannn/action-semantic-pull-request@47b15d52c5c30e94a17ec87eb8dd51ff5221fed9 # v5.3.0
|
||||
with:
|
||||
scopes: |
|
||||
kit
|
||||
nuxi
|
||||
nuxt
|
||||
schema
|
||||
test-utils
|
||||
vite
|
||||
webpack
|
||||
deps
|
||||
subjectPattern: ^(?![A-Z]).+$
|
||||
subjectPatternError: |
|
||||
The subject "{subject}" found in the pull request title "{title}"
|
||||
didn't match the configured pattern. Please ensure that the subject
|
||||
doesn't start with an uppercase character.
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
8
.gitignore
vendored
8
.gitignore
vendored
@ -4,6 +4,7 @@ jspm_packages
|
||||
|
||||
package-lock.json
|
||||
packages/*/README.md
|
||||
!packages/nuxi/README.md
|
||||
packages/*/LICENSE
|
||||
*/**/yarn.lock
|
||||
/.yarn
|
||||
@ -16,11 +17,17 @@ packages/*/LICENSE
|
||||
.tmp
|
||||
.cache
|
||||
|
||||
# Lychee cache
|
||||
.lycheecache
|
||||
|
||||
# Generated dirs
|
||||
dist
|
||||
.nuxt
|
||||
.nuxt-*
|
||||
.vercel
|
||||
.netlify
|
||||
.output
|
||||
.output-*
|
||||
.gen
|
||||
|
||||
# Junit reports
|
||||
@ -65,3 +72,4 @@ Temporary Items
|
||||
.netlify
|
||||
|
||||
fixtures-temp
|
||||
.pnpm-store
|
||||
|
12
.stackblitz/codeflow.json
Normal file
12
.stackblitz/codeflow.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"@nuxt/kit": "./packages/kit",
|
||||
"@nuxt/schema": "./packages/schema",
|
||||
"@nuxt/test-utils": "./packages/test-utils",
|
||||
"@nuxt/vite": "./packages/vite",
|
||||
"@nuxt/webpack": "./packages/webpack",
|
||||
"nuxt": "./packages/nuxt"
|
||||
}
|
||||
}
|
||||
}
|
7
.vscode/extensions.json
vendored
Normal file
7
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"vue.volar"
|
||||
]
|
||||
}
|
12
.website/.gitignore
vendored
Executable file
12
.website/.gitignore
vendored
Executable file
@ -0,0 +1,12 @@
|
||||
node_modules
|
||||
*.iml
|
||||
.idea
|
||||
*.log*
|
||||
.nuxt
|
||||
.vscode
|
||||
.DS_Store
|
||||
coverage
|
||||
dist
|
||||
sw.*
|
||||
.env
|
||||
.output
|
35
.website/README.md
Executable file
35
.website/README.md
Executable file
@ -0,0 +1,35 @@
|
||||
# Nuxt Docs Website
|
||||
|
||||
This is a temporary directory until we open source the repository for nuxt.com.
|
||||
|
||||
The goal is to simplify the contribution in the meantime to the documentation by having the possibility to preview the changes locally.
|
||||
|
||||
## Setup
|
||||
|
||||
Install dependencies in the root of the `nuxt` folder:
|
||||
|
||||
```bash
|
||||
pnpm i
|
||||
```
|
||||
|
||||
Then stub the dependencies:
|
||||
|
||||
```bash
|
||||
pnpm build:stub
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
In the root of the `nuxt` folder, run:
|
||||
|
||||
```bash
|
||||
pnpm docs:dev
|
||||
```
|
||||
|
||||
Then open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
Update the documentation within the `docs` folder.
|
||||
|
||||
---
|
||||
|
||||
For a detailed explanation of how things work, check out [Docus](https://docus.dev).
|
14
.website/app.config.ts
Normal file
14
.website/app.config.ts
Normal file
@ -0,0 +1,14 @@
|
||||
export default defineAppConfig({
|
||||
docus: {
|
||||
title: 'Nuxt Docs [dev]',
|
||||
description: 'The best place to start your documentation.',
|
||||
socials: {
|
||||
twitter: 'nuxt_js',
|
||||
github: 'nuxt/nuxt'
|
||||
},
|
||||
aside: {
|
||||
level: 1,
|
||||
collapsed: false,
|
||||
},
|
||||
}
|
||||
})
|
20
.website/nuxt.config.ts
Executable file
20
.website/nuxt.config.ts
Executable file
@ -0,0 +1,20 @@
|
||||
import { createResolver } from 'nuxt/kit'
|
||||
|
||||
const { resolve } = createResolver(import.meta.url)
|
||||
|
||||
export default defineNuxtConfig({
|
||||
// https://github.com/nuxt-themes/docus
|
||||
extends: '@nuxt-themes/docus',
|
||||
content: {
|
||||
sources: {
|
||||
docs: {
|
||||
driver: 'fs',
|
||||
prefix: '/',
|
||||
base: resolve('../docs')
|
||||
}
|
||||
}
|
||||
},
|
||||
experimental: {
|
||||
renderJsonPayloads: false
|
||||
}
|
||||
})
|
14
.website/package.json
Executable file
14
.website/package.json
Executable file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "docus-starter",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "nuxt dev",
|
||||
"build": "nuxt build",
|
||||
"generate": "nuxt generate",
|
||||
"preview": "nuxt preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt-themes/docus": "1.15.0"
|
||||
}
|
||||
}
|
0
examples/advanced/config-extends/tsconfig.json → .website/tsconfig.json
Normal file → Executable file
0
examples/advanced/config-extends/tsconfig.json → .website/tsconfig.json
Normal file → Executable file
143
README.md
143
README.md
@ -1,113 +1,52 @@
|
||||
[![Nuxt banner](./.github/assets/banner.svg)](https://nuxt.com)
|
||||
|
||||
[![Nuxt banner](./.github/assets/banner.png)](https://nuxt.com)
|
||||
|
||||
# 👋 Welcome to Nuxt
|
||||
|
||||
|
||||
Nuxt's goal is to make web development intuitive and performant, with a great developer experience.<br>Learn more in the ['What is Nuxt?'](https://nuxt.com/docs/getting-started/introduction) section of our documentation.
|
||||
|
||||
# Nuxt
|
||||
|
||||
<p>
|
||||
<a href="https://www.npmjs.com/package/nuxt"><img src="https://img.shields.io/npm/v/nuxt.svg?style=flat&colorA=18181B&colorB=28CF8D" alt="Version"></a>
|
||||
<a href="https://www.npmjs.com/package/nuxt"><img src="https://img.shields.io/npm/dm/nuxt.svg?style=flat&colorA=18181B&colorB=28CF8D" alt="Downloads"></a>
|
||||
<a href="./LICENSE"><img src="https://img.shields.io/github/license/nuxt/nuxt.svg?style=flat&colorA=18181B&colorB=28CF8D" alt="License"></a>
|
||||
<a href="https://nuxt.com"><img src="https://img.shields.io/badge/Open%20Documentation-18181B?logo=nuxt.js" alt="Website"></a>
|
||||
<a href="https://volta.net/nuxt/nuxt?utm_source=nuxt_readme"><img src="https://user-images.githubusercontent.com/904724/209143798-32345f6c-3cf8-4e06-9659-f4ace4a6acde.svg" alt="Volta board"></a>
|
||||
<a href="https://nuxt.com"><img src="https://img.shields.io/badge/Nuxt%20Docs-18181B?logo=nuxt.js" alt="Website"></a>
|
||||
<a href="https://chat.nuxt.dev"><img src="https://img.shields.io/badge/Nuxt%20Discord-18181B?logo=discord" alt="Discord"></a>
|
||||
</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="2000" colspan="2">
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="80" align="center" valign="top">
|
||||
<br>
|
||||
<a href="https://nuxt.com/docs"><img src="./.github/assets/documentation.png"></a>
|
||||
</td>
|
||||
<td valign="top">
|
||||
<h3>Documentation</h3>
|
||||
<p>
|
||||
We highly recommend you take a look at <a href="https://nuxt.com">the Nuxt documentation</a> to level up.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="80" align="center" valign="top">
|
||||
<br>
|
||||
<a href="https://nuxt.com/modules"><img src="./.github/assets/modules.png"></a>
|
||||
</td>
|
||||
<td valign="top">
|
||||
<h3>Modules</h3>
|
||||
<p>
|
||||
Discover our <a href="https://nuxt.com/modules">list of modules</a> to supercharge your Nuxt project. Created by the Nuxt team and community.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="80" align="center" valign="top">
|
||||
<br>
|
||||
<a href="https://nuxt.com/docs/examples/hello-world"><img src="./.github/assets/examples.png"></a>
|
||||
</td>
|
||||
<td>
|
||||
<h3>Examples</h3>
|
||||
<p>
|
||||
Explore different ways of using Nuxt features and get inspired with <a href="https://nuxt.com/docs/examples/essentials/hello-world">our list of examples</a>.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
Nuxt is a free and open-source framework with an intuitive and extendable way to create type-safe, performant and production-grade full-stack web applications and websites with Vue.js.
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="2000" colspan="2">
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="80" align="center" valign="top">
|
||||
<br>
|
||||
<a href="https://nuxt.com/docs/community/reporting-bugs"><img src="./.github/assets/reporting-bugs.png"></a>
|
||||
</td>
|
||||
<td valign="top">
|
||||
<h3>Reporting bugs</h3>
|
||||
<p>
|
||||
Check out the <a href="https://nuxt.com/docs/community/reporting-bugs">Reporting Bugs</a> page.</p>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="80" align="center" valign="top">
|
||||
<br>
|
||||
<a href="https://nuxt.com/docs/community/contribution"><img src="./.github/assets/suggestions.png"></a>
|
||||
</td>
|
||||
<td valign="top">
|
||||
<h3>Suggestions</h3>
|
||||
<p>
|
||||
Check out the <a href="https://nuxt.com/docs/community/contribution">Contribution</a> page.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="80" align="center" valign="top">
|
||||
<br>
|
||||
<a href="https://nuxt.com/docs/community/getting-help"><img src="./.github/assets/questions.png"></a>
|
||||
</td>
|
||||
<td valign="top">
|
||||
<h3>Questions</h3>
|
||||
<p>
|
||||
Check out the <a href="https://nuxt.com/docs/community/getting-help">Getting Help</a> page.
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
It provides a number of features that make it easy to build fast, SEO-friendly, and scalable web applications, including:
|
||||
- Server-side rendering, Static Site Generation or Hybrid Rendering
|
||||
- Automatic routing with code-splitting
|
||||
- State management
|
||||
- SEO Optimization
|
||||
- Extensible with [100+ modules](https://nuxt.com/modules)
|
||||
- Deployment to a variety of hosting platforms
|
||||
- ...[and much more](https://nuxt.com) 🚀
|
||||
|
||||
## Getting Started
|
||||
|
||||
Use the following command to create a new starter project. This will create a starter project with all the necessary files and dependencies:
|
||||
|
||||
```bash
|
||||
npx nuxi@latest init <my-project>
|
||||
```
|
||||
|
||||
Discover also [nuxt.new](https://nuxt.new): Open a Nuxt starter on CodeSandbox, StackBlitz or locally to get up and running in a few seconds.
|
||||
|
||||
## Documentation
|
||||
|
||||
We highly recommend you take a look at the [Nuxt documentation](https://nuxt.com/docs) to level up. It’s a great resource for learning more about the framework. It covers everything from getting started to advanced topics.
|
||||
|
||||
## Modules
|
||||
|
||||
Discover our [list of modules](https://nuxt.com/modules) to supercharge your Nuxt project, created by the Nuxt team and community.
|
||||
|
||||
## Contribute
|
||||
|
||||
We invite you to contribute and help improve Nuxt 💚
|
||||
|
||||
Here are a few ways you can get involved:
|
||||
- **Reporting Bugs:** If you come across any bugs or issues, please check out the [reporting bugs guide](https://nuxt.com/docs/community/reporting-bugs) to learn how to submit a bug report.
|
||||
- **Suggestions:** Have ideas to enhance Nuxt? We'd love to hear them! Check out the [contribution guide](https://nuxt.com/docs/community/contribution#creating-an-issue) to share your suggestions.
|
||||
- **Questions:** If you have questions or need assistance, the [getting help guide](https://nuxt.com/docs/community/getting-help) provides resources to help you out.
|
||||
|
||||
## Local Development
|
||||
|
||||
@ -115,7 +54,7 @@ Follow the docs to [Set Up Your Local Development Environment](https://nuxt.com/
|
||||
|
||||
## Nuxt 2
|
||||
|
||||
You can find the code for Nuxt 2 on the [`2.x` branch](https://github.com/nuxt/nuxt/tree/2.x) and the documentation at [nuxtjs.org](https://nuxtjs.org).
|
||||
You can find the code for Nuxt 2 on the [`2.x` branch](https://github.com/nuxt/nuxt/tree/2.x) and the documentation at [v2.nuxt.com](https://v2.nuxt.com).
|
||||
|
||||
## Follow us
|
||||
|
||||
|
@ -32,7 +32,7 @@ buttons:
|
||||
- label: 'Explore Examples'
|
||||
size: 'sm'
|
||||
variant: 'secondary'
|
||||
to: '/docs/examples/essentials/hello-world'
|
||||
to: '/docs/examples/hello-world'
|
||||
---
|
||||
#title
|
||||
Getting Started
|
||||
@ -107,7 +107,7 @@ From an idea to a masterpiece, guides take you on the path to becoming a Nuxter.
|
||||
#title
|
||||
API
|
||||
#description
|
||||
Every Nuxt components, composables and utilities, in details.
|
||||
Every Nuxt component, composable and utility, in detail.
|
||||
#extra
|
||||
::card-list
|
||||
:::card-item
|
||||
|
@ -19,7 +19,7 @@ Nuxt uses conventions and an opinionated directory structure to automate repetit
|
||||
- **File-based routing:** define routes based on the structure of your [`pages/` directory](/docs/guide/directory-structure/pages). This can make it easier to organize your application and avoid the need for manual route configuration.
|
||||
- **Code splitting:** Nuxt automatically splits your code into smaller chunks, which can help reduce the initial load time of your application.
|
||||
- **Server-side rendering out of the box:** Nuxt comes with built-in SSR capabilities, so you don't have to set up a separate server yourself.
|
||||
- **Auto-imports:** write Vue composables and components in their respectives directories and use them without having to import them with the benefits of tree-shaking and optimised JS bundles.
|
||||
- **Auto-imports:** write Vue composables and components in their respective directories and use them without having to import them with the benefits of tree-shaking and optimized JS bundles.
|
||||
- **Data-fetching utilities:** Nuxt provides composables to handle SSR-compatible data fetching as well as different strategies.
|
||||
- **Zero-config TypeScript support:** write type-safe code without having to learn TypeScript with our auto-generated types and `tsconfig.json`
|
||||
- **Configured build tools:** we use [Vite](https://vitejs.dev) by default to support hot module replacement (HMR) in development and bundling your code for production with best-practices baked-in.
|
||||
|
@ -76,31 +76,66 @@ By default, the workload gets distributed to the workers with the round robin st
|
||||
|
||||
There are two ways to deploy a Nuxt application to any static hosting services:
|
||||
|
||||
- Static site generation (SSG) with `ssr: true` pre-renders routes of your application at build time. (This is the default behaviour when running `nuxi generate`.) It will also generate `/200.html` and `/404.html` single-page app fallback pages, which can render dynamic routes or 404 errors on the client (though you may need to configure this on your static host).
|
||||
- Static site generation (SSG) with `ssr: true` pre-renders routes of your application at build time. (This is the default behavior when running `nuxi generate`.) It will also generate `/200.html` and `/404.html` single-page app fallback pages, which can render dynamic routes or 404 errors on the client (though you may need to configure this on your static host).
|
||||
- Alternatively, you can prerender your site with `ssr: false` (static single-page app). This will produce HTML pages with an empty `<div id="__nuxt"></div>` where your Vue app would normally be rendered. You will lose many of the benefits of prerendering your site, so it is suggested instead to use `<ClientOnly>` to wrap the portions of your site that cannot be server rendered (if any).
|
||||
|
||||
### Crawl-based Pre-rendering
|
||||
|
||||
Use the [`nuxi generate` command](/docs/api/commands/generate) to build your application. For every page, Nuxt uses a crawler to generate a corresponding HTML and payload files. The built files will be generated in the `.output/public` directory.
|
||||
Use the [`nuxi generate` command](/docs/api/commands/generate) to build and pre-render your application using the [Nitro](/docs/guide/concepts/server-engine) crawler. This command is similar to `nuxt build` with the `nitro.static` option set to `true`, or running `nuxt build --prerender`.
|
||||
|
||||
```bash
|
||||
npx nuxi generate
|
||||
```
|
||||
|
||||
### Manual Pre-rendering
|
||||
That's it! You can now deploy the `.output/public` directory to any static hosting service or preview it locally with `npx serve .output/public`.
|
||||
|
||||
You can manually specify routes that [Nitro](/docs/guide/concepts/server-engine) will fetch and pre-render during the build.
|
||||
Working of the Nitro crawler:
|
||||
|
||||
1. Load the HTML of your application's root route (`/`), any non-dynamic pages in your `~/pages` directory, and any other routes in the `nitro.prerender.routes` array.
|
||||
2. Save the HTML and `payload.json` to the `~/.output/public/` directory to be served statically.
|
||||
3. Find all anchor tags (`<a href="...">`) in the HTML to navigate to other routes.
|
||||
4. Repeat steps 1-3 for each anchor tag found until there are no more anchor tags to crawl.
|
||||
|
||||
This is important to understand since pages that are not linked to a discoverable page can't be pre-rendered automatically.
|
||||
|
||||
::alert{type=info}
|
||||
Read more about the [`nuxi generate` command](/docs/api/commands/generate#nuxi-generate).
|
||||
::
|
||||
|
||||
### Selective Pre-rendering
|
||||
|
||||
You can manually specify routes that [Nitro](/docs/guide/concepts/server-engine) will fetch and pre-render during the build or ignore routes that you don't want to pre-render like `/dynamic` in the `nuxt.config` file:
|
||||
|
||||
```ts [nuxt.config.ts|js]
|
||||
defineNuxtConfig({
|
||||
nitro: {
|
||||
prerender: {
|
||||
routes: ['/user/1', '/user/2']
|
||||
ignore: ['/dynamic']
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
You can combine this with the `crawLinks` option to pre-render a set of routes that the crawler can't discover like your `/sitemap.xml` or `/robots.txt`:
|
||||
|
||||
```ts [nuxt.config.ts|js]
|
||||
defineNuxtConfig({
|
||||
nitro: {
|
||||
prerender: {
|
||||
crawlLinks: true,
|
||||
routes: ['/sitemap.xml', '/robots.txt']
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Setting `nitro.prerender` to `true` is similar to `nitro.prerender.crawlLinks` to `true`.
|
||||
|
||||
::alert{type=info}
|
||||
Read more about [pre-rendering](https://nitro.unjs.io/config#prerender) in the Nitro documentation.
|
||||
::
|
||||
|
||||
### Client-side Only Rendering
|
||||
|
||||
If you don't want to pre-render your routes, another way of using static hosting is to set the `ssr` property to `false` in the `nuxt.config` file. The `nuxi generate` command will then output an `.output/public/index.html` entrypoint and JavaScript bundles like a classic client-side Vue.js application.
|
||||
@ -140,12 +175,27 @@ Nuxt 3 can be deployed to several cloud providers with a minimal amount of confi
|
||||
- :icon{name="logos:aws" class="h-5 w-4 inline mb-2"} [AWS](https://nitro.unjs.io/deploy/providers/aws)
|
||||
- :icon{name="logos:microsoft-azure" class="h-5 w-4 inline mb-2"} [Azure](https://nitro.unjs.io/deploy/providers/azure)
|
||||
- :icon{name="ph:cloud-duotone" class="h-5 w-4 inline mb-2"} [Cleavr](https://nitro.unjs.io/deploy/providers/cleavr)
|
||||
- :icon{name="logos:cloudflare" class="h-5 w-4 inline mb-2"} [CloudFlare](https://nitro.unjs.io/deploy/providers/cloudflare)
|
||||
- :icon{name="logos:cloudflare" class="h-5 w-4 inline mb-2"} [Cloudflare](https://nitro.unjs.io/deploy/providers/cloudflare)
|
||||
- :icon{name="logos:digital-ocean" class="h-5 w-4 inline mb-2"} [DigitalOcean](https://nitro.unjs.io/deploy/providers/digitalocean)
|
||||
- :icon{name="logos:firebase" class="h-5 w-4 inline mb-2"} [Firebase](https://nitro.unjs.io/deploy/providers/firebase)
|
||||
- :icon{name="logos:heroku-icon" class="h-5 w-4 inline mb-2"} [heroku](https://nitro.unjs.io/deploy/providers/heroku)
|
||||
- :icon{name="ph:cloud-duotone" class="h-5 w-4 inline mb-2"} [Edgio](https://nitro.unjs.io/deploy/providers/edgio)
|
||||
- :icon{name="logos:firebase" class="h-5 w-4 inline mb-2"} [Firebase](https://nitro.unjs.io/deploy/providers/firebase)
|
||||
- :icon{name="logos:heroku-icon" class="h-5 w-4 inline mb-2"} [Heroku](https://nitro.unjs.io/deploy/providers/heroku)
|
||||
- :icon{name="IconLagon" class="h-5 w-4 inline mb-2 text-black dark:text-white"} [Lagon](https://nitro.unjs.io/deploy/providers/lagon)
|
||||
- :icon{name="logos:netlify" class="h-5 w-4 inline mb-2"} [Netlify](https://nitro.unjs.io/deploy/providers/netlify)
|
||||
- :icon{name="simple-icons:render" class="h-5 w-4 inline mb-2"} [Render](https://nitro.unjs.io/deploy/providers/render)
|
||||
- :icon{name="ph:cloud-duotone" class="h-5 w-4 inline mb-2"} [Stormkit](https://nitro.unjs.io/deploy/providers/stormkit)
|
||||
- :icon{name="simple-icons:vercel" class="h-5 w-4 inline mb-2 text-black dark:text-white"} [Vercel](https://nitro.unjs.io/deploy/providers/vercel)
|
||||
|
||||
## CDN Proxy
|
||||
|
||||
In most cases, Nuxt can work with third-party content that is not generated or created by Nuxt itself. But sometimes such content can cause problems, especially Cloudflare's "Minification and Security Options".
|
||||
|
||||
Accordingly, you should make sure that the following options are unchecked / disabled in Cloudflare. Otherwise, unnecessary re-rendering or hydration errors could impact your production application.
|
||||
|
||||
1. Speed > Optimization > Auto Minify: Uncheck JavaScript, CSS and HTML
|
||||
2. Speed > Optimization > Disable "Rocket Loader™"
|
||||
3. Speed > Optimization > Disable "Mirage"
|
||||
4. Scrape Shield > Disable "Email Address Obfuscation"
|
||||
5. Scrape Shield > Disable "Server-side Excludes"
|
||||
|
||||
With these settings, you can be sure that Cloudflare won't inject scripts into your Nuxt application that may cause unwanted side effects.
|
||||
|
@ -84,6 +84,13 @@ Whether to launch a server to respond to requests in the test suite.
|
||||
* Type: `boolean`
|
||||
* Default: `true`
|
||||
|
||||
#### `port`
|
||||
|
||||
If provided, set the launched test server port to the value.
|
||||
|
||||
* Type: `number | undefined`
|
||||
* Default: `undefined`
|
||||
|
||||
#### `build`
|
||||
|
||||
Whether to run a separate build step.
|
||||
|
@ -35,7 +35,7 @@ Static sites | ✅ | ✅ | ✅
|
||||
The migration guide provides a step-by-step comparison of Nuxt 2 features to Nuxt 3 features and guidance to adapt your current application.
|
||||
|
||||
::alert{type=info}
|
||||
👉 Checkout the [**migration guide from From Nuxt 2 to Nuxt 3**](/docs/migration/overview).
|
||||
👉 Check out the [**guide to migrating from Nuxt 2 to Nuxt 3**](/docs/migration/overview).
|
||||
::
|
||||
|
||||
::alert{type=info}
|
||||
@ -52,7 +52,7 @@ If you prefer to progressively migrate your Nuxt 2 application to Nuxt 3, you ca
|
||||
|
||||
## Upgrading Nuxt 3
|
||||
|
||||
### Latest release
|
||||
### Latest Release
|
||||
|
||||
To upgrade Nuxt 3 to the [latest release](/docs/community/changelog), use the `nuxi upgrade` command.
|
||||
|
||||
@ -60,8 +60,8 @@ To upgrade Nuxt 3 to the [latest release](/docs/community/changelog), use the `n
|
||||
npx nuxi upgrade
|
||||
```
|
||||
|
||||
### Edge release channel
|
||||
### Nightly Release Channel
|
||||
|
||||
::alert{type=info icon=👉}
|
||||
To use the latest Nuxt 3 build and test features before their release, read the [edge release channel](/docs/guide/going-further/edge-channel) guide.
|
||||
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.
|
||||
::
|
||||
|
@ -30,11 +30,11 @@ Start with one of our starters and themes directly by opening [nuxt.new](https:/
|
||||
::alert
|
||||
::details
|
||||
:summary[Additional notes for an optimal setup:]
|
||||
- **Node.js**: Make sure to use an even numbered version (16, 18, etc)
|
||||
|
||||
- **Node.js**: Make sure to use an even numbered version (18, 20, etc)
|
||||
- **Nuxtr**: Install the community-developed [Nuxtr extension](https://marketplace.visualstudio.com/items?itemName=Nuxtr.nuxtr-vscode)
|
||||
- **Volar**: Either enable [**Take Over Mode**](https://vuejs.org/guide/typescript/overview.html#volar-takeover-mode) (recommended) or add the [TypeScript Vue Plugin](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin)
|
||||
|
||||
If you have enabled **Take Over Mode** or installed the **TypeScript Vue Plugin (Volar)**, you can disable generating the shim for `*.vue` files in your `nuxt.config.ts` file:
|
||||
If you have enabled **Take Over Mode** or installed the **TypeScript Vue Plugin (Volar)**, you can disable generating the shim for `*.vue` files in your [`nuxt.config.ts`](/docs/guide/directory-structure/nuxt.config) file:
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
@ -52,11 +52,15 @@ Open a terminal (if you're using [Visual Studio Code](https://code.visualstudio.
|
||||
::code-group
|
||||
|
||||
```bash [npx]
|
||||
npx nuxi init <project-name>
|
||||
npx nuxi@latest init <project-name>
|
||||
```
|
||||
|
||||
```bash [pnpm]
|
||||
pnpm dlx nuxi init <project-name>
|
||||
pnpm dlx nuxi@latest init <project-name>
|
||||
```
|
||||
|
||||
```bash [bun]
|
||||
bunx nuxi@latest init <project-name>
|
||||
```
|
||||
|
||||
::
|
||||
@ -84,6 +88,10 @@ npm install
|
||||
pnpm install
|
||||
```
|
||||
|
||||
```bash [bun]
|
||||
bun install
|
||||
```
|
||||
|
||||
::
|
||||
|
||||
## Development Server
|
||||
@ -104,6 +112,10 @@ npm run dev -- -o
|
||||
pnpm dev -o
|
||||
```
|
||||
|
||||
```bash [bun]
|
||||
bun run dev -o
|
||||
```
|
||||
|
||||
::
|
||||
|
||||
::alert{type=success icon=✨ .font-bold}
|
||||
|
@ -9,7 +9,7 @@ By default, Nuxt is configured to cover most use cases. The [`nuxt.config.ts`](/
|
||||
|
||||
## Nuxt Configuration
|
||||
|
||||
The `nuxt.config.ts` file is located at the root of a Nuxt project and can override or extend the application's behavior.
|
||||
The [`nuxt.config.ts`](/docs/guide/directory-structure/nuxt.config) file is located at the root of a Nuxt project and can override or extend the application's behavior.
|
||||
|
||||
A minimal configuration file exports the `defineNuxtConfig` function containing an object with your configuration. The `defineNuxtConfig` helper is globally available without import.
|
||||
|
||||
@ -29,6 +29,27 @@ Every configuration option is described in the [Configuration Reference](/docs/a
|
||||
You don't have to use TypeScript to build an application with Nuxt. However, it is strongly recommended to use the `.ts` extension for the `nuxt.config` file. This way you can benefit from hints in your IDE to avoid typos and mistakes while editing your configuration.
|
||||
::
|
||||
|
||||
### Environment overrides
|
||||
|
||||
You can configure fully typed, per-environment overrides in your nuxt.config
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
$production: {
|
||||
routeRules: {
|
||||
'/**': { isr: true }
|
||||
}
|
||||
},
|
||||
$development: {
|
||||
//
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
::alert{type=info}
|
||||
If you're authoring layers, you can also use the `$meta` key to provide metadata that you or the consumers of your layer might use.
|
||||
::
|
||||
|
||||
### Environment Variables and Private Tokens
|
||||
|
||||
The `runtimeConfig` API exposes values like environment variables to the rest of your application. By default, these keys are only available server-side. The keys within `runtimeConfig.public` are also available client-side.
|
||||
@ -60,7 +81,7 @@ NUXT_API_SECRET=api_secret_token
|
||||
These variables are exposed to the rest of your application using the [`useRuntimeConfig`](/docs/api/composables/use-runtime-config) composable.
|
||||
|
||||
```vue [pages/index.vue]
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
const runtimeConfig = useRuntimeConfig()
|
||||
</script>
|
||||
```
|
||||
@ -88,7 +109,7 @@ export default defineAppConfig({
|
||||
These variables are exposed to the rest of your application using the [`useAppConfig`](/docs/api/composables/use-app-config) composable.
|
||||
|
||||
```vue [pages/index.vue]
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
const appConfig = useAppConfig()
|
||||
</script>
|
||||
```
|
||||
@ -114,7 +135,7 @@ Non primitive JS types | ❌ No | ✅ Yes
|
||||
|
||||
## External Configuration Files
|
||||
|
||||
Nuxt uses `nuxt.config.ts` file as the single source of trust for configurations and skips reading external configuration files. During the course of building your project, you may have a need to configure those. The following table highlights common configurations and, where applicable, how they can be configured with Nuxt.
|
||||
Nuxt uses [`nuxt.config.ts`](/docs/guide/directory-structure/nuxt.config) file as the single source of trust for configurations and skips reading external configuration files. During the course of building your project, you may have a need to configure those. The following table highlights common configurations and, where applicable, how they can be configured with Nuxt.
|
||||
|
||||
Name | Config File | How To Configure
|
||||
|---------------------------------------------|---------------------------|-------------------------
|
||||
@ -131,5 +152,62 @@ Name | Config File | How To
|
||||
| [ESLint](https://eslint.org) | `.eslintrc.js` | [More Info](https://eslint.org/docs/latest/user-guide/configuring/configuration-files)
|
||||
| [Prettier](https://prettier.io) | `.prettierrc.json` | [More Info](https://prettier.io/docs/en/configuration.html)
|
||||
| [Stylelint](https://stylelint.io) | `.stylelintrc.json` | [More Info](https://stylelint.io/user-guide/configure)
|
||||
| [TailwindCSS](https://tailwindcss.com) | `tailwind.config.js` | [More Info](https://tailwindcss.nuxt.dev/tailwind/config/)
|
||||
| [TailwindCSS](https://tailwindcss.com) | `tailwind.config.js` | [More Info](https://tailwindcss.nuxtjs.org/tailwind/config/)
|
||||
| [Vitest](https://vitest.dev) | `vitest.config.ts` | [More Info](https://vitest.dev/config/)
|
||||
|
||||
## Vue Configuration
|
||||
|
||||
### With Vite
|
||||
|
||||
If you need to pass options to `@vitejs/plugin-vue` or `@vitejs/plugin-vue-jsx`, you can do this in your `nuxt.config` file.
|
||||
|
||||
- `vite.vue` for `@vitejs/plugin-vue`. Check available options [here](https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue).
|
||||
- `vite.vueJsx` for `@vitejs/plugin-vue-jsx`. Check available options [here](https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue-jsx).
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
vite: {
|
||||
vue: {
|
||||
customElement: true
|
||||
},
|
||||
vueJsx: {
|
||||
mergeProps: true
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
:ReadMore{link="/docs/guide/directory-structure/nuxt.config#vue"}
|
||||
|
||||
### With webpack
|
||||
|
||||
If you use webpack and need to configure `vue-loader`, you can do this using `webpack.loaders.vue` key inside your `nuxt.config` file. The available options are [defined here](https://github.com/vuejs/vue-loader/blob/main/src/index.ts#L32-L62).
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
webpack: {
|
||||
loaders: {
|
||||
vue: {
|
||||
hotReload: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
:ReadMore{link="/docs/guide/directory-structure/nuxt.config#loaders"}
|
||||
|
||||
### Enabling Experimental Vue Features
|
||||
|
||||
You may need to enable experimental features in Vue, such as `defineModel` or `propsDestructure`. Nuxt provides an easy way to do that in `nuxt.config.ts`, no matter which builder you are using:
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
vue: {
|
||||
defineModel: true,
|
||||
propsDestructure: true
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
:ReadMore{link="/docs/guide/directory-structure/nuxt.config#vue-1"}
|
||||
|
@ -28,7 +28,7 @@ If you are familiar with Vue, you might wonder where `main.js` is (the file that
|
||||
|
||||
![Components are reusable pieces of UI](/assets/docs/getting-started/views/components.svg)
|
||||
|
||||
Most components are reusable pieces of the user interface, like buttons and menus. In Nuxt, you can create these components in the `components/` directory, and they will be automatically available across your application without having to explicitly import them.
|
||||
Most components are reusable pieces of the user interface, like buttons and menus. In Nuxt, you can create these components in the [`components/` directory](/docs/guide/directory-structure/components), and they will be automatically available across your application without having to explicitly import them.
|
||||
|
||||
::code-group
|
||||
|
||||
@ -57,9 +57,9 @@ Most components are reusable pieces of the user interface, like buttons and menu
|
||||
|
||||
![Pages are views tied to a specific route](/assets/docs/getting-started/views/pages.svg)
|
||||
|
||||
Pages represent views for each specific route pattern. Every file in the `pages/` directory represents a different route displaying its content.
|
||||
Pages represent views for each specific route pattern. Every file in the [`pages/` directory](/docs/guide/directory-structure/pages) represents a different route displaying its content.
|
||||
|
||||
To use pages, create `pages/index.vue` file and add `<NuxtPage />` component to the `app.vue` (or remove `app.vue` for default entry). You can now create more pages and their corresponding routes by adding new files in the `pages/` directory.
|
||||
To use pages, create `pages/index.vue` file and add `<NuxtPage />` component to the `app.vue` (or remove `app.vue` for default entry). You can now create more pages and their corresponding routes by adding new files in the [`pages/` directory](/docs/guide/directory-structure/pages).
|
||||
|
||||
::code-group
|
||||
|
||||
@ -132,3 +132,26 @@ If you only have a single layout in your application, we recommend using app.vue
|
||||
::
|
||||
|
||||
If you want to create more layouts and learn how to use them in your pages, find more information in the [Layouts section](/docs/guide/directory-structure/layouts).
|
||||
|
||||
## Advanced: Extending the HTML template
|
||||
|
||||
::alert{type=info}
|
||||
If you only need to modify the head, you can refer to the [SEO and meta section](/docs/getting-started/seo-meta).
|
||||
::
|
||||
|
||||
You can have full control over the HTML template by adding a Nitro plugin that registers a hook.
|
||||
The callback function of the `render:html` hook allows you to mutate the HTML before it is sent to the client.
|
||||
|
||||
```ts [server/plugins/extend-html.ts]
|
||||
export default defineNitroPlugin((nitroApp) => {
|
||||
nitroApp.hooks.hook('render:html', (html, { event }) => {
|
||||
// This will be an object representation of the html template.
|
||||
console.log(html)
|
||||
html.head.push(`<meta name="description" content="My custom description" />`)
|
||||
})
|
||||
// You can also intercept the response here.
|
||||
nitroApp.hooks.hook('render:response', (response, { event }) => { console.log(response) })
|
||||
})
|
||||
```
|
||||
|
||||
:ReadMore{link="/docs/guide/going-further/hooks"}
|
||||
|
@ -13,7 +13,7 @@ Nuxt uses two directories to handle assets like stylesheets, fonts or images.
|
||||
|
||||
The [`public/` directory](/docs/guide/directory-structure/public) is used as a public server for static assets publicly available at a defined URL of your application.
|
||||
|
||||
You can get a file in the `public/` directory from your application's code or from a browser by the root URL `/`.
|
||||
You can get a file in the [`public/` directory](/docs/guide/directory-structure/public) from your application's code or from a browser by the root URL `/`.
|
||||
|
||||
### Example
|
||||
|
||||
@ -29,9 +29,9 @@ For example, referencing an image file in the `public/img/` directory, available
|
||||
|
||||
Nuxt uses [Vite](https://vitejs.dev/guide/assets.html) or [webpack](https://webpack.js.org/guides/asset-management/) to build and bundle your application. The main function of these build tools is to process JavaScript files, but they can be extended through [plugins](https://vitejs.dev/plugins/) (for Vite) or [loaders](https://webpack.js.org/loaders/) (for webpack) to process other kind of assets, like stylesheets, fonts or SVG. This step transforms the original file mainly for performance or caching purposes (such as stylesheets minification or browser cache invalidation).
|
||||
|
||||
By convention, Nuxt uses the `assets/` directory to store these files but there is no auto-scan functionality for this directory, and you can use any other name for it.
|
||||
By convention, Nuxt uses the [`assets/` directory](/docs/guide/directory-structure/assets) to store these files but there is no auto-scan functionality for this directory, and you can use any other name for it.
|
||||
|
||||
In your application's code, you can reference a file located in the `assets/` directory by using the `~/assets/` path.
|
||||
In your application's code, you can reference a file located in the [`assets/` directory](/docs/guide/directory-structure/assets) by using the `~/assets/` path.
|
||||
|
||||
### Example
|
||||
|
||||
@ -44,7 +44,7 @@ For example, referencing an image file that will be processed if a build tool is
|
||||
```
|
||||
|
||||
::alert{type=info icon=💡}
|
||||
Nuxt won't serve files in the `assets/` directory at a static URL like `/assets/my-file.png`. If you need a static URL, use the [`public/` directory](#public-directory).
|
||||
Nuxt won't serve files in the [`assets/` directory](/docs/guide/directory-structure/assets) at a static URL like `/assets/my-file.png`. If you need a static URL, use the [`public/` directory](#public-directory).
|
||||
::
|
||||
|
||||
### Global Styles Imports
|
||||
|
529
docs/1.getting-started/4.styling.md
Normal file
529
docs/1.getting-started/4.styling.md
Normal file
@ -0,0 +1,529 @@
|
||||
---
|
||||
navigation.icon: uil:palette
|
||||
---
|
||||
|
||||
# Styling
|
||||
|
||||
Nuxt is highly flexible when it comes to styling. Write your own styles, or reference local and external stylesheets.
|
||||
You can use CSS preprocessors, CSS frameworks, UI libraries and Nuxt modules to style your application.
|
||||
|
||||
## Local Stylesheets
|
||||
|
||||
If you're writing local stylesheets, the natural place to put them is the [`assets/` directory](/docs/guide/directory-structure/assets).
|
||||
|
||||
### Importing Within Components
|
||||
|
||||
You can import stylesheets in your pages, layouts and components directly.
|
||||
You can use a javascript import, or a css [`@import` statement](https://developer.mozilla.org/en-US/docs/Web/CSS/@import).
|
||||
|
||||
```vue [pages/index.vue]
|
||||
<script>
|
||||
// Use a static import for server-side compatibility
|
||||
import '~/assets/css/first.css'
|
||||
|
||||
// Caution: Dynamic imports are not server-side compatible
|
||||
import('~/assets/css/first.css')
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import url("~/assets/css/second.css");
|
||||
</style>
|
||||
```
|
||||
|
||||
::alert{type=info}
|
||||
The stylesheets will be inlined in the HTML rendered by Nuxt.
|
||||
::
|
||||
|
||||
### The CSS Property
|
||||
|
||||
You can also use the `css` property in the Nuxt configuration.
|
||||
The natural place for your stylesheets is the [`assets/` directory](/docs/guide/directory-structure/assets). You can then reference its path and Nuxt will include it to all the pages of your application.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
css: ['~/assets/css/main.css']
|
||||
})
|
||||
```
|
||||
|
||||
::alert{type=info}
|
||||
The stylesheets will be inlined in the HTML rendered by Nuxt, injected globally and present in all pages.
|
||||
::
|
||||
|
||||
### Working With Fonts
|
||||
|
||||
Place your local fonts files in your `~/public/` directory, for example in `~/public/fonts`. You can then reference them in your stylesheets using `url()`.
|
||||
|
||||
```css [assets/css/main.css]
|
||||
@font-face {
|
||||
font-family: 'FarAwayGalaxy';
|
||||
src: url('/fonts/FarAwayGalaxy.woff') format('woff');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
```
|
||||
|
||||
Then reference your fonts by name in your stylesheets, pages or components:
|
||||
|
||||
```vue
|
||||
<style>
|
||||
h1 {
|
||||
font-family: 'FarAwayGalaxy', sans-serif;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
### Stylesheets Distributed Through NPM
|
||||
|
||||
You can also reference stylesheets that are distributed through npm. Let's use the popular `animate.css` library as an example.
|
||||
|
||||
```bash
|
||||
npm install animate.css
|
||||
```
|
||||
|
||||
Then you can reference it directly in your pages, layouts and components:
|
||||
|
||||
```vue [app.vue]
|
||||
<script>
|
||||
import 'animate.css'
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import url("animate.css");
|
||||
</style>
|
||||
```
|
||||
|
||||
The package can also be referenced as a string in the css property of your Nuxt configuration.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
css: ['animate.css']
|
||||
})
|
||||
```
|
||||
|
||||
## External Stylesheets
|
||||
|
||||
You can include external stylesheets in your application by adding a link element in the head section of your nuxt.config file. You can achieve this result using different methods. Note that local stylesheets can also be included like this.
|
||||
|
||||
You can manipulate the head with the [`app.head`](/docs/api/configuration/nuxt-config#head) property of your Nuxt configuration:
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
app: {
|
||||
head: {
|
||||
link: [{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css' }]
|
||||
}
|
||||
}})
|
||||
```
|
||||
|
||||
### Dynamically Adding Stylesheets
|
||||
|
||||
You can use the useHead composable to dynamically set a value in your head in your code.
|
||||
|
||||
::ReadMore{link="/docs/api/composables/use-head"}
|
||||
::
|
||||
|
||||
```ts
|
||||
useHead({
|
||||
link: [{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css' }]
|
||||
})
|
||||
```
|
||||
|
||||
Nuxt uses `unhead` under the hood, and you can refer to its full documentation [here](https://unhead.unjs.io/).
|
||||
|
||||
### Modifying The Rendered Head With A Nitro Plugin
|
||||
|
||||
If you need more advanced control, you can intercept the rendered html with a hook and modify the head programmatically.
|
||||
|
||||
Create a plugin in `~/server/plugins/my-plugin.ts` like this:
|
||||
|
||||
```ts [server/plugins/my-plugin.ts]
|
||||
export default defineNitroPlugin((nitro) => {
|
||||
nitro.hooks.hook('render:html', (html) => {
|
||||
html.head.push('<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
External stylesheets are render-blocking resources: they must be loaded and processed before the browser renders the page. Web pages that contain unnecessarily large styles take longer to render. You can read more about it on [web.dev](https://web.dev/defer-non-critical-css/).
|
||||
|
||||
## Using Preprocessors
|
||||
|
||||
To use a preprocessor like SCSS, Sass, Less or Stylus, install it first.
|
||||
|
||||
::code-group
|
||||
|
||||
```bash [Sass & SCSS]
|
||||
npm install sass
|
||||
```
|
||||
|
||||
```bash [Less]
|
||||
npm install less
|
||||
```
|
||||
|
||||
```bash [Stylus]
|
||||
npm install stylus
|
||||
```
|
||||
|
||||
::
|
||||
|
||||
The natural place to write your stylesheets is the `assets` directory.
|
||||
You can then import your source files in your `app.vue` (or layouts files) using your preprocessor's syntax.
|
||||
|
||||
```vue [pages/app.vue]
|
||||
<style lang="scss">
|
||||
@use "~/assets/scss/main.scss";
|
||||
</style>
|
||||
```
|
||||
|
||||
Alternatively, you can use the `css` property of your Nuxt configuration.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
css: ['~/assets/scss/main.scss']
|
||||
})
|
||||
```
|
||||
|
||||
::alert{type=info}
|
||||
In both cases, the compiled stylesheets will be inlined in the HTML rendered by Nuxt.
|
||||
::
|
||||
|
||||
If you need to inject code in pre-processed files, like a [sass partial](https://sass-lang.com/documentation/at-rules/use#partials) with color variables, you can do so with the vite [preprocessors options](https://vitejs.dev/config/shared-options.html#css-preprocessoroptions).
|
||||
|
||||
Create some partials in your `assets` directory:
|
||||
|
||||
::code-group
|
||||
|
||||
```scss [assets/_colors.scss]
|
||||
$primary: #49240F;
|
||||
$secondary: #E4A79D;
|
||||
```
|
||||
|
||||
```sass [assets/_colors.sass]
|
||||
$primary: #49240F
|
||||
$secondary: #E4A79D
|
||||
```
|
||||
|
||||
::
|
||||
|
||||
Then in your `nuxt.config` :
|
||||
|
||||
::code-group
|
||||
|
||||
```ts [SCSS]
|
||||
export default defineNuxtConfig({
|
||||
vite: {
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
additionalData: '@use "@/assets/_colors.scss" as *;'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
```ts [SASS]
|
||||
export default defineNuxtConfig({
|
||||
vite: {
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
sass: {
|
||||
additionalData: '@use "@/assets/_colors.sass" as *\n'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
::
|
||||
|
||||
Nuxt uses Vite by default. If you wish to use webpack instead, refer to each preprocessor loader [documentation](https://webpack.js.org/loaders/sass-loader/).
|
||||
|
||||
## Single File Components (SFC) Styling
|
||||
|
||||
One of the best thing about Vue and SFC is how great it is at naturally dealing with styling. You can directly write CSS or preprocessor code in the style block of your components file, therefore you will have fantastic developer experience without having to use something like CSS-in-JS. However if you wish to use CSS-in-JS, you can find 3rd party libraries and modules that support it, such as [pinceau](https://pinceau.dev/).
|
||||
|
||||
You can refer to the [Vue docs](https://vuejs.org/api/sfc-css-features.html) for a comprehensive reference about styling components in SFC.
|
||||
|
||||
### Class And Style Bindings
|
||||
|
||||
You can leverage Vue SFC features to style your components with class and style attributes.
|
||||
|
||||
::code-group
|
||||
|
||||
```vue [Ref and Reactive]
|
||||
<script setup lang="ts">
|
||||
const isActive = ref(true)
|
||||
const hasError = ref(false)
|
||||
const classObject = reactive({
|
||||
active: true,
|
||||
'text-danger': false
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="static" :class="{ active: isActive, 'text-danger': hasError }"></div>
|
||||
<div :class="classObject"></div>
|
||||
</template>
|
||||
```
|
||||
|
||||
```vue [Computed]
|
||||
<script setup lang="ts">
|
||||
const isActive = ref(true)
|
||||
const error = ref(null)
|
||||
|
||||
const classObject = computed(() => ({
|
||||
active: isActive.value && !error.value,
|
||||
'text-danger': error.value && error.value.type === 'fatal'
|
||||
}))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="classObject"></div>
|
||||
</template>
|
||||
```
|
||||
|
||||
```vue [Array]
|
||||
<script setup lang="ts">
|
||||
const isActive = ref(true)
|
||||
const errorClass = ref('text-danger')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="[{ active: isActive }, errorClass]"></div>
|
||||
</template>
|
||||
```
|
||||
|
||||
```vue [Style]
|
||||
<script setup lang="ts">
|
||||
const activeColor = ref('red')
|
||||
const fontSize = ref(30)
|
||||
const styleObject = reactive({ color: 'red', fontSize: '13px' })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
|
||||
<div :style="[baseStyles, overridingStyles]"></div>
|
||||
<div :style="styleObject"></div>
|
||||
</template>
|
||||
```
|
||||
|
||||
::
|
||||
|
||||
Refer to the [Vue docs](https://vuejs.org/guide/essentials/class-and-style.html) for more information.
|
||||
|
||||
### Dynamic Styles With `v-bind`
|
||||
|
||||
You can reference JavaScript variable and expression within your style blocks with the v-bind function.
|
||||
The binding will be dynamic, meaning that if the variable value changes, the style will be updated.
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
const color = ref("red")
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="text">hello</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.text {
|
||||
color: v-bind(color);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
### Scoped Styles
|
||||
|
||||
The scoped attribute allows you to style components in isolation. The styles declared with this attribute will only apply to this component.
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="example">hi</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.example {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
### CSS Modules
|
||||
|
||||
You can use [CSS Modules](https://github.com/css-modules/css-modules) with the module attribute. Access it with the injected `$style` variable.
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<p :class="$style.red">This should be red</p>
|
||||
</template>
|
||||
|
||||
<style module>
|
||||
.red {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
### Preprocessors Support
|
||||
|
||||
SFC style blocks support preprocessors syntax. Vite come with built-in support for .scss, .sass, .less, .styl and .stylus files without configuration. You just need to install them first, and they will be available directly in SFC with the lang attribute.
|
||||
|
||||
::code-group
|
||||
|
||||
```vue [SCSS]
|
||||
<style lang="scss">
|
||||
/* Write scss here */
|
||||
</style>
|
||||
```
|
||||
|
||||
```vue [Sass]
|
||||
<style lang="sass">
|
||||
/* Write sass here */
|
||||
</style>
|
||||
```
|
||||
|
||||
```vue [LESS]
|
||||
<style lang="less">
|
||||
/* Write less here */
|
||||
</style>
|
||||
```
|
||||
|
||||
```vue [Stylus]
|
||||
<style lang="stylus">
|
||||
/* Write stylus here */
|
||||
</style>
|
||||
```
|
||||
|
||||
::
|
||||
|
||||
You can refer to the [Vite CSS docs](https://vitejs.dev/guide/features.html#css) and the [@vitejs/plugin-vue docs](https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue).
|
||||
For webpack users, refer to the [vue loader docs](https://vue-loader.vuejs.org/).
|
||||
|
||||
## Using PostCSS
|
||||
|
||||
Nuxt comes with postcss built-in. You can configure it in your `nuxt.config` file.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
postcss: {
|
||||
plugins: {
|
||||
'postcss-nested': {}
|
||||
"postcss-custom-media": {}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
For proper syntax highlighting in SFC, you can use the postcss lang attribute.
|
||||
|
||||
```vue
|
||||
<style lang="postcss">
|
||||
/* Write stylus here */
|
||||
</style>
|
||||
```
|
||||
|
||||
By default, Nuxt comes with the following plugins already pre-configured:
|
||||
|
||||
- [postcss-import](https://github.com/postcss/postcss-import): Improves the `@import` rule
|
||||
- [postcss-url](https://github.com/postcss/postcss-url): Transforms `url()` statements
|
||||
- [autoprefixer](https://github.com/postcss/autoprefixer): Automatically adds vendor prefixes
|
||||
- [cssnano](https://cssnano.co/): Minification and purge
|
||||
|
||||
## Leveraging Layouts For Multiple Styles
|
||||
|
||||
If you need to style different parts of your application completely differently, you can use layouts.
|
||||
Use different styles for different layouts.
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="default-layout">
|
||||
<h1>Default Layout</h1>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.default-layout {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
::ReadMore{link="/docs/guide/directory-structure/layouts"}
|
||||
::
|
||||
|
||||
## Third Party Libraries And Modules
|
||||
|
||||
Nuxt isn't opinionated when it comes to styling and provides you with a wide variety of options. You can use any styling tool that you want, such as popular libraries like [UnoCSS](https://unocss.dev/) or [Tailwind CSS](https://tailwindcss.com/).
|
||||
|
||||
The community and the Nuxt team have developed plenty of Nuxt modules to makes the integration easier.
|
||||
You can discover them on the [modules section](https://nuxt.com/modules) of the website.
|
||||
Here are a few modules to help you get started:
|
||||
|
||||
- [UnoCSS](https://nuxt.com/modules/unocss): Instant on-demand atomic CSS engine
|
||||
- [Tailwind CSS](https://nuxt.com/modules/tailwindcss): Utility-first CSS framework
|
||||
- [Fontaine](https://github.com/nuxt-modules/fontaine): Font metric fallback
|
||||
- [Pinceau](https://pinceau.dev/): Adaptable styling framework
|
||||
- [Nuxt UI](https://ui.nuxt.com): A UI Library for Modern Web Apps
|
||||
|
||||
Nuxt modules provide you with a good developer experience out of the box, but remember that if your favorite tool doesn't have a module, it doesn't mean that you can't use it with Nuxt! You can configure it yourself for your own project. Depending on the tool, you might need to use a [Nuxt plugin](/docs/guide/directory-structure/plugins) and/or [make your own module](/docs/guide/going-further/modules). Share them with the [community](https://nuxt.com/modules) if you do!
|
||||
|
||||
### Easily Load Webfonts
|
||||
|
||||
You can use [the Nuxt Google Fonts module](https://github.com/nuxt-modules/google-fonts) to load Google Fonts.
|
||||
|
||||
If you are using [UnoCSS](https://unocss.dev/integrations/nuxt), note that it comes with a [web fonts presets](https://unocss.dev/presets/web-fonts) to conveniently load fonts from common providers, including Google Fonts and more.
|
||||
|
||||
## Advanced
|
||||
|
||||
### Transitions
|
||||
|
||||
Nuxt comes with the same `<Transition>` element that Vue has, and also has support for the experimental [View Transitions API](/docs/getting-started/transitions#view-transitions-api-experimental).
|
||||
|
||||
::ReadMore{link="/docs/features/transitions"}
|
||||
::
|
||||
|
||||
### Font Advanced Optimization
|
||||
|
||||
We would recommend using [Fontaine](https://github.com/nuxt-modules/fontaine) to reduce your [CLS](https://web.dev/cls/). If you need something more advanced, consider creating a Nuxt module to extend the build process or the Nuxt runtime.
|
||||
|
||||
::alert{type="info"}
|
||||
Always remember to take advantage of the various tools and techniques available in the Web ecosystem at large to make styling your application easier and more efficient. Whether you're using native CSS, a preprocessor, postcss, a UI library or a module, Nuxt has got you covered. Happy styling!
|
||||
::
|
||||
|
||||
### LCP Advanced optimizations
|
||||
|
||||
You can do the following to speed-up the download of your global CSS files:
|
||||
|
||||
- Use a CDN so the files are physically closer to your users
|
||||
- Compress your assets, ideally using Brotli
|
||||
- Use HTTP2/HTTP3 for delivery
|
||||
- Host your assets on the same domain (do not use a different subdomain)
|
||||
|
||||
Most of these things should be done for you automatically if you're using modern platforms like Cloudflare, Netlify or Vercel.
|
||||
You can find an LCP optimization guide on [web.dev](https://web.dev/optimize-lcp/).
|
||||
|
||||
If all of your CSS is inlined by Nuxt, you can (experimentally) completely stop external CSS files from being referenced in your rendered HTML.
|
||||
You can achieve that with a hook, that you can place in a module, or in your Nuxt configuration file.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
hooks: {
|
||||
'build:manifest': (manifest) => {
|
||||
// find the app entry, css list
|
||||
const css = manifest['node_modules/nuxt/dist/app/entry.js']?.css
|
||||
if (css) {
|
||||
// start from the end of the array and go to the beginning
|
||||
for (let i = css.length - 1; i >= 0; i--) {
|
||||
// if it starts with 'entry', remove it from the list
|
||||
if (css[i].startsWith('entry')) css.splice(i, 1)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
@ -4,11 +4,11 @@ description: Nuxt file-system routing creates a route for every file in the page
|
||||
---
|
||||
# Routing
|
||||
|
||||
One core feature of Nuxt is the file system router. Every Vue file inside the `pages/` directory creates a corresponding URL (or route) that displays the contents of the file. By using dynamic imports for each page, Nuxt leverages code-splitting to ship the minimum amount of JavaScript for the requested route.
|
||||
One core feature of Nuxt is the file system router. Every Vue file inside the [`pages/` directory](/docs/guide/directory-structure/pages) creates a corresponding URL (or route) that displays the contents of the file. By using dynamic imports for each page, Nuxt leverages code-splitting to ship the minimum amount of JavaScript for the requested route.
|
||||
|
||||
## Pages
|
||||
|
||||
Nuxt routing is based on [vue-router](https://router.vuejs.org/) and generates the routes from every component created in the [`pages/`](/docs/guide/directory-structure/pages) directory, based on their filename.
|
||||
Nuxt routing is based on [vue-router](https://router.vuejs.org/) and generates the routes from every component created in the [`pages/` directory](/docs/guide/directory-structure/pages), based on their filename.
|
||||
|
||||
This file system routing uses naming conventions to create dynamic and nested routes:
|
||||
|
||||
@ -17,6 +17,7 @@ This file system routing uses naming conventions to create dynamic and nested ro
|
||||
```text [pages/ directory]
|
||||
pages/
|
||||
--| about.vue
|
||||
--| index.vue
|
||||
--| posts/
|
||||
----| [id].vue
|
||||
```
|
||||
@ -28,6 +29,10 @@ pages/
|
||||
"path": "/about",
|
||||
"component": "pages/about.vue"
|
||||
},
|
||||
{
|
||||
"path": "/",
|
||||
"component": "pages/index.vue"
|
||||
},
|
||||
{
|
||||
"path": "/posts/:id",
|
||||
"component": "pages/posts/[id].vue"
|
||||
@ -66,8 +71,8 @@ When a `<NuxtLink>` enters the viewport on the client side, Nuxt will automatica
|
||||
|
||||
The `useRoute()` composable can be used in a `<script setup>` block or a `setup()` method of a Vue component to access the current route details.
|
||||
|
||||
```vue [pages/posts/[id].vue]
|
||||
<script setup>
|
||||
```vue [pages/posts/[id\\].vue]
|
||||
<script setup lang="ts">
|
||||
const route = useRoute()
|
||||
|
||||
// When accessing /posts/1, route.params.id will be 1
|
||||
@ -88,8 +93,8 @@ Route middleware runs within the Vue part of your Nuxt app. Despite the similar
|
||||
There are three kinds of route middleware:
|
||||
|
||||
1. Anonymous (or inline) route middleware, which are defined directly in the pages where they are used.
|
||||
2. Named route middleware, which are placed in the `middleware/` directory and will be automatically loaded via asynchronous import when used on a page. (**Note**: The route middleware name is normalized to kebab-case, so `someMiddleware` becomes `some-middleware`.)
|
||||
3. Global route middleware, which are placed in the `middleware/` directory (with a `.global` suffix) and will be automatically run on every route change.
|
||||
2. Named route middleware, which are placed in the [`middleware/` directory](/docs/guide/directory-structure/middleware) and will be automatically loaded via asynchronous import when used on a page. (**Note**: The route middleware name is normalized to kebab-case, so `someMiddleware` becomes `some-middleware`.)
|
||||
3. Global route middleware, which are placed in the [`middleware/` directory](/docs/guide/directory-structure/middleware) (with a `.global` suffix) and will be automatically run on every route change.
|
||||
|
||||
Example of an `auth` middleware protecting the `/dashboard` page:
|
||||
|
||||
@ -105,7 +110,7 @@ export default defineNuxtRouteMiddleware((to, from) => {
|
||||
```
|
||||
|
||||
```html [pages/dashboard.vue]
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
middleware: 'auth'
|
||||
})
|
||||
@ -128,8 +133,8 @@ The `validate` property accepts the `route` as an argument. You can return a boo
|
||||
|
||||
If you have a more complex use case, then you can use anonymous route middleware instead.
|
||||
|
||||
```vue [pages/posts/[id].vue]
|
||||
<script setup>
|
||||
```vue [pages/posts/[id\\].vue]
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
validate: async (route) => {
|
||||
// Check if the id is made up of digits
|
||||
|
@ -35,8 +35,8 @@ Shortcuts are available to make configuration easier: `charset` and `viewport`.
|
||||
|
||||
## `useHead`
|
||||
|
||||
The `useHead` composable function allows you to manage your head tags in a programmatic and reactive way,
|
||||
powered by [Unhead](https://unhead.harlanzw.com/).
|
||||
The [`useHead`](/docs/api/composables/use-head) composable function allows you to manage your head tags in a programmatic and reactive way,
|
||||
powered by [Unhead](https://unhead.unjs.io/).
|
||||
|
||||
As with all composables, it can only be used with a components `setup` and lifecycle hooks.
|
||||
|
||||
@ -57,19 +57,15 @@ useHead({
|
||||
|
||||
We recommend to take a look at the [`useHead`](/docs/api/composables/use-head) and [`useHeadSafe`](/docs/api/composables/use-head-safe) composables.
|
||||
|
||||
## `useSeoMeta`
|
||||
## `useSeoMeta` and `useServerSeoMeta`
|
||||
|
||||
The `useSeoMeta` and `useServerSeoMeta` composables let you define your site's SEO meta tags as a flat object with full TypeScript support.
|
||||
The `useSeoMeta` and [`useServerSeoMeta`](/docs/api/composables/use-server-seo-meta) composables let you define your site's SEO meta tags as a flat object with full TypeScript support.
|
||||
|
||||
This helps you avoid typos and common mistakes, such as using `name` instead of `property`.
|
||||
|
||||
In most instances, the meta does not need to be reactive as robots will only scan the initial load. So we recommend using `useServerSeoMeta` as a performance-focused utility that will not do anything (or return a `head` object) on the client.
|
||||
|
||||
**Simple example:**
|
||||
|
||||
```vue [app.vue]
|
||||
<script setup lang="ts">
|
||||
useServerSeoMeta({
|
||||
useSeoMeta({
|
||||
title: 'My Amazing Site',
|
||||
ogTitle: 'My Amazing Site',
|
||||
description: 'This is my amazing site, let me tell you all about it.',
|
||||
@ -80,22 +76,7 @@ useServerSeoMeta({
|
||||
</script>
|
||||
```
|
||||
|
||||
**Reactive example:**
|
||||
|
||||
When inserting tags that are reactive, you should use the computed getter syntax (`() => value`):
|
||||
|
||||
```vue [app.vue]
|
||||
<script setup lang="ts">
|
||||
const title = ref('My title')
|
||||
|
||||
useSeoMeta({
|
||||
title,
|
||||
description: () => `description: ${title.value}`
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
Read more on the [`useSeoMeta`](https://unhead.harlanzw.com/guide/composables/use-seo-meta) composable.
|
||||
Read more on the [`useSeoMeta`](/docs/api/composables/use-seo-meta) and [`useServerSeoMeta`](/docs/api/composables/use-server-seo-meta) composables.
|
||||
|
||||
## Components
|
||||
|
||||
@ -108,7 +89,7 @@ Because these component names match native HTML elements, it is very important t
|
||||
<!-- @case-police-ignore html -->
|
||||
|
||||
```vue [app.vue]
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
const title = ref('Hello World')
|
||||
</script>
|
||||
|
||||
@ -127,7 +108,7 @@ const title = ref('Hello World')
|
||||
|
||||
## Types
|
||||
|
||||
The below is the non-reactive types used for `useHead`, `app.head` and components.
|
||||
Below are the non-reactive types used for [`useHead`](/docs/api/composables/use-head), [`app.head`](/docs/api/configuration/nuxt-config#head) and components.
|
||||
|
||||
```ts
|
||||
interface MetaObject {
|
||||
@ -180,7 +161,7 @@ It's recommended to use getters (`() => value`) over computed (`computed(() => v
|
||||
```
|
||||
|
||||
```vue [Components]
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
const description = ref('My amazing site.')
|
||||
</script>
|
||||
|
||||
@ -215,7 +196,7 @@ If you want to use a function (for full control), then this cannot be set in you
|
||||
|
||||
::
|
||||
|
||||
Now, if you set the title to `My Page` with `useHead` on another page of your site, the title would appear as 'My Page - Site Title' in the browser tab. You could also pass `null` to default to the site title.
|
||||
Now, if you set the title to `My Page` with [`useHead`](/docs/api/composables/use-head) on another page of your site, the title would appear as 'My Page - Site Title' in the browser tab. You could also pass `null` to default to the site title.
|
||||
|
||||
### Body Tags
|
||||
|
||||
@ -241,12 +222,12 @@ useHead({
|
||||
|
||||
### With `definePageMeta`
|
||||
|
||||
Within your `pages/` directory, you can use `definePageMeta` along with `useHead` to set metadata based on the current route.
|
||||
Within your [`pages/` directory](/docs/guide/directory-structure/pages), you can use `definePageMeta` along with [`useHead`](/docs/api/composables/use-head) to set metadata based on the current route.
|
||||
|
||||
For example, you can first set the current page title (this is extracted at build time via a macro, so it can't be set dynamically):
|
||||
|
||||
```vue{}[pages/some-page.vue]
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
title: 'Some Page'
|
||||
})
|
||||
@ -256,7 +237,7 @@ definePageMeta({
|
||||
And then in your layout file, you might use the route's metadata you have previously set:
|
||||
|
||||
```vue{}[layouts/default.vue]
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
const route = useRoute()
|
||||
|
||||
useHead({
|
||||
@ -265,7 +246,7 @@ useHead({
|
||||
</script>
|
||||
```
|
||||
|
||||
:LinkExample{link="/docs/examples/composables/use-head"}
|
||||
:LinkExample{link="/docs/examples/features/meta-tags"}
|
||||
|
||||
:ReadMore{link="/docs/guide/directory-structure/pages/#page-metadata"}
|
||||
|
||||
@ -274,7 +255,7 @@ useHead({
|
||||
In the example below, `titleTemplate` is set either as a string with the `%s` placeholder or as a `function`, which allows greater flexibility in setting the page title dynamically for each route of your Nuxt app:
|
||||
|
||||
```vue [app.vue]
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
useHead({
|
||||
// as a string,
|
||||
// where `%s` is replaced with the title
|
||||
@ -293,7 +274,7 @@ useHead({
|
||||
|
||||
### External CSS
|
||||
|
||||
The example below shows how you might enable Google Fonts using either the `link` property of the `useHead` composable or using the `<Link>` component:
|
||||
The example below shows how you might enable Google Fonts using either the `link` property of the [`useHead`](/docs/api/composables/use-head) composable or using the `<Link>` component:
|
||||
|
||||
::code-group
|
||||
|
||||
|
@ -318,7 +318,7 @@ To apply dynamic transitions using conditional logic, you can leverage inline [m
|
||||
|
||||
::code-group
|
||||
|
||||
```html [pages/[id].vue]
|
||||
```html [pages/[id\\].vue]
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
pageTransition: {
|
||||
@ -408,3 +408,35 @@ When `<NuxtPage />` is used in `app.vue`, transition-props can be passed directl
|
||||
::alert{type="warning"}
|
||||
Remember, this page transition cannot be overridden with `definePageMeta` on individual pages.
|
||||
::
|
||||
|
||||
## View Transitions API (experimental)
|
||||
|
||||
Nuxt ships with an experimental implementation of the [**View Transitions API**](https://developer.chrome.com/docs/web-platform/view-transitions/) (see [MDN](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)). This is an exciting new way to implement native browser transitions which (among other things) have the ability to transition between unrelated elements on different pages.
|
||||
|
||||
The Nuxt integration is under active development, but can be enabled with the `experimental.viewTransition` option in your configuration file:
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
experimental: {
|
||||
viewTransition: true
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
If you are also using Vue transitions like `pageTransition` and `layoutTransition` (see above) to achieve the same result as the new View Transitions API, then you may wish to _disable_ Vue transitions if the user's browser supports the newer, native web API. You can do this by creating `~/middleware/disable-vue-transitions.global.ts` with the following contents:
|
||||
|
||||
```js
|
||||
export default defineNuxtRouteMiddleware(to => {
|
||||
if (!document.startViewTransition) { return }
|
||||
|
||||
// Disable built-in Vue transitions
|
||||
to.meta.pageTransition = false
|
||||
to.meta.layoutTransition = false
|
||||
})
|
||||
```
|
||||
|
||||
### Known issues
|
||||
|
||||
- View transitions may not work as expected with nested pages/layouts/async components owing to this upstream Vue bug: <https://github.com/vuejs/core/issues/5513>. If you make use of this pattern, you may need to delay adopting this experimental feature or implement it yourself. Feedback is very welcome.
|
||||
|
||||
- If you perform data fetching within your page setup functions, that you may wish to reconsider using this feature for the moment. (By design, View Transitions completely freeze DOM updates whilst they are taking place.) We're looking at restrict the View Transition to the final moments before `<Suspense>` resolves, but in the interim you may want to consider carefully whether to adopt this feature if this describes you.
|
||||
|
@ -3,27 +3,46 @@ navigation.icon: uil:channel
|
||||
description: Nuxt provides composables to handle data fetching within your application.
|
||||
---
|
||||
|
||||
# Data Fetching
|
||||
# Data fetching
|
||||
|
||||
Nuxt provides useFetch, useLazyFetch, useAsyncData and useLazyAsyncData to handle data fetching within your application.
|
||||
Nuxt comes with two composables and a built-in library to perform data-fetching in browser or server environments: `useFetch`, [`useAsyncData`](/docs/api/composables/use-async-data) and `$fetch` . In a nutshell:
|
||||
|
||||
- [`useFetch`](/docs/api/composables/use-fetch) is the most straightforward way to handle data fetching in a component setup function.
|
||||
- [`$fetch`](/docs/api/utils/dollarfetch) is great to make network requests based on user interaction.
|
||||
- [`useAsyncData`](/docs/api/composables/use-async-data), combined with `$fetch`, offers more fine-grained control.
|
||||
|
||||
Both `useFetch` and `useAsyncData` share a common set of options and patterns that we will detail in the last sections.
|
||||
|
||||
Before that, it's imperative to know why these composables exist in the first place.
|
||||
|
||||
## Why using specific composables?
|
||||
|
||||
When using a framework like Nuxt that can perform calls and render pages on both client and server environments, some challenges must be addressed. This is why Nuxt provides composables to wrap your queries, instead of letting the developer rely on `$fetch` calls alone.
|
||||
|
||||
### Network calls duplication
|
||||
|
||||
The [`useFetch`](/docs/api/composables/use-fetch) and [`useAsyncData`](/docs/api/composables/use-async-data) composables ensure that once an API call is made on the server, the data is properly forwarded to the client in the payload.
|
||||
|
||||
The payload is a JavaScript object accessible through [`useNuxtApp().payload`](/docs/api/composables/use-nuxt-app#payload). It is used on the client to avoid refetching the same data when the code is executed in the browser.
|
||||
|
||||
::alert{icon=⚙️}
|
||||
Use the [Nuxt DevTools](https://devtools.nuxt.com) to inspect this data in the payload tab.
|
||||
::
|
||||
|
||||
### Suspense
|
||||
|
||||
Nuxt uses Vue’s [`<Suspense>`](https://vuejs.org/guide/built-ins/suspense) component under the hood to prevent navigation before every async data is available to the view. The data fetching composables can help you leverage this feature and use what suits best on a per-calls basis.
|
||||
|
||||
::alert{icon=👉}
|
||||
**`useFetch`, `useLazyFetch`, `useAsyncData` and `useLazyAsyncData` only work during `setup` or `Lifecycle Hooks`**
|
||||
These composables are auto-imported and can be used in `setup` functions or lifecycle hooks
|
||||
::
|
||||
|
||||
## `useFetch`
|
||||
|
||||
Within your pages, components and plugins you can use `useFetch` to universally fetch from any URL.
|
||||
|
||||
This composable provides a convenient wrapper around `useAsyncData` and `$fetch`. It automatically generates a key based on URL and fetch options, provides type hints for request url based on server routes, and infers API response type.
|
||||
|
||||
::ReadMore{link="/docs/api/composables/use-fetch"}
|
||||
::
|
||||
|
||||
### Example
|
||||
The [`useFetch`](/docs/api/composables/use-fetch) composable is the most straightforward way to perform data fetching.
|
||||
|
||||
```vue [app.vue]
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
const { data: count } = await useFetch('/api/count')
|
||||
</script>
|
||||
|
||||
@ -32,19 +51,96 @@ const { data: count } = await useFetch('/api/count')
|
||||
</template>
|
||||
```
|
||||
|
||||
::LinkExample{link="/docs/examples/composables/use-fetch"}
|
||||
This composable is a wrapper around the [`useAsyncData`](/docs/api/composables/use-async-data) composable and `$fetch` utility.
|
||||
|
||||
::ReadMore{link="/docs/api/composables/use-fetch"}
|
||||
::
|
||||
|
||||
## `useLazyFetch`
|
||||
|
||||
This composable behaves identically to `useFetch` with the `lazy: true` option set. In other words, the async function does not block navigation. That means you will need to handle the situation where the data is `null` (or whatever value you have provided in a custom `default` factory function).
|
||||
|
||||
::ReadMore{link="/docs/api/composables/use-lazy-fetch"}
|
||||
::LinkExample{link="/docs/examples/features/data-fetching"}
|
||||
::
|
||||
|
||||
### Example
|
||||
## `$fetch`
|
||||
|
||||
Nuxt includes the `ofetch` library, and is auto-imported as the `$fetch` alias globally across your application. It's what `useFetch` uses behind the scenes.
|
||||
|
||||
```ts
|
||||
const users = await $fetch('/api/users').catch((error) => error.data)
|
||||
```
|
||||
|
||||
::alert{type="warning"}
|
||||
Beware that using only `$fetch` will not provide [network calls de-duplication and navigation prevention](#why-using-specific-composables). It is recommended to use `$fetch` when posting data to an event handler, when doing client-side only logic, or combined with `useAsyncData`.
|
||||
::
|
||||
|
||||
The `ofetch` library is built on top of [the `fetch` API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) and adds handy features to it:
|
||||
|
||||
- Works the same way in browser, Node or worker environments
|
||||
- Automatic response parsing
|
||||
- Error handling
|
||||
- Auto-retry
|
||||
- Interceptors
|
||||
|
||||
::alert{icon=📘}
|
||||
[Read the full documentation of ofetch](https://github.com/unjs/ofetch)
|
||||
::
|
||||
|
||||
::ReadMore{link="/docs/api/utils/dollarfetch"}
|
||||
::
|
||||
|
||||
## `useAsyncData`
|
||||
|
||||
The `useAsyncData` composable is responsible for wrapping async logic and returning the result once it is resolved.
|
||||
|
||||
Indeed, `useFetch(url)` is nearly equivalent to `useAsyncData(url, () => $fetch(url))` - it's developer experience sugar for the most common use case.
|
||||
|
||||
There are some cases when using the [`useFetch`](/docs/api/composables/use-fetch) composable is not appropriate, for example when a CMS or a third-party provide their own query layer. In this case, you can use [`useAsyncData`](/docs/api/composables/use-async-data) to wrap your calls and still keep the benefits provided by the composable.
|
||||
|
||||
The first argument of [`useAsyncData`](/docs/api/composables/use-async-data) is the unique key used to cache the response of the second argument, the querying function. This argument can be ignored by directly passing the querying function. In that case, it will be auto-generated.
|
||||
|
||||
```ts
|
||||
const { data, error } = await useAsyncData('users', () => myGetFunction('users'))
|
||||
```
|
||||
|
||||
Since the autogenerated key only takes into account the file and line where `useAsyncData` is invoked, is recommended to always create your own key to avoid unwanted behavior, if you are creating your own custom composable that is wrapping `useAsyncData`.
|
||||
|
||||
```ts
|
||||
const id = ref(1)
|
||||
|
||||
const { data, error } = await useAsyncData(`user:${id.value}`, () => {
|
||||
return myGetFunction('users', { id: id.value })
|
||||
})
|
||||
```
|
||||
|
||||
The `useAsyncData` composable is a great way to wrap and wait for multiple `useFetch` to be done, and then retrieve the results of each.
|
||||
|
||||
```ts
|
||||
const { data: discounts, pending } = await useAsyncData('cart-discount', async () => {
|
||||
const [coupons, offers] = await Promise.all([$fetch('/cart/coupons'), $fetch('/cart/offers')])
|
||||
|
||||
return {
|
||||
coupons,
|
||||
offers
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
::ReadMore{link="/docs/api/composables/use-async-data"}
|
||||
::
|
||||
|
||||
## Options
|
||||
|
||||
[`useAsyncData`](/docs/api/composables/use-async-data) and [`useFetch`](/docs/api/composables/use-fetch) return the same object type and accept a common set of options as their last argument. They can help you control the composables behavior, such as navigation blocking, caching or execution.
|
||||
|
||||
### Lazy
|
||||
|
||||
By default, data fetching composables will wait for the resolution of their asynchronous function before navigating to a new page by using Vue’s Suspense. This feature can be ignored on client-side navigation with the `lazy` option. In that case, you will have to manually handle loading state using the `pending` value.
|
||||
|
||||
```vue [app.vue]
|
||||
<script setup lang="ts">
|
||||
const { pending, data: posts } = useFetch('/api/posts', {
|
||||
lazy: true
|
||||
})
|
||||
</script>
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<!-- you will need to handle a loading state -->
|
||||
<div v-if="pending">
|
||||
@ -56,145 +152,271 @@ This composable behaves identically to `useFetch` with the `lazy: true` option s
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
<script setup>
|
||||
You can alternatively use [`useLazyFetch`](/docs/api/composables/use-lazy-fetch) and `useLazyAsyncData` as convenient methods to perform the same.
|
||||
|
||||
```ts
|
||||
const { pending, data: posts } = useLazyFetch('/api/posts')
|
||||
watch(posts, (newPosts) => {
|
||||
// Because posts starts out null, you will not have access
|
||||
// to its contents immediately, but you can watch it.
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
## `useAsyncData`
|
||||
|
||||
Within your pages, components and plugins you can use `useAsyncData` to get access to data that resolves asynchronously.
|
||||
|
||||
::alert
|
||||
You might be asking yourself: what is the difference between `useFetch` and `useAsyncData`?
|
||||
|
||||
In brief, `useFetch` receives a URL and gets that data, whereas `useAsyncData` might have more complex logic. `useFetch(url)` is nearly equivalent to `useAsyncData(url, () => $fetch(url))` - it's developer experience sugar for the most common use case.
|
||||
::ReadMore{link="/docs/api/composables/use-lazy-fetch"}
|
||||
::
|
||||
|
||||
::ReadMore{link="/docs/api/composables/use-async-data"}
|
||||
::
|
||||
|
||||
### Example
|
||||
|
||||
```ts [server/api/count.ts]
|
||||
let counter = 0
|
||||
export default defineEventHandler(() => {
|
||||
counter++
|
||||
return counter
|
||||
})
|
||||
```
|
||||
|
||||
```vue [app.vue]
|
||||
<script setup>
|
||||
const { data } = await useAsyncData('count', () => $fetch('/api/count'))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
Page visits: {{ data }}
|
||||
</template>
|
||||
```
|
||||
|
||||
::LinkExample{link="/docs/examples/composables/use-async-data"}
|
||||
::
|
||||
|
||||
## `useLazyAsyncData`
|
||||
|
||||
This composable behaves identically to `useAsyncData` with the `lazy: true` option set. In other words, the async function does not block navigation. That means you will need to handle the situation where the data is `null` (or whatever value you have provided in a custom `default` factory function).
|
||||
|
||||
::ReadMore{link="/docs/api/composables/use-lazy-async-data"}
|
||||
::
|
||||
|
||||
### Example
|
||||
### Client-only fetching
|
||||
|
||||
By default, data fetching composables will perform their asynchronous function on both client and server environments. Set the `server` option to `false` to only perform the call on the client-side. On initial load, the data will not be fetched before hydration is complete so you have to handle a pending state, though on subsequent client-side navigation the data will be awaited before loading the page.
|
||||
|
||||
Combined with the `lazy` option, this can be useful for data that is not needed on the first render (for example, non-SEO sensitive data).
|
||||
|
||||
```ts
|
||||
/* This call is performed before hydration */
|
||||
const { article } = await useFetch('api/article')
|
||||
|
||||
/* This call will only be performed on the client */
|
||||
const { pending, data: posts } = useFetch('/api/comments', {
|
||||
lazy: true,
|
||||
server: false
|
||||
})
|
||||
```
|
||||
|
||||
The `useFetch` composable is meant to be invoked in setup method or called directly at the top level of a function in lifecycle hooks, otherwise you should use [`$fetch` method](#fetch).
|
||||
|
||||
### Minimize payload size
|
||||
|
||||
The `pick` option helps you to minimize the payload size stored in your HTML document by only selecting the fields that you want returned from the composables.
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
/* only pick the fields used in your template */
|
||||
const { data: mountain } = await useFetch('/api/mountains/everest', { pick: ['title', 'description'] })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>{{ mountain.title }}</h1>
|
||||
<p>{{ mountain.description }}</p>
|
||||
</template>
|
||||
```
|
||||
|
||||
If you need more control or map over several objects, you can use the `transform` function to alter the result of the query.
|
||||
|
||||
```ts
|
||||
const { data: mountains } = await useFetch('/api/mountains', {
|
||||
transform: (mountains) => {
|
||||
return mountains.map(mountain => ({ title: mountain.title, description: mountain.description }))
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
::alert{type="warning"}
|
||||
Both `pick` and `transform` don't prevent the unwanted data from being fetched initially. But they will prevent unwanted data from being added to the payload transferred from server to client.
|
||||
::
|
||||
|
||||
### Caching and refetching
|
||||
|
||||
#### Keys
|
||||
|
||||
[`useFetch`](/docs/api/composables/use-fetch) and [`useAsyncData`](/docs/api/composables/use-async-data) use keys to prevent refetching the same data.
|
||||
|
||||
- [`useFetch`](/docs/api/composables/use-fetch) uses the provided URL as a key. Alternatively, a `key` value can be provided in the `options` object passed as a last argument.
|
||||
- [`useAsyncData`](/docs/api/composables/use-async-data) uses its first argument as a key if it is a string. If the first argument is the handler function that performs the query, then a key that is unique to the file name and line number of the instance of `useAsyncData` will be generated for you.
|
||||
|
||||
::alert{icon=📘}
|
||||
To get the cached data by key, you can use [`useNuxtData`](/docs/api/composables/use-nuxt-data)
|
||||
::
|
||||
|
||||
#### Refresh and execute
|
||||
|
||||
If you want to fetch or refresh data manually, use the `execute` or `refresh` function provided by the composables.
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
const { data, error, execute, refresh } = await useFetch('/api/users')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
{{ pending ? 'Loading' : count }}
|
||||
<p>{{ data }}</p>
|
||||
<button @click="refresh">Refresh data</button>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
<script setup>
|
||||
const { pending, data: count } = useLazyAsyncData('count', () => $fetch('/api/count'))
|
||||
watch(count, (newCount) => {
|
||||
// Because count starts out null, you won't have access
|
||||
// to its contents immediately, but you can watch it.
|
||||
The `execute` function is an alias for `refresh` that works in exactly the same way but is more semantic for cases when the fetch is [not immediate](#not-immediate).
|
||||
|
||||
::alert{icon=📘}
|
||||
To globally refetch or invalidate cached data, see [`clearNuxtData`](/docs/api/utils/clear-nuxt-data) and [`refreshNuxtData`](/docs/api/utils/refresh-nuxt-data).
|
||||
::
|
||||
|
||||
#### Watch
|
||||
|
||||
To re-run your fetching function each time other reactive values in your application change, use the `watch` option. You can use it for one or multiple _watchable_ elements.
|
||||
|
||||
```ts
|
||||
const id = ref(1)
|
||||
|
||||
const { data, error, refresh } = await useFetch('/api/users', {
|
||||
/* Changing the id will trigger a refetch */
|
||||
watch: [id]
|
||||
})
|
||||
```
|
||||
|
||||
Note that **watching a reactive value won't change the URL fetched**. For example, this will keep fetching the same initial ID of the user because the URL is constructed at the moment the function is invoked.
|
||||
|
||||
```ts
|
||||
const id = ref(1)
|
||||
|
||||
const { data, error, refresh } = await useFetch(`/api/users/${id.value}`, {
|
||||
watch: [id]
|
||||
})
|
||||
```
|
||||
|
||||
If you need to change the URL based on a reactive value, you may want to use a [computed URL](#computed-url) instead.
|
||||
|
||||
#### Computed URL
|
||||
|
||||
Sometimes you may need to compute an URL from reactive values, and refresh the data each time these change. Instead of juggling your way around, you can attach each param as a reactive value. Nuxt will automatically use the reactive value and re-fetch each time it changes.
|
||||
|
||||
```ts
|
||||
const id = ref(null)
|
||||
|
||||
const { data, pending } = useLazyFetch('/api/user', {
|
||||
query: {
|
||||
user_id: id
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
In the case of more complex URL construction, you may use a callback as a [computed getter](https://vuejs.org/guide/essentials/computed.html) that returns the URL string.
|
||||
|
||||
Every time a dependency changes, the data will be fetched using the newly constructed URL. Combine this with [not-immediate](#not-immediate), and you can wait until the reactive element changes before fetching.
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
const id = ref(null)
|
||||
|
||||
const { data, pending, status } = useLazyFetch(() => `/api/users/${id.value}`, {
|
||||
immediate: false
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
## Refreshing Data
|
||||
|
||||
Sometimes throughout the course of your user's page visit, you may need to refresh the data loaded from the API. This can happen if the user chooses to paginate, filter results, search, etc.
|
||||
|
||||
You can make use of the `refresh()` method returned from the `useFetch()` composable to refresh the data with different query parameters:
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
const page = ref(1)
|
||||
|
||||
const { data: users, pending, refresh, error } = await useFetch(() => `users?page=${page.value}&take=6`, { baseURL: config.API_BASE_URL }
|
||||
)
|
||||
|
||||
function previous() {
|
||||
page.value--
|
||||
refresh()
|
||||
}
|
||||
|
||||
function next() {
|
||||
page.value++
|
||||
refresh()
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
The key to making this work is to call the `refresh()` method returned from the `useFetch()` composable when a query parameter has changed.
|
||||
|
||||
By default, `refresh()` will cancel any pending requests their result will not update the data or pending state. Any previously awaited promises will not resolve until this new request resolves. You can prevent this behaviour by setting the `dedupe` option, which will instead return the promise for the currently-executing request, if there is one.
|
||||
|
||||
```js
|
||||
refresh({ dedupe: true })
|
||||
```
|
||||
|
||||
### `refreshNuxtData`
|
||||
|
||||
Invalidate the cache of `useAsyncData`, `useLazyAsyncData`, `useFetch` and `useLazyFetch` and trigger the refetch.
|
||||
|
||||
This method is useful if you want to refresh all the data fetching for a current page.
|
||||
|
||||
::ReadMore{link="/docs/api/utils/refresh-nuxt-data"}
|
||||
::
|
||||
|
||||
#### Example
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
{{ pending ? 'Loading' : count }}
|
||||
<!-- disable the input while fetching -->
|
||||
<input v-model="id" type="number" :disabled="pending"/>
|
||||
|
||||
<div v-if="status === 'idle'">
|
||||
Type an user ID
|
||||
</div>
|
||||
|
||||
<div v-else-if="pending">
|
||||
Loading ...
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
{{ data }}
|
||||
</div>
|
||||
</div>
|
||||
<button @click="refresh">Refresh</button>
|
||||
</template>
|
||||
```
|
||||
|
||||
<script setup>
|
||||
const { pending, data: count } = useLazyAsyncData('count', () => $fetch('/api/count'))
|
||||
If you need to force a refresh when other reactive values change, you can also [watch other values](#watch).
|
||||
|
||||
const refresh = () => refreshNuxtData('count')
|
||||
### Not immediate
|
||||
|
||||
The `useFetch` composable will start fetching data the moment is invoked. You may prevent this by setting `immediate: false`, for example, to wait for user interaction.
|
||||
|
||||
With that, you will need both the `status` to handle the fetch lifecycle, and `execute` to start the data fetch.
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
const { data, error, execute, pending, status } = await useLazyFetch('/api/comments')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="status === 'idle'">
|
||||
<button @click="execute">Get data</button>
|
||||
</div>
|
||||
|
||||
<div v-else-if="pending">
|
||||
Loading comments...
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
{{ data }}
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
For finer control, the `status` variable can be:
|
||||
|
||||
- `idle` when the fetch hasn't started
|
||||
- `pending` when a fetch has started but not yet completed
|
||||
- `error` when the fetch fails
|
||||
- `success` when the fetch is completed successfully
|
||||
|
||||
## Passing Headers and cookies
|
||||
|
||||
When we call `$fetch` in the browser, user headers like `cookie` will be directly sent to the API. But during server-side-rendering, since the `$fetch` request takes place 'internally' within the server, it doesn't include the user's browser cookies, nor does it pass on cookies from the fetch response.
|
||||
|
||||
### Pass Client Headers to the API
|
||||
|
||||
We can use [`useRequestHeaders`](/docs/api/composables/use-request-headers) to access and proxy cookies to the API from server-side.
|
||||
|
||||
The example below adds the request headers to an isomorphic `$fetch` call to ensure that the API endpoint has access to the same `cookie` header originally sent by the user.
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
const headers = useRequestHeaders(['cookie'])
|
||||
|
||||
const { data } = await useFetch('/api/me', { headers })
|
||||
</script>
|
||||
```
|
||||
|
||||
### `clearNuxtData`
|
||||
::alert{type="warning"}
|
||||
Be very careful before proxying headers to an external API and just include headers that you need. Not all headers are safe to be bypassed and might introduce unwanted behavior. Here is a list of common headers that are NOT to be proxied:
|
||||
|
||||
Delete cached data, error status and pending promises of `useAsyncData` and `useFetch`.
|
||||
|
||||
This method is useful if you want to invalidate the data fetching for another page.
|
||||
|
||||
::ReadMore{link="/docs/api/utils/clear-nuxt-data"}
|
||||
- `host`, `accept`
|
||||
- `content-length`, `content-md5`, `content-type`
|
||||
- `x-forwarded-host`, `x-forwarded-port`, `x-forwarded-proto`
|
||||
- `cf-connecting-ip`, `cf-ray`
|
||||
::
|
||||
|
||||
### Pass Cookies From Server-side API Calls on SSR Response
|
||||
|
||||
If you want to pass on/proxy cookies in the other direction, from an internal request back to the client, you will need to handle this yourself.
|
||||
|
||||
```ts [composables/fetch.ts]
|
||||
import { appendResponseHeader, H3Event } from 'h3'
|
||||
|
||||
export const fetchWithCookie = async (event: H3Event, url: string) => {
|
||||
/* Get the response from the server endpoint */
|
||||
const res = await $fetch.raw(url)
|
||||
/* Get the cookies from the response */
|
||||
const cookies = (res.headers.get('set-cookie') || '').split(',')
|
||||
/* Attach each cookie to our incoming Request */
|
||||
for (const cookie of cookies) {
|
||||
appendResponseHeader(event, 'set-cookie', cookie)
|
||||
}
|
||||
/* Return the data of the response */
|
||||
return res._data
|
||||
}
|
||||
```
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
// This composable will automatically pass cookies to the client
|
||||
const event = useRequestEvent()
|
||||
|
||||
const result = await fetchWithCookie(event, '/api/with-cookie')
|
||||
|
||||
onMounted(() => console.log(document.cookie))
|
||||
</script>
|
||||
```
|
||||
|
||||
## Options API support
|
||||
|
||||
Nuxt 3 provides a way to perform `asyncData` fetching within the Options API. You must wrap your component definition within `defineNuxtComponent` for this to work.
|
||||
@ -202,6 +424,7 @@ Nuxt 3 provides a way to perform `asyncData` fetching within the Options API. Yo
|
||||
```vue
|
||||
<script>
|
||||
export default defineNuxtComponent({
|
||||
/* Use the fetchKey option to provide a unique key */
|
||||
fetchKey: 'hello',
|
||||
async asyncData () {
|
||||
return {
|
||||
@ -219,177 +442,9 @@ Using `<script setup lang="ts">` is the recommended way of declaring Vue compone
|
||||
::ReadMore{link="/docs/api/utils/define-nuxt-component"}
|
||||
::
|
||||
|
||||
## Using `$fetch` directly
|
||||
|
||||
There are instances where you may need to directly call the API. Nuxt 3 provides a globally available `$fetch` method using [unjs/ofetch](https://github.com/unjs/ofetch) (in addition to `fetch`) with the same API as the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch).
|
||||
|
||||
Using `$fetch` has a number of benefits, including:
|
||||
|
||||
It will handle 'smartly' making direct API calls if it's running on the server, or making a client-side call to your API if it's running on the client. (It can also handle calling third-party APIs.)
|
||||
|
||||
Plus, it comes with convenience features including automatically parsing responses and stringifying data.
|
||||
|
||||
::ReadMore{link="/docs/api/utils/dollarfetch"}
|
||||
::
|
||||
|
||||
### Isomorphic `$fetch` and `fetch` calls
|
||||
|
||||
When we call `$fetch` in the browser, user headers like `cookie` will be directly sent to the API. But during server-side-rendering, since the `$fetch` request takes place 'internally' within the server, it doesn't include the user's browser cookies, nor does it pass on cookies from the fetch response.
|
||||
|
||||
#### Example: Pass Client Headers to the API
|
||||
|
||||
We can use [`useRequestHeaders`](/docs/api/composables/use-request-headers) to access and proxy cookies to the API from server-side.
|
||||
|
||||
The example below adds the request headers to an isomorphic `$fetch` call to ensure that the API endpoint has access to the same `cookie` header originally sent by the user.
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
const headers = useRequestHeaders(['cookie'])
|
||||
const { data } = await useFetch('/api/me', { headers })
|
||||
</script>
|
||||
```
|
||||
|
||||
::alert{type="warning"}
|
||||
Be very careful before proxying headers to an external API and just include headers that you need. Not all headers are safe to be bypassed and might introduce unwanted behavior. Here is a list of common headers that are NOT to be proxied:
|
||||
|
||||
* `host`, `accept`
|
||||
* `content-length`, `content-md5`, `content-type`
|
||||
* `x-forwarded-host`, `x-forwarded-port`, `x-forwarded-proto`
|
||||
* `cf-connecting-ip`, `cf-ray`
|
||||
::
|
||||
|
||||
#### Example: Pass Cookies From Server-side API Calls on SSR Response
|
||||
|
||||
If you want to pass on/proxy cookies in the other direction, from an internal request back to the client, you will need to handle this yourself.
|
||||
|
||||
```ts [composables/fetch.ts]
|
||||
export const fetchWithCookie = async (event: H3Event, url: string) => {
|
||||
const res = await $fetch.raw(url)
|
||||
const cookies = (res.headers.get('set-cookie') || '').split(',')
|
||||
for (const cookie of cookies) {
|
||||
appendHeader(event, 'set-cookie', cookie)
|
||||
}
|
||||
return res._data
|
||||
}
|
||||
```
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
// This composable will automatically pass cookies to the client
|
||||
const event = useRequestEvent()
|
||||
const result = await fetchWithCookie(event, '/api/with-cookie')
|
||||
onMounted(() => console.log(document.cookie))
|
||||
</script>
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Minimize Payload
|
||||
|
||||
The data returned by these composables will be stored inside the page payload. This means that every key returned that is not used in your component will be added to the payload.
|
||||
|
||||
::alert{icon=👉}
|
||||
**We strongly recommend you only select the keys that you will use in your component.**
|
||||
::
|
||||
|
||||
Imagine that `/api/mountains/everest` returns the following object:
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "Mount Everest",
|
||||
"description": "Mount Everest is Earth's highest mountain above sea level, located in the Mahalangur Himal sub-range of the Himalayas. The China–Nepal border runs across its summit point",
|
||||
"height": "8,848 m",
|
||||
"countries": [
|
||||
"China",
|
||||
"Nepal"
|
||||
],
|
||||
"continent": "Asia",
|
||||
"image": "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f6/Everest_kalapatthar.jpg/600px-Everest_kalapatthar.jpg"
|
||||
}
|
||||
```
|
||||
|
||||
If you plan to only use `title` and `description` in your component, you can select the keys by chaining the result of `$fetch` or `pick` option:
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
const { data: mountain } = await useFetch('/api/mountains/everest', { pick: ['title', 'description'] })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>{{ mountain.title }}</h1>
|
||||
<p>{{ mountain.description }}</p>
|
||||
</template>
|
||||
```
|
||||
|
||||
### Avoid double calls
|
||||
|
||||
Calling `$fetch` in code that is executed on both server and client (such as in the top level of a `setup` function) will fetch the data twice - initially on the server and then again on the client during the hydration phase. This is because `$fetch` does not automatically serialize or transfer the data to the client.
|
||||
|
||||
For example:
|
||||
|
||||
**/pages/price.vue**: Isomorphic code below executes `$fetch` twice (initially on the server, then again on the client).
|
||||
|
||||
```ts
|
||||
<script setup lang="ts">
|
||||
const price = $fetch('/api/price');
|
||||
</script>
|
||||
```
|
||||
|
||||
**/server/api/product.get.ts**: Server only code below executes `$fetch` only once at the server side.
|
||||
|
||||
```ts
|
||||
export default eventHandler(async (event: H3Event) => {
|
||||
const price = $fetch('/api/price');
|
||||
return { color: getColor(), price };
|
||||
});
|
||||
```
|
||||
|
||||
If fetching twice isn't your intended behavior, to fetch only on the server side and transfer it to the client, wrap `$fetch` with `useAsyncData()` or use `useFetch()`.
|
||||
|
||||
```ts
|
||||
<script setup lang="ts">
|
||||
const { data } = await useAsyncData('price', () => $fetch('/api/price'));
|
||||
// or
|
||||
const { data } = await useFetch('/api/price')
|
||||
</script>
|
||||
```
|
||||
|
||||
## Using Async Setup
|
||||
|
||||
If you are using `async setup()`, the current component instance will be lost after the first `await`. (This is a Vue 3 limitation.) If you want to use multiple async operations, such as multiple calls to `useFetch`, you will need to use `<script setup>` or await them together at the end of setup.
|
||||
|
||||
::alert{icon=👉}
|
||||
Using `<script setup>` is recommended, as it removes the limitation of using top-level await. [Read more](https://vuejs.org/api/sfc-script-setup.html#top-level-await)
|
||||
::
|
||||
|
||||
```vue
|
||||
<script>
|
||||
export default defineComponent({
|
||||
async setup() {
|
||||
const [{ data: organization }, { data: repos }] = await Promise.all([
|
||||
useFetch(`https://api.github.com/orgs/nuxt`),
|
||||
useFetch(`https://api.github.com/orgs/nuxt/repos`)
|
||||
])
|
||||
|
||||
return {
|
||||
organization,
|
||||
repos
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header>
|
||||
<h1>{{ organization.login }}</h1>
|
||||
<p>{{ organization.description }}</p>
|
||||
</header>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Serialization
|
||||
|
||||
When fetching data from the `server` directory, the response is serialized using `JSON.stringify`. However, since serialization is limited to only JavaScript primitive types, Nuxt does its best to convert the return type of `$fetch` and `useFetch` to match the actual value.
|
||||
When fetching data from the `server` directory, the response is serialized using `JSON.stringify`. However, since serialization is limited to only JavaScript primitive types, Nuxt does its best to convert the return type of `$fetch` and [`useFetch`](/docs/api/composables/use-fetch) to match the actual value.
|
||||
|
||||
::alert{icon=👉}
|
||||
You can learn more about `JSON.stringify` limitations [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description).
|
||||
|
@ -5,18 +5,18 @@ description: Nuxt provides powerful state management libraries and the useState
|
||||
|
||||
# State Management
|
||||
|
||||
Nuxt provides the `useState` composable to create a reactive and SSR-friendly shared state across components.
|
||||
Nuxt provides the [`useState`](/docs/api/composables/use-state) composable to create a reactive and SSR-friendly shared state across components.
|
||||
|
||||
`useState` is an SSR-friendly [`ref`](https://vuejs.org/api/reactivity-core.html#ref) replacement. Its value will be preserved after server-side rendering (during client-side hydration) and shared across all components using a unique key.
|
||||
[`useState`](/docs/api/composables/use-state) is an SSR-friendly [`ref`](https://vuejs.org/api/reactivity-core.html#ref) replacement. Its value will be preserved after server-side rendering (during client-side hydration) and shared across all components using a unique key.
|
||||
|
||||
::ReadMore{link="/docs/api/composables/use-state"}
|
||||
::
|
||||
|
||||
::alert{icon=👉}
|
||||
`useState` only works during `setup` or [`Lifecycle Hooks`](https://vuejs.org/api/composition-api-lifecycle.html#composition-api-lifecycle-hooks).
|
||||
[`useState`](/docs/api/composables/use-state) only works during `setup` or [`Lifecycle Hooks`](https://vuejs.org/api/composition-api-lifecycle.html#composition-api-lifecycle-hooks).
|
||||
::
|
||||
::alert{type=warning}
|
||||
Because the data inside `useState` will be serialized to JSON, it is important that it does not contain anything that cannot be serialized, such as classes, functions or symbols.
|
||||
Because the data inside [`useState`](/docs/api/composables/use-state) will be serialized to JSON, it is important that it does not contain anything that cannot be serialized, such as classes, functions or symbols.
|
||||
::
|
||||
|
||||
## Best Practices
|
||||
@ -36,7 +36,7 @@ Instead use `const useX = () => useState('x')`
|
||||
In this example, we use a component-local counter state. Any other component that uses `useState('counter')` shares the same reactive state.
|
||||
|
||||
```vue [app.vue]
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
const counter = useState('counter', () => Math.round(Math.random() * 1000))
|
||||
</script>
|
||||
|
||||
@ -53,17 +53,82 @@ const counter = useState('counter', () => Math.round(Math.random() * 1000))
|
||||
</template>
|
||||
```
|
||||
|
||||
::LinkExample{link="/docs/examples/composables/use-state"}
|
||||
::LinkExample{link="/docs/examples/features/state-management"}
|
||||
::
|
||||
|
||||
::ReadMore{link="/docs/api/composables/use-state"}
|
||||
::
|
||||
|
||||
### Advanced
|
||||
::alert{icon=📘}
|
||||
To globally invalidate cached state, see [`clearNuxtState`](/docs/api/utils/clear-nuxt-state).
|
||||
::
|
||||
|
||||
### Advanced Usage
|
||||
|
||||
In this example, we use a composable that detects the user's default locale from the HTTP request headers and keeps it in a `locale` state.
|
||||
|
||||
::LinkExample{link="/docs/examples/other/locale"}
|
||||
```ts [composables/locale.ts]
|
||||
import type { Ref } from 'vue'
|
||||
|
||||
export const useLocale = () => useState<string>('locale', () => useDefaultLocale().value)
|
||||
|
||||
export const useDefaultLocale = (fallback = 'en-US') => {
|
||||
const locale = ref(fallback)
|
||||
if (process.server) {
|
||||
const reqLocale = useRequestHeaders()['accept-language']?.split(',')[0]
|
||||
if (reqLocale) {
|
||||
locale.value = reqLocale
|
||||
}
|
||||
} else if (process.client) {
|
||||
const navLang = navigator.language
|
||||
if (navLang) {
|
||||
locale.value = navLang
|
||||
}
|
||||
}
|
||||
return locale
|
||||
}
|
||||
|
||||
export const useLocales = () => {
|
||||
const locale = useLocale()
|
||||
const locales = ref([
|
||||
'en-US',
|
||||
'en-GB',
|
||||
...
|
||||
'ja-JP-u-ca-japanese'
|
||||
])
|
||||
if (!locales.value.includes(locale.value)) {
|
||||
locales.value.unshift(locale.value)
|
||||
}
|
||||
return locales
|
||||
}
|
||||
|
||||
export const useLocaleDate = (date: Ref<Date> | Date, locale = useLocale()) => {
|
||||
return computed(() => new Intl.DateTimeFormat(locale.value, { dateStyle: 'full' }).format(unref(date)))
|
||||
}
|
||||
```
|
||||
|
||||
```vue [app.vue]
|
||||
<script setup lang="ts">
|
||||
const locales = useLocales()
|
||||
const locale = useLocale()
|
||||
const date = useLocaleDate(new Date('2016-10-26'))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h1>Nuxt birthday</h1>
|
||||
<p>{{ date }}</p>
|
||||
<label for="locale-chooser">Preview a different locale</label>
|
||||
<select id="locale-chooser" v-model="locale">
|
||||
<option v-for="locale of locales" :key="locale" :value="locale">
|
||||
{{ locale }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
::LinkExample{link="/docs/examples/advanced/locale"}
|
||||
::
|
||||
|
||||
## Shared State
|
||||
@ -76,7 +141,7 @@ export const useColor = () => useState<string>('color', () => 'pink')
|
||||
```
|
||||
|
||||
```vue [app.vue]
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
const color = useColor() // Same as useState('color')
|
||||
</script>
|
||||
|
||||
@ -89,8 +154,8 @@ const color = useColor() // Same as useState('color')
|
||||
|
||||
Nuxt **used to rely** on the Vuex library to provide global state management. If you are migrating from Nuxt 2, please head to [the migration guide](/docs/migration/configuration#vuex).
|
||||
|
||||
Nuxt is not opiniated about state management, so feel free to choose the right solution for your needs. There are multiple integrations with the most popular state management libraries, including:
|
||||
Nuxt is not opinionated about state management, so feel free to choose the right solution for your needs. There are multiple integrations with the most popular state management libraries, including:
|
||||
|
||||
- [Pinia](/modules/pinia) - the official Vue recommendation
|
||||
- [Harlem](/modules/harlem) - immutable global state management
|
||||
- [XState](/modules/xstate) - state machine approach with tools for visualising and testing your state logic
|
||||
- [XState](/modules/xstate) - state machine approach with tools for visualizing and testing your state logic
|
||||
|
@ -41,6 +41,7 @@ This includes:
|
||||
|
||||
* running Nuxt plugins
|
||||
* processing `app:created` and `app:beforeMount` hooks
|
||||
* rendering your Vue app to HTML (on the server)
|
||||
* mounting the app (on client-side), though you should handle this case with `onErrorCaptured` or with `vue:error`
|
||||
* processing the `app:mounted` hook
|
||||
|
||||
@ -56,9 +57,36 @@ You can change this behavior by setting `experimental.emitRouteChunkError` to `f
|
||||
|
||||
## Rendering an Error Page
|
||||
|
||||
When Nuxt encounters a fatal error, whether during the server lifecycle, or when rendering your Vue application (both SSR and SPA), it will either render a JSON response (if requested with `Accept: application/json` header) or an HTML error page.
|
||||
When Nuxt encounters a fatal error (any unhandled error on the server, or an error created with `fatal: true` on the client) it will either render a JSON response (if requested with `Accept: application/json` header) or trigger a full-screen error page.
|
||||
|
||||
You can customize this error page by adding `~/error.vue` in the source directory of your application, alongside `app.vue`. This page has a single prop - `error` which contains an error for you to handle.
|
||||
This error may occur during the server lifecycle when:
|
||||
|
||||
* processing your Nuxt plugins
|
||||
* rendering your Vue app into HTML
|
||||
* a server API route throws an error
|
||||
|
||||
An error can also occur on the client side when:
|
||||
|
||||
* processing your Nuxt plugins
|
||||
* before mounting the application (`app:beforeMount` hook)
|
||||
* mounting your app if the error was not handled with `onErrorCaptured` or `vue:error` hook
|
||||
* the Vue app is initialized and mounted in browser (`app:mounted`).
|
||||
|
||||
The lifecycle hooks are listed [here](/docs/api/advanced/hooks).
|
||||
|
||||
You can customize this error page by adding `~/error.vue` in the source directory of your application, alongside `app.vue`. Although it is called an 'error page' it's not a route and shouldn't be placed in your `~/pages` directory. For the same reason, you shouldn't use `definePageMeta` within this page.
|
||||
|
||||
The error page has a single prop - `error` which contains an error for you to handle.
|
||||
|
||||
The `error` object provides the fields: `url`, `statusCode`, `statusMessage`, `message`, `description` and `data`. If you have an error with custom fields they will be lost; you should assign them to `data` instead. For custom errors we highly recommend to use `onErrorCaptured` composable that can be called in a page/component setup function or `vue:error` runtime nuxt hook that can be configured in a nuxt plugin.
|
||||
|
||||
```ts
|
||||
export default defineNuxtPlugin(nuxtApp => {
|
||||
nuxtApp.hook('vue:error', (err) => {
|
||||
//
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
When you are ready to remove the error page, you can call the `clearError` helper function, which takes an optional path to redirect to (for example, if you want to navigate to a 'safe' page).
|
||||
|
||||
@ -66,20 +94,24 @@ When you are ready to remove the error page, you can call the `clearError` helpe
|
||||
Make sure to check before using anything dependent on Nuxt plugins, such as `$route` or `useRouter`, as if a plugin threw an error, then it won't be re-run until you clear the error.
|
||||
::
|
||||
|
||||
::alert{type="warning"}
|
||||
If you are running on Node 16 and you set any cookies when rendering your error page, they will [overwrite cookies previously set](https://github.com/nuxt/nuxt/pull/20585). We recommend using a newer version of Node as Node 16 will reach end-of-life in September 2023.
|
||||
::
|
||||
|
||||
### Example
|
||||
|
||||
```vue [error.vue]
|
||||
<template>
|
||||
<button @click="handleError">Clear errors</button>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
const props = defineProps({
|
||||
error: Object
|
||||
})
|
||||
|
||||
const handleError = () => clearError({ redirect: '/' })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button @click="handleError">Clear errors</button>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Error Helper Methods
|
||||
@ -106,8 +138,8 @@ If you throw an error created with `createError`:
|
||||
|
||||
### Example
|
||||
|
||||
```vue [pages/movies/[slug].vue]
|
||||
<script setup>
|
||||
```vue [pages/movies/[slug\\].vue]
|
||||
<script setup lang="ts">
|
||||
const route = useRoute()
|
||||
const { data } = await useFetch(`/api/movies/${route.params.slug}`)
|
||||
if (!data.value) {
|
||||
@ -155,9 +187,9 @@ If you navigate to another route, the error will be cleared automatically.
|
||||
<!-- some content -->
|
||||
<NuxtErrorBoundary @error="someErrorLogger">
|
||||
<!-- You use the default slot to render your content -->
|
||||
<template #error="{ error }">
|
||||
You can display the error locally here.
|
||||
<button @click="error = null">
|
||||
<template #error="{ error, clearError }">
|
||||
You can display the error locally here: {{ error }}
|
||||
<button @click="clearError">
|
||||
This will clear the error.
|
||||
</button>
|
||||
</template>
|
||||
@ -165,5 +197,5 @@ If you navigate to another route, the error will be cleared automatically.
|
||||
</template>
|
||||
```
|
||||
|
||||
::LinkExample{link="/docs/examples/app/error-handling"}
|
||||
::LinkExample{link="/docs/examples/advanced/error-handling"}
|
||||
::
|
||||
|
86
docs/1.getting-started/8.server.md
Normal file
86
docs/1.getting-started/8.server.md
Normal file
@ -0,0 +1,86 @@
|
||||
---
|
||||
navigation.icon: uil:laptop-connection
|
||||
description: Build full-stack applications, fetch data from your database, create APIs, or even generate static server-side content like a sitemap or a RSS feed, from a single codebase.
|
||||
---
|
||||
|
||||
# Server
|
||||
|
||||
Nuxt's server framework allows you to build **full-stack applications**. For example, you can fetch data from a database or another server, create an API or even generate static server-side content like a sitemap or an RSS feed - all from a single codebase.
|
||||
|
||||
::ReadMore{link="/docs/guide/directory-structure/server"}
|
||||
::
|
||||
|
||||
## Powered by Nitro
|
||||
|
||||
Nuxt's server is [Nitro](https://github.com/unjs/nitro). Nitro was originally created for Nuxt but is now part of [UnJS](https://unjs.io/) and used for other frameworks - and can even be used on its own.
|
||||
|
||||
Using Nitro gives Nuxt superpowers:
|
||||
|
||||
- Full control of the server-side part of your app
|
||||
- Universal deployment on any provider (many zero-config)
|
||||
- Hybrid rendering
|
||||
|
||||
## Full control of the server-side part of your app
|
||||
|
||||
With Nitro, you can easily manage the server part of your Nuxt app, from API endpoints to middleware.
|
||||
|
||||
Both endpoints and middleware can be defined like this:
|
||||
|
||||
```ts [server/api/test.ts]
|
||||
export default defineEventHandler({
|
||||
// ... Do whatever you want here
|
||||
})
|
||||
```
|
||||
|
||||
And you can directly return `text`, `json`, `html` or even a `stream`.
|
||||
|
||||
Out-of-the-box, Nitro supports **hot module replacement** and **auto-import** like the other parts of your Nuxt application.
|
||||
|
||||
::ReadMore{link="/docs/guide/directory-structure/server"}
|
||||
::
|
||||
|
||||
## Universal Deployment
|
||||
|
||||
Nitro offers the ability to deploy your Nuxt app anywhere, from a bare metal server to the edge network, with a start time of just a few milliseconds. That's fast!
|
||||
|
||||
Today, Nitro offers more than 15 presets to build your Nuxt app for different cloud providers and servers, including:
|
||||
|
||||
- [Cloudflare Workers](https://workers.cloudflare.com/)
|
||||
- [Netlify Functions](https://www.netlify.com/products/functions/)
|
||||
- [Vercel Edge Network](https://vercel.com/docs/edge-network/introduction)
|
||||
|
||||
Or for other runtimes:
|
||||
|
||||
- [Deno](https://deno.land/)
|
||||
- [Bun](https://bun.sh/)
|
||||
|
||||
::ReadMore{link="/docs/getting-started/deployment"}
|
||||
::
|
||||
|
||||
## Hybrid Rendering
|
||||
|
||||
Do you need both static and dynamic pages in your Nuxt app? Nitro has your back!
|
||||
|
||||
Nitro has a powerful feature called `routeRules` which allows you to define a set of rules to customize how each route of your Nuxt app is rendered (and more).
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
routeRules: {
|
||||
'/': { prerender: true }, // Generated at build time for SEO purpose
|
||||
'/api/*': { cache: { maxAge: 60 * 60 } } // Cached for 1 hour
|
||||
'/old-page': { redirect: { to: { '/new-page', status: 302 } } // Redirection to avoid 404
|
||||
// ...
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
[More rules are available](https://nuxt.com/docs/guide/concepts/rendering#hybrid-rendering) to customize the rendering mode of your routes.
|
||||
|
||||
In addition, there are some route rules (for example, `ssr` and `experimentalNoScripts`) that are not Nitro rules but affect Nuxt's behavior when rendering your pages to HTML.
|
||||
|
||||
Some route rules (`redirect` and `prerender`) also affect client-side behavior.
|
||||
|
||||
Nitro is used to build the app for server side rendering, as well as pre-rendering.
|
||||
|
||||
::ReadMore{link="/docs/guide/concepts/rendering"}
|
||||
::
|
@ -11,14 +11,14 @@ Some use cases:
|
||||
|
||||
::list{type="success"}
|
||||
- Share reusable configuration presets across projects using `nuxt.config` and `app.config`
|
||||
- Create a component library using `components/` directory
|
||||
- Create utility and composable library using `composables/` and `utils/` directories
|
||||
- Create a component library using [`components/` directory](/docs/guide/directory-structure/components)
|
||||
- Create utility and composable library using [`composables/` directory](/docs/guide/directory-structure/composables) and [`utils/` directory](/docs/guide/directory-structure/utils)
|
||||
- Create [Nuxt themes](https://github.com/nuxt-themes)
|
||||
- Create Nuxt module presets
|
||||
- Share standard setup across projects
|
||||
::
|
||||
|
||||
You can extend a layer by adding the [extends](/docs/api/configuration/nuxt-config#extends) property to the `nuxt.config.ts` file.
|
||||
You can extend a layer by adding the [extends](/docs/api/configuration/nuxt-config#extends) property to the [`nuxt.config.ts`](/docs/guide/directory-structure/nuxt.config) file.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
|
@ -2,7 +2,9 @@
|
||||
description: "Nuxt auto-imports helper functions, composables and Vue APIs."
|
||||
---
|
||||
|
||||
# Auto imports
|
||||
# Auto-imports
|
||||
|
||||
## Composables and Helper Functions
|
||||
|
||||
Nuxt auto-imports helper functions, composables and Vue APIs to use across your application without explicitly importing them. Based on the directory structure, every Nuxt application can also use auto-imports for its own components, composables and plugins. Components, composables or plugins can use these functions.
|
||||
|
||||
@ -21,32 +23,32 @@ In the [server directory](/docs/guide/directory-structure/server), we auto impor
|
||||
You can also auto-import functions exported from custom folders or third-party packages by configuring the [`imports` section](/docs/api/configuration/nuxt-config#imports) of your `nuxt.config` file.
|
||||
::
|
||||
|
||||
## Built-in Auto-imports
|
||||
### Built-in Auto-imports
|
||||
|
||||
### Nuxt Auto-imports
|
||||
#### Nuxt Auto-imports
|
||||
|
||||
Nuxt auto-imports functions and composables to perform [data fetching](/docs/getting-started/data-fetching), get access to the [app context](/docs/api/composables/use-nuxt-app) and [runtime config](/docs/guide/going-further/runtime-config), manage [state](/docs/getting-started/state-management) or define components and plugins.
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
/* useAsyncData() and $fetch() are auto-imported */
|
||||
const { data, refresh, pending } = await useAsyncData('/api/hello', () => $fetch('/api/hello'))
|
||||
</script>
|
||||
```
|
||||
|
||||
### Vue Auto-imports
|
||||
#### Vue Auto-imports
|
||||
|
||||
Vue 3 exposes Reactivity APIs like `ref` or `computed`, as well as lifecycle hooks and helpers that are auto-imported by Nuxt.
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
/* ref() and computed() are auto-imported */
|
||||
const count = ref(1)
|
||||
const double = computed(() => count.value * 2)
|
||||
</script>
|
||||
```
|
||||
|
||||
### Using Vue and Nuxt composables
|
||||
#### Using Vue and Nuxt composables
|
||||
|
||||
<!-- TODO: move to separate page with https://github.com/nuxt/nuxt/issues/14723 and add more information -->
|
||||
|
||||
@ -54,16 +56,16 @@ When you are using the built-in Composition API composables provided by Vue and
|
||||
|
||||
During a component lifecycle, Vue tracks the temporary instance of the current component (and similarly, Nuxt tracks a temporary instance of `nuxtApp`) via a global variable, and then unsets it in same tick. This is essential when server rendering, both to avoid cross-request state pollution (leaking a shared reference between two users) and to avoid leakage between different components.
|
||||
|
||||
That means that (with very few exceptions) you cannot use them outside a Nuxt plugin, Nuxt route middleware or Vue setup function. On top of that, you must use them synchronously - that is, you cannot use `await` before calling a composable, except within `<script setup>` blocks, in `defineNuxtPlugin` or in `defineNuxtRouteMiddleware`, where we perform a transform to keep the synchronous context even after the `await`.
|
||||
That means that (with very few exceptions) you cannot use them outside a Nuxt plugin, Nuxt route middleware or Vue setup function. On top of that, you must use them synchronously - that is, you cannot use `await` before calling a composable, except within `<script setup>` blocks, within the setup function of a component declared with `defineNuxtComponent`, in `defineNuxtPlugin` or in `defineNuxtRouteMiddleware`, where we perform a transform to keep the synchronous context even after the `await`.
|
||||
|
||||
If you get an error message like `Nuxt instance is unavailable` then it probably means you are calling a Nuxt composable in the wrong place in the Vue or Nuxt lifecycle.
|
||||
|
||||
See the full explanation in this [comment](https://github.com/nuxt/framework/issues/5740#issuecomment-1229197529).
|
||||
See the full explanation in this [comment](https://github.com/nuxt/nuxt/issues/14269#issuecomment-1397352832).
|
||||
|
||||
::NeedContribution
|
||||
::
|
||||
|
||||
#### Example
|
||||
##### Example
|
||||
|
||||
**Example:** Breaking code:
|
||||
|
||||
@ -88,7 +90,7 @@ export const useMyComposable = () => {
|
||||
}
|
||||
```
|
||||
|
||||
## Directory-based Auto-imports
|
||||
### Directory-based Auto-imports
|
||||
|
||||
Nuxt directly auto-imports files created in defined directories:
|
||||
|
||||
@ -96,12 +98,12 @@ Nuxt directly auto-imports files created in defined directories:
|
||||
- `composables/` for [Vue composables](/docs/guide/directory-structure/composables).
|
||||
- `utils/` for helper functions and other utilities.
|
||||
|
||||
## Explicit Imports
|
||||
### Explicit Imports
|
||||
|
||||
Nuxt exposes every auto-import with the `#imports` alias that can be used to make the import explicit if needed:
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from '#imports'
|
||||
|
||||
const count = ref(1)
|
||||
@ -109,9 +111,9 @@ Nuxt exposes every auto-import with the `#imports` alias that can be used to mak
|
||||
</script>
|
||||
```
|
||||
|
||||
## Disable Auto-imports
|
||||
### Disabling Auto-imports
|
||||
|
||||
In case you want to disable auto-imports, you can set `imports.autoImport` to `false` in your `nuxt.config.ts`.
|
||||
If you want to disable auto-importing composables and utilities, you can set `imports.autoImport` to `false` in the `nuxt.config` file.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
@ -121,4 +123,43 @@ export default defineNuxtConfig({
|
||||
})
|
||||
```
|
||||
|
||||
This will disable implicit auto imports completely but it's still possible to use [Explicit Imports](#explicit-imports).
|
||||
This will disable auto-imports completely but it's still possible to use [explicit imports](#explicit-imports) from `#imports`.
|
||||
|
||||
## Auto-imported Components
|
||||
|
||||
Nuxt also automatically imports components from your `~/components` directory, although this is configured separately from auto-importing composables and utility functions.
|
||||
|
||||
:ReadMore{link="/docs/guide/directory-structure/components"}
|
||||
|
||||
To disable auto-importing components from your own `~/components` directory, you can set `components.dirs` to an empty array (though note that this will not affect components added by modules).
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
components: {
|
||||
dirs: []
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Auto-import from third-party packages
|
||||
|
||||
Nuxt also allows auto-importing from third-party packages.
|
||||
|
||||
::alert
|
||||
If you are using the Nuxt module for that package, it is likely that the module has already configured auto-imports for that package.
|
||||
::
|
||||
|
||||
For example, you could enable the auto-import of the `useI18n` composable from the `vue-i18n` package like this:
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
imports: {
|
||||
presets: [
|
||||
{
|
||||
from: 'vue-i18n',
|
||||
imports: ['useI18n']
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
@ -36,7 +36,7 @@ Most applications need multiple pages and a way to navigate between them. This i
|
||||
|
||||
The `app.vue` file is the entry point, which represents the page displayed in the browser window.
|
||||
|
||||
Inside the `<template>` of the component, we use the `<Welcome>` component created in the `components/` directory without having to import it.
|
||||
Inside the `<template>` of the component, we use the `<Welcome>` component created in the [`components/` directory](/docs/guide/directory-structure/components) without having to import it.
|
||||
|
||||
Try to replace the `<template>`’s content with a custom welcome message. The browser window on the right will automatically render the changes without reloading.
|
||||
|
||||
@ -94,7 +94,7 @@ The [Composition API](https://vuejs.org/guide/extras/composition-api-faq.html) i
|
||||
Used with the `setup` keyword in the `<script>` definition, here is the above component rewritten with Composition API and Nuxt 3’s auto-imported Reactivity APIs:
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
const count = ref(0);
|
||||
const increment = () => count.value++;
|
||||
</script>
|
||||
@ -103,7 +103,7 @@ Used with the `setup` keyword in the `<script>` definition, here is the above co
|
||||
The goal of Nuxt 3 is to provide a great developer experience around the Composition API.
|
||||
|
||||
- Use auto-imported [Reactivity functions](https://vuejs.org/api/reactivity-core.html) from Vue and Nuxt 3 [built-in composables](/docs/api/composables/use-async-data).
|
||||
- Write your own auto-imported reusable functions in the `composables/` directory.
|
||||
- Write your own auto-imported reusable functions in the [`composables/` directory](/docs/guide/directory-structure/composables).
|
||||
|
||||
### TypeScript Support
|
||||
|
||||
|
@ -1,40 +1,20 @@
|
||||
---
|
||||
description: "Nuxt supports different rendering modes. Each one has pros and cons covered in this section."
|
||||
description: "Nuxt supports different rendering modes, universal rendering, client-side rendering but also offers hybrid-rendering and the possibility to render on CDN Edge Servers."
|
||||
---
|
||||
|
||||
# Rendering Modes
|
||||
|
||||
Both the browser and server can interpret JavaScript code to render Vue.js components into HTML elements. This step is called **rendering**. Nuxt supports both **client-side** and **universal** rendering. The two approaches have pros and cons that we will cover in this section.
|
||||
Nuxt supports different rendering modes, [universal rendering](#universal-rendering), [client-side rendering](#client-side-rendering) but also offers [hybrid-rendering](#hybrid-rendering) and the possibility to render your application on [CDN Edge Servers](#edge-side-rendering).
|
||||
|
||||
## Client-side Only Rendering
|
||||
Both the browser and server can interpret JavaScript code to turn Vue.js components into HTML elements. This step is called **rendering**. Nuxt supports both **universal** and **client-side** rendering. The two approaches have benefits and downsides that we will cover.
|
||||
|
||||
Out of the box, a traditional Vue.js application is rendered in the browser (or **client**). Then, Vue.js generates HTML elements after the browser downloads and parses all the JavaScript code containing the instructions to create the current interface.
|
||||
|
||||
![Users have to wait for the browser to download, parse and execute the JavaScript before seeing the page's content](/assets/docs/concepts/rendering/light/csr.svg){.light-img.dark:hidden}
|
||||
![Users have to wait for the browser to download, parse and execute the JavaScript before seeing the page's content](/assets/docs/concepts/rendering/dark/csr.svg){.dark-img.hidden.dark:block}
|
||||
|
||||
While this technique allows building complex and dynamic UIs with smooth page transitions, it has different pros and cons:
|
||||
|
||||
### Pros
|
||||
|
||||
- **Development speed**: When working entirely on the client-side, we don't have to worry about the server compatibility of the code, for example, by using browser-only APIs like the `window` object.
|
||||
- **Cheaper:** Running a server adds a cost of infrastructure as you would need to run on a platform that supports JavaScript. We can host Client-only applications on any static server with HTML, CSS, and JavaScript files.
|
||||
- **Offline:** Because code entirely runs in the browser, it can nicely keep working while the internet is unavailable.
|
||||
|
||||
### Cons
|
||||
|
||||
- **Performance**: The user has to wait for the browser to download, parse and run JavaScript files. Depending on the network for the download part and the user's device for the parsing and execution, this can take some time and impact the user's experience.
|
||||
- **Search Engine Optimization**: Indexing and updating the content delivered via client-side rendering takes more time than with a server-rendered HTML document. This is related to the performance drawback we discussed, as search engine crawlers won't wait for the interface to be fully rendered on their first try to index the page. Your content will take more time to show and update in search results pages with pure client-side rendering.
|
||||
|
||||
### Examples
|
||||
|
||||
Client-side rendering is a good choice for heavily interactive **web applications** that don't need indexing or whose users visit frequently. It can leverage browser caching to skip the download phase on subsequent visits, such as **SaaS, back-office applications, or online games**.
|
||||
By default, Nuxt uses **universal rendering** to provide better user experience, performance and to optimize search engine indexing, but you can switch rendering modes in [one line of configuration](/docs/api/configuration/nuxt-config#ssr).
|
||||
|
||||
## Universal Rendering
|
||||
|
||||
When the browser requests a URL with universal (client-side + server-side) rendering enabled, the server returns a fully rendered HTML page to the browser. Whether the page has been generated in advance and cached or is rendered on the fly, at some point, Nuxt has run the JavaScript (Vue.js) code in a server environment, producing an HTML document. Users immediately get the content of our application, contrary to client-side rendering. This step is similar to traditional **server-side rendering** performed by PHP or Ruby applications.
|
||||
When the browser requests a URL with universal (server-side + client-side) rendering enabled, the server returns a fully rendered HTML page to the browser. Whether the page has been generated in advance and cached or is rendered on the fly, at some point, Nuxt has run the JavaScript (Vue.js) code in a server environment, producing an HTML document. Users immediately get the content of our application, contrary to client-side rendering. This step is similar to traditional **server-side rendering** performed by PHP or Ruby applications.
|
||||
|
||||
To not lose the benefits of the client-side rendering method, such as dynamic interfaces and pages transitions, the Client loads the javascript code that runs on the Server in the background once the HTML document has been downloaded. The browser interprets it again (hence **Universal rendering**) and Vue.js takes control of the document and enables interactivity.
|
||||
To not lose the benefits of the client-side rendering method, such as dynamic interfaces and pages transitions, the Client (browser) loads the JavaScript code that runs on the Server in the background once the HTML document has been downloaded. The browser interprets it again (hence **Universal rendering**) and Vue.js takes control of the document and enables interactivity.
|
||||
|
||||
Making a static page interactive in the browser is called "Hydration."
|
||||
|
||||
@ -43,15 +23,15 @@ Universal rendering allows a Nuxt application to provide quick page load times w
|
||||
![Users can access the static content when the HTML document is loaded. Hydration then allows page's interactivity](/assets/docs/concepts/rendering/light/ssr.svg){.light-img.dark:hidden}
|
||||
![Users can access the static content when the HTML document is loaded. Hydration then allows page's interactivity](/assets/docs/concepts/rendering/dark/ssr.svg){.dark-img.hidden.dark:block}
|
||||
|
||||
### Pros
|
||||
|
||||
**Benefits of server-side rendering:**
|
||||
- **Performance**: Users can get immediate access to the page's content because browsers can display static content much faster than JavaScript-generated one. At the same time, Nuxt preserves the interactivity of a web application when the hydration process happens.
|
||||
- **Search Engine Optimization**: Universal rendering delivers the entire HTML content of the page to the browser as a classic server application. Web crawlers can directly index the page's content, which makes Universal rendering a great choice for any content that you want to index quickly.
|
||||
|
||||
### Cons
|
||||
|
||||
**Downsides of server-side rendering:**
|
||||
- **Development constraints:** Server and browser environments don't provide the same APIs, and it can be tricky to write code that can run on both sides seamlessly. Fortunately, Nuxt provides guidelines and specific variables to help you determine where a piece of code is executed.
|
||||
- **Cost:** A server needs to be running in order to render pages on the fly. This adds a monthly cost like any traditional server. However, the server calls are highly reduced thanks to universal rendering with the browser taking over on client-side navigation.
|
||||
- **Cost:** A server needs to be running in order to render pages on the fly. This adds a monthly cost like any traditional server. However, the server calls are highly reduced thanks to universal rendering with the browser taking over on client-side navigation. A cost reduction is possible by leveraging [edge-side-rendering](#edge-side-rendering).
|
||||
|
||||
Universal rendering is very versatile and can fit almost any use case, and is especially appropriate for any content-oriented websites: **blogs, marketing websites, portfolios, e-commerce sites, and marketplaces.**
|
||||
|
||||
::alert
|
||||
For more examples about writing Vue code without hydration mismatch, see [the Vue docs](https://vuejs.org/guide/scaling-up/ssr.html#hydration-mismatch).
|
||||
@ -61,64 +41,107 @@ For more examples about writing Vue code without hydration mismatch, see [the Vu
|
||||
When importing a library that relies on browser APIs and has side effects, make sure the component importing it is only called client-side. Bundlers do not treeshake imports of modules containing side effects.
|
||||
::
|
||||
|
||||
### Examples
|
||||
## Client-Side Rendering
|
||||
|
||||
Universal rendering is very versatile and can fit almost any use case, and is especially appropriate for any content-oriented websites: **blogs, marketing websites, portfolios, e-commerce sites, and marketplaces.**
|
||||
Out of the box, a traditional Vue.js application is rendered in the browser (or **client**). Then, Vue.js generates HTML elements after the browser downloads and parses all the JavaScript code containing the instructions to create the current interface.
|
||||
|
||||
## Summary
|
||||
![Users have to wait for the browser to download, parse and execute the JavaScript before seeing the page's content](/assets/docs/concepts/rendering/light/csr.svg){.light-img.dark:hidden}
|
||||
![Users have to wait for the browser to download, parse and execute the JavaScript before seeing the page's content](/assets/docs/concepts/rendering/dark/csr.svg){.dark-img.hidden.dark:block}
|
||||
|
||||
Client-side and universal rendering are different strategies to display an interface in a browser.
|
||||
**Benefits of client-side rendering:**
|
||||
- **Development speed**: When working entirely on the client-side, we don't have to worry about the server compatibility of the code, for example, by using browser-only APIs like the `window` object.
|
||||
- **Cheaper:** Running a server adds a cost of infrastructure as you would need to run on a platform that supports JavaScript. We can host Client-only applications on any static server with HTML, CSS, and JavaScript files.
|
||||
- **Offline:** Because code entirely runs in the browser, it can nicely keep working while the internet is unavailable.
|
||||
|
||||
By default, Nuxt uses **universal rendering** to provide better user experience and performance, and to optimize search engine indexing, but you can switch rendering modes in [one line of configuration](/docs/api/configuration/nuxt-config#ssr).
|
||||
**Downsides of client-side rendering:**
|
||||
- **Performance**: The user has to wait for the browser to download, parse and run JavaScript files. Depending on the network for the download part and the user's device for the parsing and execution, this can take some time and impact the user's experience.
|
||||
- **Search Engine Optimization**: Indexing and updating the content delivered via client-side rendering takes more time than with a server-rendered HTML document. This is related to the performance drawback we discussed, as search engine crawlers won't wait for the interface to be fully rendered on their first try to index the page. Your content will take more time to show and update in search results pages with pure client-side rendering.
|
||||
|
||||
## New Rendering Patterns in Nuxt 3
|
||||
Client-side rendering is a good choice for heavily interactive **web applications** that don't need indexing or whose users visit frequently. It can leverage browser caching to skip the download phase on subsequent visits, such as **SaaS, back-office applications, or online games**.
|
||||
|
||||
In most cases, universal rendering as performed in Nuxt 2 offers a good user and developer experience. However, Nuxt 3 takes universal rendering a step further by introducing hybrid rendering and edge-side rendering.
|
||||
You can enable client-side only rendering with Nuxt in your `nuxt.config.ts`:
|
||||
|
||||
### Hybrid Rendering
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
ssr: false
|
||||
})
|
||||
```
|
||||
|
||||
::alert{type=info}
|
||||
If you do use `ssr: false`, you should also place an HTML file in `~/app/spa-loading-template.html` with some HTML you would like to use to render a loading screen that will be rendered until your app is hydrated.
|
||||
:ReadMore{link="/docs/api/configuration/nuxt-config#spaloadingtemplate"}
|
||||
::
|
||||
|
||||
## Hybrid Rendering
|
||||
|
||||
Hybrid rendering allows different caching rules per route using **Route Rules** and decides how the server should respond to a new request on a given URL.
|
||||
|
||||
### Rendering on CDN Edge Workers
|
||||
Previously every route/page of a Nuxt application and server must use the same rendering mode, universal or client-side. In various cases, some pages could be generated at build time, while others should be client-side rendered. For example, think of a content website with an admin section. Every content page should be primarily static and generated once, but the admin section requires registration and behaves more like a dynamic application.
|
||||
|
||||
Traditionally, server-side and universal rendering was only possible using Node.js. Nuxt 3 takes it to another level by directly rendering code in CDN edge workers, reducing latency and costs.
|
||||
Nuxt 3 includes route rules and hybrid rendering support. Using route rules you can define rules for a group of nuxt routes, change rendering mode or assign a cache strategy based on route!
|
||||
|
||||
Nitro is the new [server engine](/docs/guide/concepts/server-engine) that powers Nuxt 3. It offers cross-platform support for Node.js, Deno, Workers, and more. Nitro's design is platform-agnostic and allows rendering a Nuxt application at the edge, closer to your users, allowing replication and further optimizations.
|
||||
Nuxt server will automatically register corresponding middleware and wrap routes with cache handlers using [Nitro caching layer](https://nitro.unjs.io/guide/cache).
|
||||
|
||||
### Route Rules
|
||||
**Example:**
|
||||
|
||||
> 🧪 Route rules are still under active development, and subject to change.
|
||||
|
||||
Previously every route/page of a Nuxt application and server must use the same rendering mode, client-side or universal. But in various cases, some pages could be generated at build time, while others should be client-side rendered. For example, think of a content website with an admin section. Every content page should be primarily static and generated once, but the admin section requires registration and behaves more like a dynamic application.
|
||||
|
||||
Nuxt 3 includes route rules and hybrid rendering support. Using route rules you can define rules for a group of nuxt routes, change rendering mode or assign a cache strategy based on route! Nuxt server will automatically register corresponding middleware and wrap routes with cache handlers using [nitro caching layer](https://nitro.unjs.io/guide/cache). Whenever possible, route rules will be automatically applied to the deployment platform's native rules (currently Netlify and Vercel are supported).
|
||||
|
||||
- `redirect` - Define server-side redirects.
|
||||
- `ssr` - Disables server-side rendering for sections of your app and make them SPA-only with `ssr: false`
|
||||
- `cors` - Automatically adds cors headers with `cors: true` - you can customize the output by overriding with `headers`
|
||||
- `headers` - Add specific headers to sections of your site - for example, your assets
|
||||
- `swr` - Add cache headers to the server response and cache it in the server or reverse proxy for a configurable TTL. The `node-server` preset of Nitro is able to cache the full response. For Netlify and Vercel, the response is also added to the CDN layer.
|
||||
- `static` - The behavior is the same as `swr` except that there is no TTL; the response is cached until the next deployment. On Netlify and Vercel, it enables full incremental static generation.
|
||||
- `prerender` - Prerenders routes at build time and includes them in your build as static assets
|
||||
|
||||
**Examples:**
|
||||
|
||||
```ts
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
routeRules: {
|
||||
// Static page generated on-demand, revalidates in background
|
||||
'/blog/**': { swr: true },
|
||||
// Static page generated on-demand once
|
||||
'/articles/**': { static: true },
|
||||
// Set custom headers matching paths
|
||||
'/_nuxt/**': { headers: { 'cache-control': 's-maxage=0' } },
|
||||
// Render these routes with SPA
|
||||
// Homepage pre-rendered at build time
|
||||
'/': { prerender: true },
|
||||
// Product page generated on-demand, revalidates in background
|
||||
'/products/**': { swr: 3600 },
|
||||
// Blog post generated on-demand once until next deploy
|
||||
'/blog/**': { isr: true },
|
||||
// Admin dashboard renders only on client-side
|
||||
'/admin/**': { ssr: false },
|
||||
// Add cors headers
|
||||
'/api/v1/**': { cors: true },
|
||||
// Add redirect headers
|
||||
'/old-page': { redirect: '/new-page' },
|
||||
'/old-page2': { redirect: { to: '/new-page', statusCode: 302 } }
|
||||
// Add cors headers on API routes
|
||||
'/api/**': { cors: true },
|
||||
// Redirects legacy urls
|
||||
'/old-page': { redirect: '/new-page' }
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
The different properties you can use are the following:
|
||||
- `redirect: string`{lang=ts} - Define server-side redirects.
|
||||
- `ssr: boolean`{lang=ts} - Disables server-side rendering for sections of your app and make them SPA-only with `ssr: false`
|
||||
- `cors: boolean`{lang=ts} - Automatically adds cors headers with `cors: true` - you can customize the output by overriding with `headers`
|
||||
- `headers: object`{lang=ts} - Add specific headers to sections of your site - for example, your assets
|
||||
- `swr: number|boolean`{lang=ts} - Add cache headers to the server response and cache it on the server or reverse proxy for a configurable TTL (time to live). The `node-server` preset of Nitro is able to cache the full response. When the TTL expired, the cached response will be sent while the page will be regenerated in the background. If true is used, a `stale-while-revalidate` header is added without a MaxAge.
|
||||
- `isr: number|boolean`{lang=ts} - The behavior is the same as `swr` except that we are able to add the response to the CDN cache on platforms that support this (currently Netlify or Vercel). If `true` is used, the content persists until the next deploy inside the CDN.
|
||||
- `prerender:boolean`{lang=ts} - Prerenders routes at build time and includes them in your build as static assets
|
||||
- `experimentalNoScripts: boolean`{lang=ts} - Disables rendering of Nuxt scripts and JS resource hints for sections of your site.
|
||||
|
||||
Whenever possible, route rules will be automatically applied to the deployment platform's native rules for optimal performances (Netlify and Vercel are currently supported).
|
||||
|
||||
::alert{type="warning"}
|
||||
Note that Hybrid Rendering is not available when using `nuxt generate`.
|
||||
::
|
||||
|
||||
**Examples:**
|
||||
- [Nuxt + Vercel integration with hybrid rendering](https://github.com/danielroe/nuxt-vercel-isr)
|
||||
|
||||
## Edge-Side Rendering
|
||||
|
||||
Edge-Side Rendering (ESR) is a powerful feature introduced in Nuxt 3 that allows the rendering of your Nuxt application closer to your users via edge servers of a Content Delivery Network (CDN). By leveraging ESR, you can ensure improved performance and reduced latency, thereby providing an enhanced user experience.
|
||||
|
||||
With ESR, the rendering process is pushed to the 'edge' of the network - the CDN's edge servers. Note that ESR is more a deployment target than an actual rendering mode.
|
||||
|
||||
When a request for a page is made, instead of going all the way to the original server, it's intercepted by the nearest edge server. This server generates the HTML for the page and sends it back to the user. This process minimizes the physical distance the data has to travel, **reducing latency and loading the page faster**.
|
||||
|
||||
Edge-side rendering is possible thanks to [Nitro](https://nitro.unjs.io), the [server engine](/docs/guide/concepts/server-engine) that powers Nuxt 3. It offers cross-platform support for Node.js, Deno, Cloudflare Workers, and more.
|
||||
|
||||
The current platforms where you can leverage ESR are:
|
||||
- [Cloudflare Pages](https://pages.cloudflare.com/) with zero configuration using the git integration and the `nuxt build` command
|
||||
- [Lagon](https://lagon.app) using the `NITRO_PRESET=lagon npx nuxt build` command
|
||||
- [Vercel Edge Functions](https://vercel.com/features/edge-functions) using the `nuxt build` command and `NITRO_PRESET=vercel-edge` environment variable
|
||||
- [Netlify Edge Functions](https://www.netlify.com/products/#netlify-edge-functions) using the `nuxt build` command and `NITRO_PRESET=netlify-edge` environment variable
|
||||
|
||||
Note that **Hybrid Rendering** can be used when using Edge-Side Rendering with route rules.
|
||||
|
||||
You can explore open source examples deployed on some of the platform mentioned above:
|
||||
- [Nuxt Todos Edge](https://github.com/atinux/nuxt-todos-edge): A todos application with user authentication, SSR and SQLite.
|
||||
- [Atinotes](https://github.com/atinux/atinotes): An editable website with universal rendering.
|
||||
|
||||
<!-- TODO: link to templates with ESR category for examples -->
|
||||
|
@ -24,7 +24,7 @@ Key features include:
|
||||
Check out [the h3 docs](https://github.com/unjs/h3) for more information.
|
||||
|
||||
::alert{type="info" icon=ℹ️}
|
||||
Learn more about the API layer in the [`server/`](/docs/guide/directory-structure/server) directory.
|
||||
Learn more about the API layer in the [`server/` directory](/docs/guide/directory-structure/server).
|
||||
::
|
||||
|
||||
## Direct API Calls
|
||||
@ -54,6 +54,6 @@ Nuxt 3 generates this dist when running `nuxt build` into a [`.output`](/docs/gu
|
||||
|
||||
The output contains runtime code to run your Nuxt server in any environment (including experimental browser service workers!) and serve your static files, making it a true hybrid framework for the JAMstack. In addition, Nuxt implements a native storage layer, supporting multi-source drivers and local assets.
|
||||
|
||||
::alert{type="info" icon=IconCode}
|
||||
::alert{type="info" icon=ℹ️}
|
||||
Check out the Nitro engine on GitHub: [unjs/nitro](https://github.com/unjs/nitro).
|
||||
::
|
||||
|
@ -19,7 +19,7 @@ Best of all, Nuxt modules can be distributed in npm packages. This makes it poss
|
||||
|
||||
## The `modules` Property
|
||||
|
||||
Once you have installed the modules you can then add them to your `nuxt.config.ts` file under the `modules` property. Module developers usually provide additional steps and details for usage.
|
||||
Once you have installed the modules you can then add them to your [`nuxt.config.ts`](/docs/guide/directory-structure/nuxt.config) file under the `modules` property. Module developers usually provide additional steps and details for usage.
|
||||
|
||||
```ts{}[nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
|
@ -7,7 +7,7 @@ head.title: ".nuxt/"
|
||||
|
||||
# .nuxt Directory
|
||||
|
||||
Nuxt uses the `.nuxt/` directory in development to generate your Vue application.
|
||||
Nuxt uses the [`.nuxt/` directory](/docs/guide/directory-structure/nuxt) in development to generate your Vue application.
|
||||
|
||||
::alert{type=warning}
|
||||
You should not touch any files inside since the whole directory will be re-created when running `nuxt dev`.
|
||||
|
@ -7,7 +7,7 @@ head.title: ".output/"
|
||||
|
||||
# Output Directory
|
||||
|
||||
Nuxt creates the `.output/` directory when building your application for production.
|
||||
Nuxt creates the [`.output/` directory](/docs/guide/directory-structure/output) when building your application for production.
|
||||
|
||||
::alert{type=warning}
|
||||
You should not touch any files inside since the whole directory will be re-created when running `nuxt build`.
|
||||
|
@ -7,7 +7,7 @@ head.title: "assets/"
|
||||
|
||||
# Assets Directory
|
||||
|
||||
The `assets/` directory is used to add all the website's assets that the build tool (webpack or Vite) will process.
|
||||
The [`assets/` directory](/docs/guide/directory-structure/assets) is used to add all the website's assets that the build tool (webpack or Vite) will process.
|
||||
|
||||
The directory usually contains the following types of files:
|
||||
|
||||
|
@ -1,15 +1,15 @@
|
||||
---
|
||||
navigation.icon: IconDirectory
|
||||
title: "components"
|
||||
description: "The components/ directory is where you put all your Vue components."
|
||||
description: "The components/ directory is where you put all your Vue components."
|
||||
head.title: "components/"
|
||||
---
|
||||
|
||||
# Components Directory
|
||||
|
||||
The `components/` directory is where you put all your Vue components which can then be imported inside your pages or other components ([learn more](https://vuejs.org/guide/essentials/component-basics.html#components-basics)).
|
||||
The [`components/` directory](/docs/guide/directory-structure/components) is where you put all your Vue components which can then be imported inside your pages or other components ([learn more](https://vuejs.org/guide/essentials/component-basics.html#components-basics)).
|
||||
|
||||
Nuxt automatically imports any components in your `components/` directory (along with components that are registered by any modules you may be using).
|
||||
Nuxt automatically imports any components in your [`components/` directory](/docs/guide/directory-structure/components) (along with components that are registered by any modules you may be using).
|
||||
|
||||
```bash
|
||||
| components/
|
||||
@ -34,7 +34,20 @@ By default, only the `~/components` directory is scanned. If you want to add oth
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
components: [
|
||||
// ~/calendar-module/components/event/Update.vue => <EventUpdate />
|
||||
{ path: '~/calendar-module/components' },
|
||||
|
||||
// ~/user-module/components/account/UserDeleteDialog.vue => <UserDeleteDialog />
|
||||
{ path: '~/user-module/components', pathPrefix: false },
|
||||
|
||||
// ~/components/special-components/Btn.vue => <SpecialBtn />
|
||||
{ path: '~/components/special-components', prefix: 'Special' },
|
||||
|
||||
// It's important that this comes last if you have overrides you wish to apply
|
||||
// to sub-directories of `~/components`.
|
||||
//
|
||||
// ~/components/Btn.vue => <Btn />
|
||||
// ~/components/base/Btn.vue => <BaseBtn />
|
||||
'~/components'
|
||||
]
|
||||
})
|
||||
@ -98,18 +111,21 @@ This registers the components using the same strategy as used in Nuxt 2. For exa
|
||||
|
||||
## Dynamic Components
|
||||
|
||||
If you want to use the Vue `<component :is="someComputedComponent">` syntax, then you will need to use the `resolveComponent` helper provided by Vue.
|
||||
If you want to use the Vue `<component :is="someComputedComponent">` syntax, you need to use the `resolveComponent` helper provided by Vue or import the component directly from `#components` and pass it into `is` prop.
|
||||
|
||||
For example:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<component :is="clickable ? MyButton : 'div'" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { SomeComponent } from '#components'
|
||||
|
||||
<script setup>
|
||||
const MyButton = resolveComponent('MyButton')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component :is="clickable ? MyButton : 'div'" />
|
||||
<component :is="SomeComponent" />
|
||||
</template>
|
||||
```
|
||||
|
||||
::alert{type=warning}
|
||||
@ -150,14 +166,6 @@ To dynamically import a component (also known as lazy-loading a component) all y
|
||||
This is particularly useful if the component is not always needed. By using the `Lazy` prefix you can delay loading the component code until the right moment, which can be helpful for optimizing your JavaScript bundle size.
|
||||
|
||||
```html [pages/index.vue]
|
||||
<template>
|
||||
<div>
|
||||
<h1>Mountains</h1>
|
||||
<LazyMountainsList v-if="show" />
|
||||
<button v-if="!show" @click="show = true">Show List</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
@ -167,6 +175,14 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h1>Mountains</h1>
|
||||
<LazyMountainsList v-if="show" />
|
||||
<button v-if="!show" @click="show = true">Show List</button>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Direct Imports
|
||||
@ -174,6 +190,11 @@ export default {
|
||||
You can also explicitly import components from `#components` if you want or need to bypass Nuxt's auto-importing functionality.
|
||||
|
||||
```html [pages/index.vue]
|
||||
<script setup lang="ts">
|
||||
import { NuxtLink, LazyMountainsList } from '#components'
|
||||
const show = ref(false)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h1>Mountains</h1>
|
||||
@ -182,11 +203,6 @@ You can also explicitly import components from `#components` if you want or need
|
||||
<NuxtLink to="/">Home</NuxtLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { NuxtLink, LazyMountainsList } from '#components'
|
||||
const show = ref(false)
|
||||
</script>
|
||||
```
|
||||
|
||||
## `<ClientOnly>` Component
|
||||
@ -296,8 +312,23 @@ Now you can register server-only components with the `.server` suffix and use th
|
||||
</template>
|
||||
```
|
||||
|
||||
::alert{type=warning}
|
||||
Slots are not supported by server components in their current state of development.
|
||||
Server-only components use `<NuxtIsland>` under the hood, meaning that `lazy` prop and `#fallback` slot are both passed down to `<NuxtIsland>`.
|
||||
|
||||
#### Server Component Context
|
||||
|
||||
When rendering a server-only or island component, `<NuxtIsland>` makes a fetch request which comes back with a `NuxtIslandResponse`. (This is an internal request if rendered on the server, or a request that you can see in the network tab if it's rendering on client-side navigation.)
|
||||
|
||||
This means:
|
||||
|
||||
- A new Vue app will be created server-side to create the `NuxtIslandResponse`.
|
||||
- A new 'island context' will be created while rendering the component.
|
||||
- You can't access the 'island context' from the rest of your app and you can't access the context of the rest of your app from the island component. In other words, the server component or island is _isolated_ from the rest of your app.
|
||||
- Your plugins will run again when rendering the island, unless they have `env: { islands: false }` set (which you can do in an object-syntax plugin).
|
||||
|
||||
Within an island component, you can access its island context through `nuxtApp.ssrContext.islandContext`. Note that while island components are still marked as experimental, the format of this context may change.
|
||||
|
||||
::alert{type=info}
|
||||
Slots can be interactive and are wrapped within a `<div>` with `display: contents;`
|
||||
::
|
||||
|
||||
### Paired with a `.client` component
|
||||
@ -313,7 +344,7 @@ In this case, the `.server` + `.client` components are two 'halves' of a compone
|
||||
```html [pages/example.vue]
|
||||
<template>
|
||||
<div>
|
||||
<!-- this component will render Comments.server server-side then Comments.client once mounted in client-side -->
|
||||
<!-- this component will render Comments.server on the server then Comments.client once mounted in the browser -->
|
||||
<Comments />
|
||||
</div>
|
||||
</template>
|
||||
@ -336,6 +367,12 @@ The content will not be included in production builds and tree-shaken.
|
||||
<DevOnly>
|
||||
<!-- this component will only be rendered during development -->
|
||||
<LazyDebugBar />
|
||||
|
||||
<!-- if you ever require to have a replacement during production -->
|
||||
<!-- be sure to test these using `nuxt preview` -->
|
||||
<template #fallback>
|
||||
<div><!-- empty div for flex.justify-between --></div>
|
||||
</template>
|
||||
</DevOnly>
|
||||
</div>
|
||||
</template>
|
||||
@ -390,7 +427,7 @@ export default defineNuxtModule({
|
||||
const { resolve } = createResolver(import.meta.url)
|
||||
// Add ./components dir to the list
|
||||
dirs.push({
|
||||
path: fileURLToPath(resolve('./components')),
|
||||
path: resolve('./components'),
|
||||
prefix: 'awesome'
|
||||
})
|
||||
}
|
||||
@ -419,4 +456,4 @@ export default defineNuxtConfig({
|
||||
|
||||
It will automatically import the components only if used and also support HMR when updating your components in `node_modules/awesome-ui/components/`.
|
||||
|
||||
:LinkExample{link="/docs/examples/auto-imports/components"}
|
||||
:LinkExample{link="/docs/examples/features/auto-imports"}
|
||||
|
@ -7,7 +7,7 @@ description: Use the composables/ directory to auto-import your Vue composables
|
||||
|
||||
# Composables Directory
|
||||
|
||||
Nuxt 3 uses the `composables/` directory to automatically import your Vue composables into your application using [auto-imports](/docs/guide/concepts/auto-imports)!
|
||||
Nuxt 3 uses the [`composables/` directory](/docs/guide/directory-structure/composables) to automatically import your Vue composables into your application using [auto-imports](/docs/guide/concepts/auto-imports)!
|
||||
|
||||
Under the hood, Nuxt auto generates the file `.nuxt/imports.d.ts` to declare the types.
|
||||
|
||||
@ -35,18 +35,18 @@ export default function () {
|
||||
**Usage:** You can now use auto imported composable in `.js`, `.ts` and `.vue` files
|
||||
|
||||
```vue [app.vue]
|
||||
<script setup lang="ts">
|
||||
const foo = useFoo()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
{{ foo }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const foo = useFoo()
|
||||
</script>
|
||||
```
|
||||
|
||||
::LinkExample{link="/docs/examples/auto-imports/composables"}
|
||||
::LinkExample{link="/docs/examples/features/auto-imports"}
|
||||
::
|
||||
|
||||
## Examples
|
||||
@ -75,7 +75,7 @@ export const useHello = () => {
|
||||
|
||||
## How Files Are Scanned
|
||||
|
||||
Nuxt only scans files at the top level of the `composables/` directory, e.g.:
|
||||
Nuxt only scans files at the top level of the [`composables/` directory](/docs/guide/directory-structure/composables), e.g.:
|
||||
|
||||
```bash
|
||||
composables
|
||||
|
@ -7,7 +7,7 @@ description: The Content module reads the content/ directory to create a file-ba
|
||||
|
||||
# Content Directory
|
||||
|
||||
The [Nuxt Content module](https://content.nuxtjs.org) reads the `content/` directory in your project and parses `.md`, `.yml`, `.csv` and `.json` files to create a file-based CMS for your application.
|
||||
The [Nuxt Content module](https://content.nuxtjs.org) reads the [`content/` directory](/docs/guide/directory-structure/content) in your project and parses `.md`, `.yml`, `.csv` and `.json` files to create a file-based CMS for your application.
|
||||
|
||||
::list{type=success}
|
||||
|
||||
@ -38,6 +38,10 @@ Install the `@nuxt/content` module in your project:
|
||||
pnpm add -D @nuxt/content
|
||||
```
|
||||
|
||||
```bash [bun]
|
||||
bun add -D @nuxt/content
|
||||
```
|
||||
|
||||
::
|
||||
|
||||
Then, add `@nuxt/content` to the `modules` section of `nuxt.config.ts`:
|
||||
@ -55,7 +59,7 @@ export default defineNuxtConfig({
|
||||
|
||||
### Create Content
|
||||
|
||||
Place your markdown files inside the `content/` directory in the root directory of your project:
|
||||
Place your markdown files inside the [`content/` directory](/docs/guide/directory-structure/content) in the root directory of your project:
|
||||
|
||||
```md [content/index.md]
|
||||
# Hello Content
|
||||
@ -67,7 +71,7 @@ The module automatically loads and parses them.
|
||||
|
||||
To render content pages, add a [catch-all route](/docs/guide/directory-structure/pages/#catch-all-route) using the `ContentDoc` component:
|
||||
|
||||
```vue [pages/[...slug].vue]
|
||||
```vue [pages/[...slug\\].vue]
|
||||
<template>
|
||||
<main>
|
||||
<ContentDoc />
|
||||
|
@ -9,7 +9,7 @@ head.title: "layouts/"
|
||||
|
||||
Nuxt provides a customizable layouts framework you can use throughout your application, ideal for extracting common UI or code patterns into reusable layout components.
|
||||
|
||||
Layouts are placed in the `layouts/` directory and will be automatically loaded via asynchronous import when used. Layouts are used by adding `<NuxtLayout>` to your `app.vue`, and either setting a `layout` property as part of your page metadata (if you are using the `~/pages` integration), or by manually specifying it as a prop to `<NuxtLayout>`. (**Note**: The layout name is normalized to kebab-case, so `someLayout` becomes `some-layout`.)
|
||||
Layouts are placed in the [`layouts/` directory](/docs/guide/directory-structure/layouts) and will be automatically loaded via asynchronous import when used. Layouts are used by adding `<NuxtLayout>` to your `app.vue`, and either setting a `layout` property as part of your page metadata (if you are using the `~/pages` integration), or by manually specifying it as a prop to `<NuxtLayout>`. (**Note**: The layout name is normalized to kebab-case, so `someLayout` becomes `some-layout`.)
|
||||
|
||||
If you only have a single layout in your application, we recommend using [app.vue](/docs/guide/directory-structure/app) instead.
|
||||
|
||||
@ -53,16 +53,16 @@ If you use a `app.vue` you will also need to add `<NuxtLayout>`:
|
||||
You can directly override the default layout like this:
|
||||
|
||||
```vue{}[app.vue]
|
||||
<script setup lang="ts">
|
||||
// You might choose this based on an API call or logged-in status
|
||||
const layout = "custom";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NuxtLayout :name="layout">
|
||||
<NuxtPage />
|
||||
</NuxtLayout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// You might choose this based on an API call or logged-in status
|
||||
const layout = "custom";
|
||||
</script>
|
||||
```
|
||||
|
||||
Alternatively, you can override the default layout per-page like this:
|
||||
@ -115,13 +115,7 @@ Learn more about [defining page meta](/docs/guide/directory-structure/pages#page
|
||||
You can also use a ref or computed property for your layout.
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<button @click="enableCustomLayout">Update layout</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
function enableCustomLayout () {
|
||||
setPageLayout('custom')
|
||||
}
|
||||
@ -129,9 +123,15 @@ definePageMeta({
|
||||
layout: false,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<button @click="enableCustomLayout">Update layout</button>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
::LinkExample{link="/docs/examples/routing/layouts"}
|
||||
::LinkExample{link="/docs/examples/features/layouts"}
|
||||
::
|
||||
|
||||
## Overriding a Layout on a Per-page Basis
|
||||
@ -141,6 +141,12 @@ If you are using the `~/pages` integration, you can take full control by setting
|
||||
::code-group
|
||||
|
||||
```vue [pages/index.vue]
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: false,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<NuxtLayout name="custom">
|
||||
@ -150,12 +156,6 @@ If you are using the `~/pages` integration, you can take full control by setting
|
||||
</NuxtLayout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
layout: false,
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
```vue [layouts/custom.vue]
|
||||
|
@ -16,8 +16,8 @@ Route middleware run within the Vue part of your Nuxt app. Despite the similar n
|
||||
There are three kinds of route middleware:
|
||||
|
||||
1. Anonymous (or inline) route middleware, which are defined directly in the pages where they are used.
|
||||
2. Named route middleware, which are placed in the `middleware/` directory and will be automatically loaded via asynchronous import when used on a page. (**Note**: The route middleware name is normalized to kebab-case, so `someMiddleware` becomes `some-middleware`.)
|
||||
3. Global route middleware, which are placed in the `middleware/` directory (with a `.global` suffix) and will be automatically run on every route change.
|
||||
2. Named route middleware, which are placed in the [`middleware/` directory](/docs/guide/directory-structure/middleware) and will be automatically loaded via asynchronous import when used on a page. (**Note**: The route middleware name is normalized to kebab-case, so `someMiddleware` becomes `some-middleware`.)
|
||||
3. Global route middleware, which are placed in the [`middleware/` directory](/docs/guide/directory-structure/middleware) (with a `.global` suffix) and will be automatically run on every route change.
|
||||
|
||||
The first two kinds of route middleware can be [defined in `definePageMeta`](/docs/guide/directory-structure/pages).
|
||||
|
||||
@ -30,7 +30,12 @@ export default defineNuxtRouteMiddleware((to, from) => {
|
||||
if (to.params.id === '1') {
|
||||
return abortNavigation()
|
||||
}
|
||||
// In a real app you would probably not redirect every route to `/`
|
||||
// however it is important to check `to.path` before redirecting or you
|
||||
// might get an infinite redirect loop
|
||||
if (to.path !== '/') {
|
||||
return navigateTo('/')
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
@ -75,7 +80,7 @@ middleware/
|
||||
```
|
||||
|
||||
```vue [pages/profile.vue]
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
middleware: [
|
||||
function (to, from) {
|
||||
@ -155,7 +160,7 @@ export default defineNuxtPlugin(() => {
|
||||
In your page file, you can reference this route middleware
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
middleware: ["auth"]
|
||||
// or middleware: 'auth'
|
||||
|
@ -7,7 +7,7 @@ description: Use the modules/ directory to automatically register local modules
|
||||
|
||||
# Modules Directory
|
||||
|
||||
Nuxt scans the `modules/` directory and loads them before starting. It is a good place to place any local modules you develop while building your application.
|
||||
Nuxt scans the [`modules/` directory](/docs/guide/directory-structure/modules) and loads them before starting. It is a good place to place any local modules you develop while building your application.
|
||||
|
||||
The auto-registered files patterns are:
|
||||
- `modules/*/index.ts`
|
||||
@ -41,14 +41,14 @@ export default defineNuxtModule({
|
||||
```ts [modules/hello/runtime/api-route.ts]
|
||||
export default defineEventHandler(() => {
|
||||
return { hello: 'world' }
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
::
|
||||
|
||||
When starting Nuxt, the `hello` module will be registered and the `/api/hello` route will be available.
|
||||
|
||||
The local modules are registered by alphabetical order. You can change the order by prefixing with a number if front of each directory:
|
||||
Local modules are registered in alphabetical order. You can change the order by adding a number to the front of each directory name:
|
||||
|
||||
```md
|
||||
modules/
|
||||
|
@ -7,4 +7,4 @@ head.title: "node_modules/"
|
||||
|
||||
# Node modules Directory
|
||||
|
||||
The package manager ([`npm`](https://docs.npmjs.com/cli/v7/commands/npm) or [`yarn`](https://yarnpkg.com/) or [`pnpm`](https://pnpm.io/cli/install)) creates the `node_modules/` directory to store the dependencies of your project.
|
||||
The package manager ([`npm`](https://docs.npmjs.com/cli/commands/npm) or [`yarn`](https://yarnpkg.com/) or [`pnpm`](https://pnpm.io/cli/install) or [`bun`](https://bun.sh/package-manager)) creates the [`node_modules/` directory](/docs/guide/directory-structure/node_modules) to store the dependencies of your project.
|
||||
|
@ -110,7 +110,7 @@ If you want a parameter to be _optional_, you must enclose it in double square b
|
||||
|
||||
Given the example above, you can access group/id within your component via the `$route` object:
|
||||
|
||||
```vue [pages/users-[group]/[id].vue]
|
||||
```vue [pages/users-[group\\]/[id\\].vue]
|
||||
<template>
|
||||
<p>{{ $route.params.group }} - {{ $route.params.id }}</p>
|
||||
</template>
|
||||
@ -122,10 +122,10 @@ Navigating to `/users-admins/123` would render:
|
||||
<p>admins - 123</p>
|
||||
```
|
||||
|
||||
If you want to access the route using Composition API, there is a global `useRoute` function that will allow you to access the route just like `this.$route` in the Options API.
|
||||
If you want to access the route using Composition API, there is a global [`useRoute`](/docs/api/composables/use-route) function that will allow you to access the route just like `this.$route` in the Options API.
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
const route = useRoute()
|
||||
|
||||
if (route.params.group === 'admins' && !route.params.id) {
|
||||
@ -134,11 +134,15 @@ if (route.params.group === 'admins' && !route.params.id) {
|
||||
</script>
|
||||
```
|
||||
|
||||
::alert{type="info"}
|
||||
Named parent routes will take priority over nested dynamic routes. For the `/foo/hello` route, `~/pages/foo.vue` will take priority over `~/pages/foo/[slug].vue` . Use `~/pages/foo/index.vue` and `~/pages/foo/[slug].vue` to match `/foo` and `/foo/hello` with different pages,.
|
||||
::
|
||||
|
||||
## Catch-all Route
|
||||
|
||||
If you need a catch-all route, you create it by using a file named like `[...slug].vue`. This will match _all_ routes under that path.
|
||||
|
||||
```vue [pages/[...slug].vue]
|
||||
```vue [pages/[...slug\\].vue]
|
||||
<template>
|
||||
<p>{{ $route.params.slug }}</p>
|
||||
</template>
|
||||
@ -182,7 +186,7 @@ This file tree will generate these routes:
|
||||
]
|
||||
```
|
||||
|
||||
To display the `child.vue` component, you have to insert the `<NuxtPage>` component inside `pages/parent.vue`:
|
||||
To display the `child.vue` component, you have to insert the `<NuxtPage>` component inside `pages/parent.vue`:
|
||||
|
||||
```html{}[pages/parent.vue]
|
||||
<template>
|
||||
@ -201,7 +205,7 @@ If you want more control over when the `<NuxtPage>` component is re-rendered (fo
|
||||
<template>
|
||||
<div>
|
||||
<h1>I am the parent view</h1>
|
||||
<NuxtPage :page-key="someKey" />
|
||||
<NuxtPage :page-key="route => route.fullPath" />
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
@ -209,7 +213,7 @@ If you want more control over when the `<NuxtPage>` component is re-rendered (fo
|
||||
Or alternatively:
|
||||
|
||||
```html{}[pages/child.vue]
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
key: route => route.fullPath
|
||||
})
|
||||
@ -224,7 +228,7 @@ definePageMeta({
|
||||
You might want to define metadata for each route in your app. You can do this using the `definePageMeta` macro, which will work both in `<script>` and in `<script setup>`:
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
title: 'My home page'
|
||||
})
|
||||
@ -234,7 +238,7 @@ definePageMeta({
|
||||
This data can then be accessed throughout the rest of your app from the `route.meta` object.
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
const route = useRoute()
|
||||
|
||||
console.log(route.meta.title) // My home page
|
||||
@ -246,7 +250,7 @@ If you are using nested routes, the page metadata from all these routes will be
|
||||
Much like `defineEmits` or `defineProps` (see [Vue docs](https://vuejs.org/api/sfc-script-setup.html#defineprops-defineemits)), `definePageMeta` is a **compiler macro**. It will be compiled away so you cannot reference it within your component. Instead, the metadata passed to it will be hoisted out of the component. Therefore, the page meta object cannot reference the component (or values defined on the component). However, it can reference imported bindings.
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
import { someData } from '~/utils/example'
|
||||
|
||||
const title = ref('')
|
||||
@ -317,7 +321,7 @@ export {}
|
||||
|
||||
## Navigation
|
||||
|
||||
To navigate between pages of your app, you should use the [`<NuxtLink>`](/docs/api/components/nuxt-link) component.
|
||||
To navigate between pages of your app, you should use the [`<NuxtLink>`](/docs/api/components/nuxt-link) component.
|
||||
|
||||
This component is included with Nuxt and therefore you don't have to import it as you do with other components.
|
||||
|
||||
@ -340,7 +344,7 @@ Nuxt 3 allows programmatic navigation through the `navigateTo()` utility method.
|
||||
**Note:** Ensure to always `await` on `navigateTo` or chain its result by returning from functions.
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
const name = ref('');
|
||||
const type = ref(1);
|
||||
|
||||
@ -361,3 +365,31 @@ function navigate(){
|
||||
As your app gets bigger and more complex, your routing might require more flexibility. For this reason, Nuxt directly exposes the router, routes and router options for customization in different ways.
|
||||
|
||||
:ReadMore{link="/docs/guide/going-further/custom-routing"}
|
||||
|
||||
## Multiple pages directories
|
||||
|
||||
By default, all your pages should be in one `pages` directory at the root of your project.
|
||||
However, you can use Layers to create groupings of your app's pages.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
-| nuxt.config.ts
|
||||
-| some-app/
|
||||
---| nuxt.config.ts
|
||||
---| pages
|
||||
-----| app-page.vue
|
||||
```
|
||||
|
||||
```ts
|
||||
// some-app/nuxt.config.ts
|
||||
export default defineNuxtConfig({
|
||||
})
|
||||
|
||||
// nuxt.config.ts
|
||||
export default defineNuxtConfig({
|
||||
extends: ['./some-app'],
|
||||
})
|
||||
```
|
||||
|
||||
:ReadMore{link="/docs/guide/going-further/layers"}
|
||||
|
@ -10,12 +10,12 @@ head.title: "plugins/"
|
||||
Nuxt automatically reads the files in your `plugins` directory and loads them at the creation of the Vue application. You can use `.server` or `.client` suffix in the file name to load a plugin only on the server or client side.
|
||||
|
||||
::alert{type=warning}
|
||||
All plugins in your `plugins/` directory are auto-registered, so you should not add them to your `nuxt.config` separately.
|
||||
All plugins in your [`plugins/` directory](/docs/guide/directory-structure/plugins) are auto-registered, so you should not add them to your `nuxt.config` separately.
|
||||
::
|
||||
|
||||
## Which Files Are Registered
|
||||
|
||||
Only files at the top level of the `plugins/` directory (or index files within any subdirectories) will be registered as plugins.
|
||||
Only files at the top level of the [`plugins/` directory](/docs/guide/directory-structure/plugins) (or index files within any subdirectories) will be registered as plugins.
|
||||
|
||||
For example:
|
||||
|
||||
@ -40,22 +40,71 @@ export default defineNuxtPlugin(nuxtApp => {
|
||||
})
|
||||
```
|
||||
|
||||
### Object Syntax Plugins
|
||||
|
||||
It is also possible to define a plugin using an object syntax, for more advanced use cases. For example:
|
||||
|
||||
```ts
|
||||
export default defineNuxtPlugin({
|
||||
name: 'my-plugin',
|
||||
enforce: 'pre', // or 'post'
|
||||
async setup (nuxtApp) {
|
||||
// this is the equivalent of a normal functional plugin
|
||||
},
|
||||
hooks: {
|
||||
// You can directly register Nuxt app hooks here
|
||||
'app:created'() {
|
||||
const nuxtApp = useNuxtApp()
|
||||
//
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
You can also use the `env` property to define the environments in which your plugin will run.
|
||||
|
||||
- **islands**
|
||||
- **default**: `true`
|
||||
- **description**: You can set this value to `false` if you don't want the plugin to run when rendering server-only or island components.
|
||||
|
||||
::alert
|
||||
If you are using an object-syntax plugin, the properties may be statically analyzed in future to produce a more optimized build. So you should not define them at runtime. For example, setting `enforce: process.server ? 'pre' : 'post'` would defeat any future optimization Nuxt is able to do for your plugins.
|
||||
::
|
||||
|
||||
## Plugin Registration Order
|
||||
|
||||
You can control the order in which plugins are registered by prefixing a number to the file names.
|
||||
You can control the order in which plugins are registered by prefixing with 'alphabetical' numbering to the file names.
|
||||
|
||||
For example:
|
||||
|
||||
```bash
|
||||
plugins/
|
||||
| - 1.myPlugin.ts
|
||||
| - 2.myOtherPlugin.ts
|
||||
| - 01.myPlugin.ts
|
||||
| - 02.myOtherPlugin.ts
|
||||
```
|
||||
|
||||
In this example, `2.myOtherPlugin.ts` will be able to access anything that was injected by `1.myPlugin.ts`.
|
||||
In this example, `02.myOtherPlugin.ts` will be able to access anything that was injected by `01.myPlugin.ts`.
|
||||
|
||||
This is useful in situations where you have a plugin that depends on another plugin.
|
||||
|
||||
::alert{type=info icon=💡}
|
||||
In case you're new to 'alphabetical' numbering, remember that filenames are sorted as strings, not as numeric values. For example, `10.myPlugin.ts` would come before `2.myOtherPlugin.ts`. This is why the example prefixes single digit numbers with `0`.
|
||||
::
|
||||
|
||||
## Loading strategy
|
||||
|
||||
By default, Nuxt loads plugins sequentially. You can define a plugin as `parallel` so Nuxt won't wait the end of the plugin's execution before loading the next plugin.
|
||||
|
||||
```ts
|
||||
export default defineNuxtPlugin({
|
||||
name: 'my-plugin',
|
||||
parallel: true,
|
||||
async setup (nuxtApp) {
|
||||
// the next plugin will be executed immediately
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Using Composables Within Plugins
|
||||
|
||||
You can use [composables](/docs/guide/directory-structure/composables) within Nuxt plugins:
|
||||
@ -93,16 +142,16 @@ export default defineNuxtPlugin(() => {
|
||||
In another file you can use this:
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
// alternatively, you can also use it here
|
||||
const { $hello } = useNuxtApp()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
{{ $hello('world') }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// alternatively, you can also use it here
|
||||
const { $hello } = useNuxtApp()
|
||||
</script>
|
||||
```
|
||||
|
||||
## Typing Plugins
|
||||
@ -141,8 +190,6 @@ If you are using WebStorm, you may need to augment `@vue/runtime-core` until [th
|
||||
|
||||
If you want to use Vue plugins, like [vue-gtag](https://github.com/MatteoGabriele/vue-gtag) to add Google Analytics tags, you can use a Nuxt plugin to do so.
|
||||
|
||||
> There is an Open RFC to make this even easier! See [nuxt/nuxt#17143](https://github.com/nuxt/nuxt/discussions/17143)
|
||||
|
||||
First, install the plugin you want.
|
||||
|
||||
```bash
|
||||
@ -182,7 +229,8 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||
})
|
||||
```
|
||||
|
||||
:ReadMore{link="https://vuejs.org/guide/reusability/custom-directives.html"}
|
||||
|
||||
::LinkExample{link="/docs/examples/app/plugins"}
|
||||
::alert{type=warning}
|
||||
If you register a Vue directive, you _must_ register it on both client and server side unless you are only using it when rendering one side. If the directive only makes sense from a client side, you can always move it to `~/plugins/my-directive.client.ts` and provide a 'stub' directive for the server in `~/plugins/my-directive.server.ts`.
|
||||
::
|
||||
|
||||
:ReadMore{link="https://vuejs.org/guide/reusability/custom-directives.html"}
|
||||
|
@ -7,8 +7,8 @@ head.title: "public/"
|
||||
|
||||
# Public Directory
|
||||
|
||||
The `public/` directory is directly served at the server root and contains public files that have to keep their names (e.g. `robots.txt`) _or_ likely won't change (e.g. `favicon.ico`).
|
||||
The [`public/` directory](/docs/guide/directory-structure/public) is directly served at the server root and contains public files that have to keep their names (e.g. `robots.txt`) _or_ likely won't change (e.g. `favicon.ico`).
|
||||
|
||||
::alert{icon=💡}
|
||||
This is known as the [`static/`](https://nuxtjs.org/docs/directory-structure/static) directory in Nuxt 2.
|
||||
This is known as the [`static/` directory](https://nuxtjs.org/docs/directory-structure/static) in Nuxt 2.
|
||||
::
|
||||
|
@ -7,33 +7,46 @@ description: The server/ directory is used to register API and server handlers t
|
||||
|
||||
# Server Directory
|
||||
|
||||
Nuxt automatically scans files inside the `~/server/api`, `~/server/routes`, and `~/server/middleware` directories to register API and server handlers with HMR support.
|
||||
Nuxt automatically scans files inside these directories to register API and server handlers with HMR support:
|
||||
- `~/server/api`
|
||||
- `~/server/routes`
|
||||
- `~/server/middleware`
|
||||
|
||||
Each file should export a default function defined with `defineEventHandler()`.
|
||||
Each file should export a default function defined with `defineEventHandler()` or `eventHandler()` (alias).
|
||||
|
||||
The handler can directly return JSON data, a `Promise` or use `event.node.res.end()` to send response.
|
||||
The handler can directly return JSON data, a `Promise`, or use `event.node.res.end()` to send a response.
|
||||
|
||||
::ReadMore{link="https://nitro.unjs.io/guide/routing" title="Nitro Route Handling Docs"}
|
||||
::
|
||||
|
||||
## Example
|
||||
|
||||
Create a new file in `server/api/hello.ts`:
|
||||
**Example:** Create the `/api/hello` route with `server/api/hello.ts` file:
|
||||
|
||||
```ts [server/api/hello.ts]
|
||||
export default defineEventHandler((event) => {
|
||||
return {
|
||||
api: 'works'
|
||||
hello: 'world'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
You can now universally call this API using `await $fetch('/api/hello')`.
|
||||
You can now universally call this API in your pages and components:
|
||||
|
||||
```vue [pages/index.vue]
|
||||
<script setup lang="ts">
|
||||
const { data } = await useFetch('/api/hello')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<pre>{{ data }}</pre>
|
||||
</template>
|
||||
```
|
||||
|
||||
Note that [h3 utilities](https://github.com/unjs/h3#utilities) are auto-imported.
|
||||
|
||||
:ReadMore{link="https://nitro.unjs.io/guide/routing" title="Nitro Route Handling Docs"}
|
||||
|
||||
## Server Routes
|
||||
|
||||
Files inside the `~/server/api` are automatically prefixed with `/api` in their route.
|
||||
For adding server routes without `/api` prefix, you can instead put them into `~/server/routes` directory.
|
||||
|
||||
To add server routes without `/api` prefix, put them into `~/server/routes` directory.
|
||||
|
||||
**Example:**
|
||||
|
||||
@ -43,6 +56,10 @@ export default defineEventHandler(() => 'Hello World!')
|
||||
|
||||
Given the example above, the `/hello` route will be accessible at <http://localhost:3000/hello>.
|
||||
|
||||
::alert{type=info icon=💡}
|
||||
Note that currently server routes do not support the full functionality of dynamic routes as [pages](https://nuxt.com/docs/guide/directory-structure/pages#dynamic-routes) do.
|
||||
::
|
||||
|
||||
## Server Middleware
|
||||
|
||||
Nuxt will automatically read in any file in the `~/server/middleware` to create server middleware for your project.
|
||||
@ -57,7 +74,7 @@ Middleware handlers should not return anything (nor close or respond to the requ
|
||||
|
||||
```ts [server/middleware/log.ts]
|
||||
export default defineEventHandler((event) => {
|
||||
console.log('New request: ' + event.node.req.url)
|
||||
console.log('New request: ' + getRequestURL(event))
|
||||
})
|
||||
```
|
||||
|
||||
@ -79,18 +96,55 @@ export default defineNitroPlugin((nitroApp) => {
|
||||
})
|
||||
```
|
||||
|
||||
::ReadMore{link="https://nitro.unjs.io/guide/plugins" title="Nitro Plugins"}
|
||||
::
|
||||
:ReadMore{link="https://nitro.unjs.io/guide/plugins" title="Nitro Plugins"}
|
||||
|
||||
## Server Utilities
|
||||
|
||||
Server routes are powered by [unjs/h3](https://github.com/unjs/h3) which comes with a handy set of helpers.
|
||||
|
||||
::ReadMore{link="https://www.jsdocs.io/package/h3#package-index-functions" title="Available H3 Request Helpers"}
|
||||
::
|
||||
:ReadMore{link="https://www.jsdocs.io/package/h3#package-index-functions" title="Available H3 Request Helpers"}
|
||||
|
||||
You can add more helpers yourself inside the `~/server/utils` directory.
|
||||
|
||||
For example, you can define a custom handler utility that wraps the original handler and performs additional operations before returning the final response.
|
||||
|
||||
**Example:**
|
||||
|
||||
```ts [server/utils/handler.ts]
|
||||
import type { EventHandler, EventHandlerRequest } from 'h3'
|
||||
|
||||
export const defineWrappedResponseHandler = <T extends EventHandlerRequest, D> (
|
||||
handler: EventHandler<T, D>
|
||||
): EventHandler<T, D> =>
|
||||
defineEventHandler<T>(async event => {
|
||||
try {
|
||||
// do something before the route handler
|
||||
const response = await handler(event)
|
||||
// do something after the route handler
|
||||
return { response }
|
||||
} catch (err) {
|
||||
// Error handling
|
||||
return { err }
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Server Types
|
||||
|
||||
::alert{type="info"}
|
||||
This feature is available from Nuxt >= 3.5
|
||||
::
|
||||
|
||||
To improve clarity within your IDE between the auto-imports from 'nitro' and 'vue', you can add a `~/server/tsconfig.json` with the following content:
|
||||
|
||||
```json [server/tsconfig.json]
|
||||
{
|
||||
"extends": "../.nuxt/tsconfig.server.json"
|
||||
}
|
||||
```
|
||||
|
||||
Although right now these values won't be respected when type checking (`nuxi typecheck`), you should get better type hints in your IDE.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Matching Route Parameters
|
||||
@ -99,8 +153,11 @@ Server routes can use dynamic parameters within brackets in the file name like `
|
||||
|
||||
**Example:**
|
||||
|
||||
```ts [server/api/hello/[name].ts]
|
||||
export default defineEventHandler((event) => `Hello, ${event.context.params.name}!`)
|
||||
```ts [server/api/hello/[name\\].ts]
|
||||
export default defineEventHandler((event) => {
|
||||
const name = getRouterParam(event, 'name')
|
||||
return `Hello, ${name}!`
|
||||
})
|
||||
```
|
||||
|
||||
You can now universally call this API using `await $fetch('/api/hello/nuxt')` and get `Hello, nuxt!`.
|
||||
@ -123,17 +180,49 @@ Given the example above, fetching `/test` with:
|
||||
- **POST** method: Returns `Test post handler`
|
||||
- Any other method: Returns 405 error
|
||||
|
||||
You can also use `index.[method].ts` inside a directory for structuring your code differently.
|
||||
|
||||
**Example:**
|
||||
|
||||
```ts [server/api/foo/index.ts]
|
||||
export default defineEventHandler((event) => {
|
||||
// handle the `api/foo` endpoint
|
||||
})
|
||||
```
|
||||
|
||||
This is useful to create API namespaces.
|
||||
|
||||
**Examples:**
|
||||
|
||||
```ts [server/api/foo/index.get.ts]
|
||||
export default defineEventHandler((event) => {
|
||||
// handle GET requests for the `api/foo` endpoint
|
||||
})
|
||||
```
|
||||
|
||||
```ts [server/api/foo/index.post.ts]
|
||||
export default defineEventHandler((event) => {
|
||||
// handle POST requests for the `api/foo` endpoint
|
||||
})
|
||||
```
|
||||
|
||||
```ts [server/api/foo/bar.get.ts]
|
||||
export default defineEventHandler((event) => {
|
||||
// handle GET requests for the `api/foo/bar` endpoint
|
||||
})
|
||||
```
|
||||
|
||||
### Catch-all Route
|
||||
|
||||
Catch-all routes are helpful for fallback route handling. For example, creating a file named `~/server/api/foo/[...].ts` will register a catch-all route for all requests that do not match any route handler, such as `/api/foo/bar/baz`.
|
||||
|
||||
**Examples:**
|
||||
|
||||
```ts [server/api/foo/[...].ts]
|
||||
```ts [server/api/foo/[...\\].ts]
|
||||
export default defineEventHandler(() => `Default foo handler`)
|
||||
```
|
||||
|
||||
```ts [server/api/[...].ts]
|
||||
```ts [server/api/[...\\].ts]
|
||||
export default defineEventHandler(() => `Default api handler`)
|
||||
```
|
||||
|
||||
@ -169,7 +258,7 @@ If no errors are thrown, a status code of `200 OK` will be returned. Any uncaugh
|
||||
|
||||
To return other error codes, throw an exception with `createError`
|
||||
|
||||
```ts [server/api/validation/[id].ts]
|
||||
```ts [server/api/validation/[id\\].ts]
|
||||
export default defineEventHandler((event) => {
|
||||
const id = parseInt(event.context.params.id) as number
|
||||
if (!Number.isInteger(id)) {
|
||||
@ -188,7 +277,7 @@ To return other status codes, you can use the `setResponseStatus` utility.
|
||||
|
||||
For example, to return `202 Accepted`
|
||||
|
||||
```ts [server/api/validation/[id].ts]
|
||||
```ts [server/api/validation/[id\\].ts]
|
||||
export default defineEventHandler((event) => {
|
||||
setResponseStatus(event, 202)
|
||||
})
|
||||
@ -232,7 +321,7 @@ export default defineNuxtConfig({
|
||||
|
||||
### Using a Nested Router
|
||||
|
||||
```ts [server/api/hello/[...slug].ts]
|
||||
```ts [server/api/hello/[...slug\\].ts]
|
||||
import { createRouter, defineEventHandler, useBase } from 'h3'
|
||||
|
||||
const router = createRouter()
|
||||
@ -258,8 +347,8 @@ export default defineEventHandler((event) => {
|
||||
### Sending Redirect
|
||||
|
||||
```ts [server/api/foo.get.ts]
|
||||
export default defineEventHandler((event) => {
|
||||
return sendRedirect(event, '/path/redirect/to', 302)
|
||||
export default defineEventHandler(async (event) => {
|
||||
await sendRedirect(event, '/path/redirect/to', 302)
|
||||
})
|
||||
```
|
||||
|
||||
@ -288,10 +377,12 @@ Never combine `next()` callback with a legacy middleware that is `async` or retu
|
||||
|
||||
### Server Storage
|
||||
|
||||
Nitro provides a cross-platform [storage layer](https://nitro.unjs.io/guide/storage). In order to configure additional storage mount points, you can use `nitro.storage`.
|
||||
Nitro provides a cross-platform [storage layer](https://nitro.unjs.io/guide/storage). In order to configure additional storage mount points, you can use `nitro.storage`, or [server plugins](#server-plugins).
|
||||
|
||||
#### Example: Using Redis
|
||||
|
||||
Using `nitro.storage`:
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
nitro: {
|
||||
@ -311,6 +402,43 @@ export default defineNuxtConfig({
|
||||
})
|
||||
```
|
||||
|
||||
Alternatively, using server plugins:
|
||||
|
||||
::code-group
|
||||
|
||||
```ts [server/plugins/storage.ts]
|
||||
import redisDriver from 'unstorage/drivers/redis'
|
||||
|
||||
export default defineNitroPlugin(() => {
|
||||
const storage = useStorage()
|
||||
|
||||
// Dynamically pass in credentials from runtime configuration, or other sources
|
||||
const driver = redisDriver({
|
||||
base: 'redis',
|
||||
host: useRuntimeConfig().redis.host,
|
||||
port: useRuntimeConfig().redis.port,
|
||||
/* other redis connector options */
|
||||
})
|
||||
|
||||
// Mount driver
|
||||
storage.mount('redis', driver)
|
||||
})
|
||||
```
|
||||
|
||||
``` ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
runtimeConfig: {
|
||||
redis: { // Default values
|
||||
host: '',
|
||||
port: 0,
|
||||
/* other redis connector options */
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
::
|
||||
|
||||
Create a new file in `server/api/test.post.ts`:
|
||||
|
||||
```ts [server/api/test.post.ts]
|
||||
@ -333,13 +461,6 @@ export default defineEventHandler(async (event) => {
|
||||
Create a new file in `app.vue`:
|
||||
|
||||
```vue [app.vue]
|
||||
<template>
|
||||
<div>
|
||||
<div>Post state: {{ resDataSuccess }}</div>
|
||||
<div>Get Data: {{ resData.text }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { data: resDataSuccess } = await useFetch('/api/test', {
|
||||
method: 'post',
|
||||
@ -347,6 +468,13 @@ Create a new file in `app.vue`:
|
||||
})
|
||||
const { data: resData } = await useFetch('/api/test')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div>Post state: {{ resDataSuccess }}</div>
|
||||
<div>Get Data: {{ resData.text }}</div>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
::ReadMore{link="/docs/guide/directory-structure/server"}
|
||||
|
@ -7,10 +7,10 @@ description: Use the utils/ directory to auto-import your utility functions thro
|
||||
|
||||
# Utils Directory
|
||||
|
||||
Nuxt 3 uses the `utils/` directory to automatically import helper functions and other utilities throughout your application using [auto-imports](/docs/guide/concepts/auto-imports)!
|
||||
Nuxt 3 uses the [`utils/` directory](/docs/guide/directory-structure/utils) to automatically import helper functions and other utilities throughout your application using [auto-imports](/docs/guide/concepts/auto-imports)!
|
||||
|
||||
::alert{type=info}
|
||||
The main purpose of the `utils/` directory is to allow a semantic distinction between your Vue composables and other auto-imported utility functions.
|
||||
The main purpose of the [`utils/` directory](/docs/guide/directory-structure/utils) is to allow a semantic distinction between your Vue composables and other auto-imported utility functions.
|
||||
|
||||
The way `utils/` auto-imports work and are scanned is identical to [the composables/ directory](/docs/guide/directory-structure/composables). You can see examples and more information about how they work in that section of the docs.
|
||||
::
|
||||
|
@ -7,6 +7,8 @@ head.title: ".env"
|
||||
|
||||
# .env File
|
||||
|
||||
## At Build, Dev, and Generate Time
|
||||
|
||||
Nuxt CLI has built-in [dotenv](https://github.com/motdotla/dotenv) support in development mode and when running `nuxi build` and `nuxi generate`.
|
||||
|
||||
In addition to any process environment variables, if you have a `.env` file in your project root directory, it will be automatically loaded **at build, dev, and generate time**, and any environment variables set there will be accessible within your `nuxt.config` file and modules.
|
||||
@ -27,7 +29,17 @@ When updating `.env` in development mode, the Nuxt instance is automatically res
|
||||
Note that removing a variable from `.env` or removing the `.env` file entirely will not unset values that have already been set.
|
||||
::
|
||||
|
||||
However, **after your server is built**, you are responsible for setting environment variables when you run the server. Your `.env` file will not be read at this point. How you do this is different for every environment. On a Linux server, you could pass the environment variables as arguments using the terminal `DATABASE_HOST=mydatabaseconnectionstring node .output/server/index.mjs`. Or you could source your env file using `source .env && node .output/server/index.mjs`.
|
||||
## Production Preview
|
||||
|
||||
**After your server is built**, you are responsible for setting environment variables when you run the server. Your `.env` file will not be read at this point. How you do this is different for every environment.
|
||||
|
||||
For local production preview purpose, we recommend using [`nuxi preview`](https://nuxt.com/docs/api/commands/preview) since using this command, the `.env` file will be loaded into `process.env` for convenience. Note that this command requires dependencies to be installed in the package directory.
|
||||
|
||||
Or you could pass the environment variables as arguments using the terminal. For example, on Linux or macOS:
|
||||
|
||||
```bash
|
||||
DATABASE_HOST=mydatabaseconnectionstring node .output/server/index.mjs
|
||||
```
|
||||
|
||||
Note that for a purely static site, it is not possible to set runtime configuration config after your project is prerendered.
|
||||
|
||||
|
@ -15,8 +15,18 @@ We recommend having a `.gitignore` file that has **at least** the following entr
|
||||
# Nuxt dev/build outputs
|
||||
.output
|
||||
.nuxt
|
||||
|
||||
# Node dependencies
|
||||
node_modules
|
||||
# System files
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
|
||||
# Local env files
|
||||
.env.*
|
||||
!.env.example
|
||||
```
|
||||
|
@ -9,7 +9,7 @@ description: The .nuxtignore file lets Nuxt ignore files in your project’s roo
|
||||
|
||||
The `.nuxtignore` file lets Nuxt ignore `layouts`, `pages`, `components`, `composables` and `middleware` files in your project’s root directory (`rootDir`) during the build phase. The `.nuxtignore` file is subject to the same specification as `.gitignore` and `.eslintignore` files, in which each line is a glob pattern indicating which files should be ignored.
|
||||
|
||||
**Note**: You can also configure [`ignoreOptions`](/docs/guide/directory-structure/nuxt.config#ignoreoptions), [`ignorePrefix`](/docs/guide/directory-structure/nuxt.config#ignoreprefix) and [`ignore`](/docs/guide/directory-structure/nuxt.config#ignore) in your `nuxt.config` file.
|
||||
**Note**: You can also configure [`ignoreOptions`](/docs/api/configuration/nuxt-config#ignoreoptions), [`ignorePrefix`](/docs/api/configuration/nuxt-config#ignoreprefix) and [`ignore`](/docs/api/configuration/nuxt-config#ignore) in your `nuxt.config` file.
|
||||
|
||||
## Example
|
||||
|
||||
|
@ -91,3 +91,32 @@ declare module 'nuxt/schema' {
|
||||
// It is always important to ensure you import/export something when augmenting a type
|
||||
export {}
|
||||
```
|
||||
|
||||
### App Config Merging Strategy in Layers
|
||||
|
||||
Nuxt uses a custom merging strategy for the `AppConfig` within the layers of your application.
|
||||
This strategy is implemented using a [Function Merger](https://github.com/unjs/defu#function-merger), which allows defining a custom merging strategy for every key in `app.config` that has an array as value.
|
||||
|
||||
::alert{type=warning}
|
||||
The Function Merger should only be used in the base `app.config` of your application.
|
||||
::
|
||||
|
||||
Here's an example of how you can use:
|
||||
|
||||
::code-group
|
||||
|
||||
```ts [layer/app.config.ts]
|
||||
export default defineAppConfig({
|
||||
// Default array value
|
||||
array: ['hello'],
|
||||
})
|
||||
```
|
||||
|
||||
```ts [app.config.ts]
|
||||
export default defineAppConfig({
|
||||
// Overwrite default array value by using a merger function
|
||||
array: () => ['bonjour'],
|
||||
})
|
||||
```
|
||||
|
||||
::
|
||||
|
@ -34,7 +34,7 @@ If you have a [`pages/`](/docs/guide/directory-structure/pages) directory, to di
|
||||
```
|
||||
|
||||
::alert{type=danger}
|
||||
Since Nuxt 3 uses [`<Suspense>`](https://vuejs.org/guide/built-ins/suspense.html#suspense) inside `<NuxtPage>`, it cannot be set as a root element.
|
||||
Since `<NuxtPage>` internally uses the [`<Suspense>`](https://vuejs.org/guide/built-ins/suspense.html#suspense) component, `<NuxtPage>` cannot be set as a root element.
|
||||
::
|
||||
|
||||
::alert{type=warning}
|
||||
|
@ -2,9 +2,9 @@
|
||||
navigation.icon: IconFile
|
||||
title: package.json
|
||||
head.title: package.json
|
||||
description: The package.json file contains all the dependencies and scripts for your application.
|
||||
description: The package.json file contains all the dependencies and scripts for your application.
|
||||
---
|
||||
|
||||
# Package.json File
|
||||
|
||||
The `package.json` file contains all the dependencies and scripts for your application ([learn more](https://docs.npmjs.com/cli/v7/configuring-npm/package-json)).
|
||||
The `package.json` file contains all the dependencies and scripts for your application ([learn more](https://docs.npmjs.com/cli/configuring-npm/package-json)).
|
||||
|
238
docs/2.guide/3.going-further/1.experimental-features.md
Normal file
238
docs/2.guide/3.going-further/1.experimental-features.md
Normal file
@ -0,0 +1,238 @@
|
||||
---
|
||||
title: "Experimental Features"
|
||||
description: "Nuxt experimental features needs to be enabled manually."
|
||||
---
|
||||
|
||||
# Experimental Features
|
||||
|
||||
The Nuxt experimental features can be enabled in the Nuxt configuration file.
|
||||
Internally, Nuxt uses `@nuxt/schema` to define these experimental features. You can refer to the [API documentation](https://nuxt.com/docs/api/configuration/nuxt-config#experimental) or the [source code](https://github.com/nuxt/nuxt/blob/main/packages/schema/src/config/experimental.ts) for more information.
|
||||
|
||||
::alert{type=info icon=💡}
|
||||
Note that these features are experimental and could be removed or modified in the future.
|
||||
::
|
||||
|
||||
## asyncContext
|
||||
|
||||
Enable native async context to be accessible for nested composables in Nuxt and in Nitro. See [full explanation in #20918](https://github.com/nuxt/nuxt/pull/20918).
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export defineNuxtConfig({ experimental: { asyncContext: true } })
|
||||
```
|
||||
|
||||
## asyncEntry
|
||||
|
||||
Enables generation of an async entry point for the Vue bundle, aiding module federation support.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export defineNuxtConfig({ experimental: { asyncEntry: true } })
|
||||
```
|
||||
|
||||
## reactivityTransform
|
||||
|
||||
Enables Vue's reactivity transform. Note that this feature has been marked as deprecated in Vue 3.3 and is planned to be removed from core in Vue 3.4.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export defineNuxtConfig({ experimental: { reactivityTransform: true } })
|
||||
```
|
||||
|
||||
::ReadMore{link="/docs/getting-started/configuration#enabling-experimental-vue-features"}
|
||||
::
|
||||
|
||||
## externalVue
|
||||
|
||||
Externalizes `vue`, `@vue/*` and `vue-router` when building.
|
||||
*Enabled by default.*
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export defineNuxtConfig({ experimental: { externalVue: true } })
|
||||
```
|
||||
|
||||
::alert{type=warning icon=⚠️}
|
||||
This feature will likely be removed in Nuxt 3.6.
|
||||
::
|
||||
|
||||
## treeshakeClientOnly
|
||||
|
||||
Tree shakes contents of client-only components from server bundle.
|
||||
*Enabled by default.*
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export defineNuxtConfig({ experimental: { treeshakeClientOnly: true } })
|
||||
```
|
||||
|
||||
## emitRouteChunkError
|
||||
|
||||
Emits `app:chunkError` hook when there is an error loading vite/webpack chunks. Default behavior is to perform a hard reload of the new route when a chunk fails to load.
|
||||
You can disable automatic handling by setting this to `false`, or handle chunk errors manually by setting it to `manual`.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export defineNuxtConfig({ experimental: { emitRouteChunkError: 'automatic' } }) // or 'manual' or false
|
||||
```
|
||||
|
||||
## restoreState
|
||||
|
||||
Allows Nuxt app state to be restored from `sessionStorage` when reloading the page after a chunk error or manual `reloadNuxtApp()` call.
|
||||
To avoid hydration errors, it will be applied only after the Vue app has been mounted,
|
||||
meaning there may be a flicker on initial load.
|
||||
|
||||
::alert{type=warning icon=⚠️}
|
||||
Consider carefully before enabling this as it can cause unexpected behavior,
|
||||
and consider providing explicit keys to [`useState`](/docs/api/composables/use-state) as auto-generated keys may not match across builds.
|
||||
::
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export defineNuxtConfig({ experimental: { restoreState: true } })
|
||||
```
|
||||
|
||||
## inlineSSRStyles
|
||||
|
||||
Inlines styles when rendering HTML. This is currently available only when using Vite.
|
||||
You can also pass a function that receives the path of a Vue component and returns a boolean indicating whether to inline the styles for that component.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export defineNuxtConfig({ experimental: { inlineSSRStyles: true } }) // or a function to determine inlining
|
||||
```
|
||||
|
||||
## noScripts
|
||||
|
||||
Disables rendering of Nuxt scripts and JS resource hints. Can also be configured granularly within `routeRules`.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export defineNuxtConfig({ experimental: { noScripts: true } })
|
||||
```
|
||||
|
||||
## renderJsonPayloads
|
||||
|
||||
Allows rendering of JSON payloads with support for revivifying complex types.
|
||||
*Enabled by default.*
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export defineNuxtConfig({ experimental: { renderJsonPayloads: true } })
|
||||
```
|
||||
|
||||
## noVueServer
|
||||
|
||||
Disables Vue server renderer endpoint within Nitro.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export defineNuxtConfig({ experimental: { noVueServer: true } })
|
||||
```
|
||||
|
||||
## payloadExtraction
|
||||
|
||||
Enables extraction of payloads of pages generated with `nuxt generate`.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export defineNuxtConfig({ experimental: { payloadExtraction: true } })
|
||||
```
|
||||
|
||||
## clientFallback
|
||||
|
||||
Enables the experimental `<NuxtClientFallback>` component for rendering content on the client if there's an error in SSR.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export defineNuxtConfig({ experimental: { clientFallback: true } })
|
||||
```
|
||||
|
||||
## crossOriginPrefetch
|
||||
|
||||
Enables cross-origin prefetch using the Speculation Rules API.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export defineNuxtConfig({ experimental: { crossOriginPrefetch: true } })
|
||||
```
|
||||
|
||||
## viewTransition
|
||||
|
||||
Enables View Transition API integration with client-side router.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export defineNuxtConfig({ experimental: { viewTransition: true } })
|
||||
```
|
||||
|
||||
::ReadMore{link="/docs/getting-started/transitions#view-transitions-api-experimental"}
|
||||
::
|
||||
|
||||
## writeEarlyHints
|
||||
|
||||
Enables writing of early hints when using node server.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export defineNuxtConfig({ experimental: { writeEarlyHints: true } })
|
||||
```
|
||||
|
||||
## componentIslands
|
||||
|
||||
Enables experimental component islands support with `<NuxtIsland>` and `.island.vue` files.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export defineNuxtConfig({ experimental: { componentIslands: true } })
|
||||
```
|
||||
|
||||
::ReadMore{link="/docs/guide/directory-structure/components#server-components"}
|
||||
::
|
||||
|
||||
You can follow the server components roadmap on [GitHub](https://github.com/nuxt/nuxt/issues/19772).
|
||||
|
||||
## configSchema
|
||||
|
||||
Enables config schema support.
|
||||
*Enabled by default.*
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export defineNuxtConfig({ experimental: { configSchema: true } })
|
||||
```
|
||||
|
||||
## polyfillVueUseHead
|
||||
|
||||
Adds a compatibility layer for modules, plugins, or user code relying on the old `@vueuse/head` API.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export defineNuxtConfig({ experimental: { polyfillVueUseHead: false } })
|
||||
```
|
||||
|
||||
## respectNoSSRHeader
|
||||
|
||||
Allow disabling Nuxt SSR responses by setting the `x-nuxt-no-ssr` header.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export defineNuxtConfig({ experimental: { respectNoSSRHeader: false } })
|
||||
```
|
||||
|
||||
## localLayerAliases
|
||||
|
||||
Resolve `~`, `~~`, `@` and `@@` aliases located within layers with respect to their layer source and root directories.
|
||||
*Enabled by default.*
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export defineNuxtConfig({ experimental: { localLayerAliases: true } })
|
||||
```
|
||||
|
||||
## typedPages
|
||||
|
||||
Enable the new experimental typed router using [unplugin-vue-router](https://github.com/posva/unplugin-vue-router).
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export defineNuxtConfig({ experimental: { typedPages: false } })
|
||||
```
|
||||
|
||||
Out of the box, this will enable typed usage of `navigateTo`, `<NuxtLink>`, `router.push()` and more.
|
||||
You can even get typed params within a page by using `const route = useRoute('route-name')`.
|
||||
|
||||
## watcher
|
||||
|
||||
Set an alternative watcher that will be used as the watching service for Nuxt.
|
||||
Nuxt uses `chokidar-granular` by default, which will ignore top-level directories
|
||||
(like `node_modules` and `.git`) that are excluded from watching.
|
||||
You can set this instead to `parcel` to use `@parcel/watcher`, which may improve
|
||||
performance in large projects or on Windows platforms.
|
||||
You can also set this to `chokidar` to watch all files in your source directory.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export defineNuxtConfig({
|
||||
experimental: {
|
||||
watcher: 'chokidar-granular' // 'chokidar' or 'parcel' are also options
|
||||
}
|
||||
})
|
||||
```
|
@ -36,8 +36,6 @@ Check [Nuxt App](/docs/api/composables/use-nuxt-app) for more information about
|
||||
|
||||
`nuxtApp` has the following properties:
|
||||
|
||||
Note: this is an internal interface, and some properties might change until stable release.
|
||||
|
||||
```js
|
||||
const nuxtApp = {
|
||||
vueApp, // the global Vue application: https://vuejs.org/api/application.html#application-api
|
||||
|
@ -91,19 +91,19 @@ Within the Vue part of your Nuxt app, you will need to call `useRuntimeConfig()`
|
||||
The entire runtime config is available on the server-side, but it is read-only to avoid context sharing.
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<div>Check developer console!</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
const config = useRuntimeConfig()
|
||||
console.log('Runtime config:', config)
|
||||
if (process.server) {
|
||||
console.log('API secret:', config.apiSecret)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div>Check developer console!</div>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
**🛑 Security note:** Be careful not to expose runtime config keys to the client-side by either rendering them or passing them to `useState`.
|
||||
@ -150,9 +150,9 @@ It is also possible to type your runtime config manually:
|
||||
declare module 'nuxt/schema' {
|
||||
interface RuntimeConfig {
|
||||
apiSecret: string
|
||||
public: {
|
||||
apiBase: string
|
||||
}
|
||||
interface PublicRuntimeConfig {
|
||||
apiBase: string
|
||||
}
|
||||
}
|
||||
// It is always important to ensure you import/export something when augmenting a type
|
||||
|
@ -1,56 +0,0 @@
|
||||
---
|
||||
title: "Edge Channel"
|
||||
description: "Edge channel allows to use latest commits from the repository."
|
||||
---
|
||||
|
||||
# Edge Release Channel
|
||||
|
||||
Nuxt 3 is landing commits, improvements, and bug fixes every day. You can opt-in to test them earlier before the next release.
|
||||
|
||||
After each commit is merged into the `main` branch of [nuxt/nuxt](https://github.com/nuxt/nuxt) and **passing all tests**, we trigger an automated npm release using GitHub Actions publishing Nuxt 3 packages.
|
||||
|
||||
You can opt in to use this release channel and avoid waiting for the next release and helping Nuxt by beta testing changes.
|
||||
|
||||
The build and publishing method and quality of edge releases are the same as stable ones. The only difference is that you should often check the GitHub repository for updates. There is a slight chance of regressions not being caught during the review process and by the automated tests. Therefore, we internally use this channel to double-check everything before each release.
|
||||
|
||||
:::Alert
|
||||
Features only available on the edge channel are marked with an alert in the documentation.
|
||||
:::
|
||||
|
||||
## Opting Into the Edge Channel
|
||||
|
||||
Update `nuxt` dependency inside `package.json`:
|
||||
|
||||
```diff [package.json]
|
||||
{
|
||||
"devDependencies": {
|
||||
-- "nuxt": "^3.0.0"
|
||||
++ "nuxt": "npm:nuxt3@latest"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Remove lockfile (`package-lock.json`, `yarn.lock`, or `pnpm-lock.yaml`) and reinstall dependencies.
|
||||
|
||||
## Opting Out From the Edge Channel
|
||||
|
||||
Update `nuxt` dependency inside `package.json`:
|
||||
|
||||
```diff [package.json]
|
||||
{
|
||||
"devDependencies": {
|
||||
-- "nuxt": "npm:nuxt3@latest"
|
||||
++ "nuxt": "^3.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Remove lockfile (`package-lock.json`, `yarn.lock`, or `pnpm-lock.yaml`) and reinstall dependencies.
|
||||
|
||||
## Using Latest `nuxi` CLI From Edge
|
||||
|
||||
:::Alert
|
||||
All cli dependencies are bundled because of the building method for reducing `nuxi` package size. You can get dependency updates and CLI improvements using the edge channel.
|
||||
:::
|
||||
|
||||
You can use `npx nuxi-edge@latest [command]` to try the latest version of the nuxi CLI.
|
56
docs/2.guide/3.going-further/11.nightly-release-channel.md
Normal file
56
docs/2.guide/3.going-further/11.nightly-release-channel.md
Normal file
@ -0,0 +1,56 @@
|
||||
---
|
||||
title: "Nightly Release Channel"
|
||||
description: "The nightly release channel allows using Nuxt built directly from the latest commits to the repository."
|
||||
---
|
||||
|
||||
# Nightly Release Channel
|
||||
|
||||
Nuxt lands commits, improvements, and bug fixes every day. You can opt in to test them earlier before the next release.
|
||||
|
||||
After a commit is merged into the `main` branch of [nuxt/nuxt](https://github.com/nuxt/nuxt) and **passes all tests**, we trigger an automated npm release, using GitHub Actions.
|
||||
|
||||
You can use these 'nightly' releases to beta test new features and changes.
|
||||
|
||||
The build and publishing method and quality of these 'nightly' releases are the same as stable ones. The only difference is that you should often check the GitHub repository for updates. There is a slight chance of regressions not being caught during the review process and by the automated tests. Therefore, we internally use this channel to double-check everything before each release.
|
||||
|
||||
:::Alert
|
||||
Features that are only available on the nightly release channel are marked with an alert in the documentation.
|
||||
:::
|
||||
|
||||
## Opting Into the Nightly Release Channel
|
||||
|
||||
Update `nuxt` dependency inside `package.json`:
|
||||
|
||||
```diff [package.json]
|
||||
{
|
||||
"devDependencies": {
|
||||
-- "nuxt": "^3.0.0"
|
||||
++ "nuxt": "npm:nuxt-nightly@latest"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Remove lockfile (`package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, or `bun.lockb`) and reinstall dependencies.
|
||||
|
||||
## Opting Out From the Nightly Release Channel
|
||||
|
||||
Update `nuxt` dependency inside `package.json`:
|
||||
|
||||
```diff [package.json]
|
||||
{
|
||||
"devDependencies": {
|
||||
-- "nuxt": "npm:nuxt-nightly@latest"
|
||||
++ "nuxt": "^3.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Remove lockfile (`package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, or `bun.lockb`) and reinstall dependencies.
|
||||
|
||||
## Using Latest `nuxi` CLI From Nightly Release
|
||||
|
||||
:::Alert
|
||||
All cli dependencies are bundled because of the building method for reducing `nuxi` package size. You can get dependency updates and CLI improvements using the nightly release channel.
|
||||
:::
|
||||
|
||||
You can use `npx nuxi-edge@latest [command]` to try the latest version of the nuxi CLI.
|
@ -53,7 +53,7 @@ Learn more about [available lifecycle hooks](/docs/api/advanced/hooks)
|
||||
|
||||
## Nitro App Hooks (Runtime)
|
||||
|
||||
These hooks are available for [Nitro plugins](https://nitro.unjs.io/guide/advanced/plugins) to hook into Nitro's runtime behavior.
|
||||
These hooks are available for [Nitro plugins](https://nitro.unjs.io/guide/plugins) to hook into Nitro's runtime behavior.
|
||||
|
||||
### Usage within a Nitro Plugin
|
||||
|
||||
@ -71,5 +71,28 @@ export default defineNitroPlugin((nitroApp) => {
|
||||
```
|
||||
|
||||
::alert{icon=👉}
|
||||
Learn more about available [Nitro lifecycle hooks](/docs/api/advanced/hooks#nitro-hooks-runtime-server-side).
|
||||
Learn more about available [Nitro lifecycle hooks](/docs/api/advanced/hooks#nitro-app-hooks-runtime-server-side).
|
||||
::
|
||||
|
||||
## Add additional hooks
|
||||
|
||||
You can add additional hooks by augmenting the types provided by Nuxt. This can be useful for modules.
|
||||
|
||||
```ts
|
||||
import { HookResult } from "@nuxt/schema";
|
||||
|
||||
declare module '#app' {
|
||||
interface RuntimeNuxtHooks {
|
||||
'your-nuxt-runtime-hook': () => HookResult
|
||||
}
|
||||
interface NuxtHooks {
|
||||
'your-nuxt-hook': () => HookResult
|
||||
}
|
||||
}
|
||||
|
||||
declare module 'nitropack' {
|
||||
interface NitroRuntimeHooks {
|
||||
'your-nitro-hook': () => void;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -127,7 +127,19 @@ export default function (inlineOptions, nuxt) {
|
||||
}
|
||||
```
|
||||
|
||||
Outside of short inline modules defined in `nuxt.config.ts`, **we do not recommend** using this low-level function definition. Instead, to define a module, **we recommend** using the higher-level `defineNuxtModule` helper provided by [Nuxt Kit](/docs/api/advanced/kit).
|
||||
You can get type-hint support for this function using the higher-level `defineNuxtModule` helper provided by [Nuxt Kit](/docs/api/advanced/kit).
|
||||
|
||||
```js
|
||||
import { defineNuxtModule } from '@nuxt/kit'
|
||||
|
||||
export default defineNuxtModule((options, nuxt) => {
|
||||
nuxt.hook('pages:extend', pages => {
|
||||
console.log(`Discovered ${pages.length} pages`)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
However, **we do not recommend** using this low-level function definition. Instead, to define a module, **we recommend** using the object-syntax with `meta` property to identify your module, especially when publishing to npm.
|
||||
|
||||
This helper makes writing Nuxt Module more straightforward by implementing many common patterns seen in modules, guaranteeing future compatibility, and improving your module author developer experience and the one of your module users.
|
||||
|
||||
@ -251,7 +263,7 @@ When you need to handle more complex configuration alterations, you should consi
|
||||
|
||||
#### Exposing Options to Runtime
|
||||
|
||||
Because modules aren't part of the application runtime, their options aren't too. However, in many cases, you might need access to some of these module options within your runtime code. We recommend exposing the needed config using Nuxt's [`runtimeConfig`](/docs/api/configuration/nuxt-config#runtimeconfig).
|
||||
Because modules aren't part of the application runtime, their options aren't either. However, in many cases, you might need access to some of these module options within your runtime code. We recommend exposing the needed config using Nuxt's [`runtimeConfig`](/docs/api/configuration/nuxt-config#runtimeconfig).
|
||||
|
||||
<!-- TODO: Update after #18466 (or equivalent) -->
|
||||
|
||||
@ -268,7 +280,7 @@ export default defineNuxtModule({
|
||||
})
|
||||
```
|
||||
|
||||
Note that we use [`defu`](https://github.com/unjs/defu) to extend the public runtime configuration the user can provides instead of overwritting it.
|
||||
Note that we use [`defu`](https://github.com/unjs/defu) to extend the public runtime configuration the user can provides instead of overwriting it.
|
||||
|
||||
You can then access your module options in a plugin, component, the application like any other runtime configuration:
|
||||
|
||||
@ -363,6 +375,40 @@ export default defineNuxtModule({
|
||||
})
|
||||
```
|
||||
|
||||
#### Injecting Server Routes With `addServerHandler`
|
||||
|
||||
```ts
|
||||
import { defineNuxtModule, addServerHandler, createResolver } from '@nuxt/kit'
|
||||
|
||||
export default defineNuxtModule({
|
||||
setup(options, nuxt) {
|
||||
const resolver = createResolver(import.meta.url)
|
||||
|
||||
addServerHandler({
|
||||
route: '/api/hello',
|
||||
handler: resolver.resolve('./runtime/server/api/hello/index.get.ts')
|
||||
})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
You can also add a dynamic server route:
|
||||
|
||||
```ts
|
||||
import { defineNuxtModule, addServerHandler, createResolver } from '@nuxt/kit'
|
||||
|
||||
export default defineNuxtModule({
|
||||
setup(options, nuxt) {
|
||||
const resolver = createResolver(import.meta.url)
|
||||
|
||||
addServerHandler({
|
||||
route: '/api/hello/:name',
|
||||
handler: resolver.resolve('./runtime/server/api/hello/[name].get.ts')
|
||||
})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### Injecting Other Assets
|
||||
|
||||
If your module should provide other kinds of assets, they can also be injected. Here's a simple example module injecting a stylesheet through Nuxt's `css` array.
|
||||
@ -399,6 +445,37 @@ export default defineNuxtModule({
|
||||
})
|
||||
```
|
||||
|
||||
#### Using Other Modules in Your Module
|
||||
|
||||
If your module depends on other modules, you can add them by using Nuxt Kit's `installModule` utility. For example, if you wanted to use Nuxt Tailwind in your module, you could add it as below:
|
||||
|
||||
```ts
|
||||
import { defineNuxtModule, createResolver, installModule } from '@nuxt/kit'
|
||||
|
||||
export default defineNuxtModule<ModuleOptions>({
|
||||
async setup (options, nuxt) {
|
||||
const { resolve } = createResolver(import.meta.url)
|
||||
|
||||
// We can inject our CSS file which includes Tailwind's directives
|
||||
nuxt.options.css.push(resolve('./runtime/assets/styles.css'))
|
||||
|
||||
await installModule('@nuxtjs/tailwindcss', {
|
||||
// module configuration
|
||||
exposeConfig: true,
|
||||
config: {
|
||||
darkMode: 'class',
|
||||
content: {
|
||||
files: [
|
||||
resolve('./runtime/components/**/*.{vue,mjs,ts}'),
|
||||
resolve('./runtime/*.{mjs,js,ts}')
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### Using Hooks
|
||||
|
||||
[Lifecycle hooks](/docs/guide/going-further/hooks) allow you to expand almost every aspect of Nuxt. Modules can hook to them programmatically or through the `hooks` map in their definition.
|
||||
@ -414,8 +491,8 @@ export default defineNuxtModule({
|
||||
}
|
||||
},
|
||||
setup (options, nuxt) {
|
||||
// Programmatically hook to the `page:extend` hook
|
||||
nuxt.hook('page:extend', (pages) => {
|
||||
// Programmatically hook to the `pages:extend` hook
|
||||
nuxt.hook('pages:extend', (pages) => {
|
||||
console.info(`Discovered ${pages.length} pages`);
|
||||
})
|
||||
}
|
||||
@ -443,22 +520,40 @@ export default defineNuxtModule({
|
||||
|
||||
::
|
||||
|
||||
#### Augmenting Types
|
||||
#### Adding Templates/Virtual Files
|
||||
|
||||
If your module should augment types handled by Nuxt, you can use the `prepare:types` hook to perform this operation.
|
||||
If you need to add a virtual file that can be imported into the user's app, you can use the `addTemplate` utility.
|
||||
|
||||
```js
|
||||
import { defineNuxtModule, addTemplate, createResolver } from '@nuxt/kit'
|
||||
```ts
|
||||
import { defineNuxtModule, addTemplate } from '@nuxt/kit'
|
||||
|
||||
export default defineNuxtModule({
|
||||
setup (options, nuxt) {
|
||||
const { resolve } = createResolver(import.meta.url)
|
||||
|
||||
// Generating types to be injected
|
||||
// The file is added to Nuxt's internal virtual file system and can be imported from '#build/my-module-feature.mjs'
|
||||
addTemplate({
|
||||
filename: 'my-module.d.ts',
|
||||
getContents: () => {
|
||||
return `// Generated by my-module
|
||||
filename: 'my-module-feature.mjs',
|
||||
getContents: () => 'export const myModuleFeature = () => "hello world !"'
|
||||
})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### Adding Type Declarations
|
||||
|
||||
You might also want to add a type declaration to the user's project (for example, to augment a Nuxt interface
|
||||
or provide a global type of your own). For this, Nuxt provides the `addTypeTemplate` utility that both
|
||||
writes a template to the disk and adds a reference to it in the generated `nuxt.d.ts` file.
|
||||
|
||||
If your module should augment types handled by Nuxt, you can use `addTypeTemplate` to perform this operation:
|
||||
|
||||
```js
|
||||
import { defineNuxtModule, addTemplate, addTypeTemplate } from '@nuxt/kit'
|
||||
|
||||
export default defineNuxtModule({
|
||||
setup (options, nuxt) {
|
||||
addTypeTemplate({
|
||||
filename: 'types/my-module.d.ts',
|
||||
getContents: () => `// Generated by my-module
|
||||
interface MyModuleNitroRules {
|
||||
myModule?: { foo: 'bar' }
|
||||
}
|
||||
@ -467,13 +562,29 @@ export default defineNuxtModule({
|
||||
interface NitroRouteConfig extends MyModuleNitroRules {}
|
||||
}
|
||||
export {}`
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
// Injecting previously generated types
|
||||
nuxt.hooks.hook('prepare:types', ({ references }) => {
|
||||
references.push({ path: resolve(nuxt.options.buildDir, 'my-module.d.ts') })
|
||||
If you need more granular control, you can use the `prepare:types` hook to register a callback that will inject your types.
|
||||
|
||||
```ts
|
||||
const template = addTemplate({ /* template options */ })
|
||||
nuxt.hook('prepare:types', ({ references }) => {
|
||||
references.push({ path: template.dst })
|
||||
})
|
||||
```
|
||||
|
||||
##### Updating Templates
|
||||
|
||||
If you need to update your templates/virtual files, you can leverage the `updateTemplates` utility like this :
|
||||
|
||||
```ts
|
||||
nuxt.hook('builder:watch', async (event, path) => {
|
||||
if (path.includes('my-module-feature.config')) {
|
||||
// This will reload the template that you registered
|
||||
updateTemplates({ filter: t => t.filename === 'my-module-feature.mjs' })
|
||||
}
|
||||
})
|
||||
```
|
||||
@ -548,7 +659,7 @@ An example of such a workflow is available on [the module starter](https://githu
|
||||
|
||||
Having a playground Nuxt application to test your module when developing it is really useful. [The module starter integrates one for that purpose](#how-to-develop).
|
||||
|
||||
You can test your module with other Nuxt applications (applications that are not part of your module repository) locally. To do so, you can use [`npm pack`](https://docs.npmjs.com/cli/v7/commands/npm-pack) command, or your package manager equivalent, to create a tarball from your module. Then in your test project, you can add your module to `package.json` packages as: `"my-module": "file:/path/to/tarball.tgz"`.
|
||||
You can test your module with other Nuxt applications (applications that are not part of your module repository) locally. To do so, you can use [`npm pack`](https://docs.npmjs.com/cli/commands/npm-pack) command, or your package manager equivalent, to create a tarball from your module. Then in your test project, you can add your module to `package.json` packages as: `"my-module": "file:/path/to/tarball.tgz"`.
|
||||
|
||||
After that, you should be able to reference `my-module` like in any regular project.
|
||||
|
||||
@ -598,7 +709,7 @@ It's a good practice to make a minimal reproduction with your module and [StackB
|
||||
|
||||
This not only provides potential users of your module a quick and easy way to experiment with the module but also an easy way for them to build minimal reproductions they can send you when they encounter issues.
|
||||
|
||||
#### Do Not Advertize With a Specific Nuxt Version
|
||||
#### Do Not Advertise With a Specific Nuxt Version
|
||||
|
||||
Nuxt 3, Nuxt Kit, and other new toolings are made to have both forward and backward compatibility in mind.
|
||||
|
||||
|
@ -6,7 +6,7 @@ description: Nuxt provides a powerful system that allows you to extend the defau
|
||||
|
||||
Nuxt layers are a powerful feature that you can use to share and reuse partial Nuxt applications within a monorepo, or from a git repository or npm package. The layers structure is almost identical to a standard Nuxt application, which makes them easy to author and maintain. ([Read More](/docs/getting-started/layers))
|
||||
|
||||
A minimal Nuxt layer directory should contain a `nuxt.config.ts` file to indicate it is a layer.
|
||||
A minimal Nuxt layer directory should contain a [`nuxt.config.ts`](/docs/guide/directory-structure/nuxt.config) file to indicate it is a layer.
|
||||
|
||||
```ts{}[base/nuxt.config.ts]
|
||||
export default defineNuxtConfig({})
|
||||
@ -18,7 +18,7 @@ Additionally, certain other files in the layer directory will be auto-scanned an
|
||||
- `composables/*` - Extend the default composables
|
||||
- `pages/*` - Extend the default pages
|
||||
- `server/*` - Extend the default server endpoints & middleware
|
||||
- `nuxt.config.ts` - Extend the default nuxt config
|
||||
- [`nuxt.config.ts`](/docs/guide/directory-structure/nuxt.config)- Extend the default nuxt config
|
||||
- `app.config.ts` - Extend the default app config
|
||||
|
||||
## Basic Example
|
||||
@ -61,6 +61,10 @@ Additionally, certain other files in the layer directory will be auto-scanned an
|
||||
|
||||
::
|
||||
|
||||
::alert{type="info"}
|
||||
If you're interested in deepening your understanding about layers, consider examining [a fully fleshed out [`nuxt.config.ts`](/docs/guide/directory-structure/nuxt.config) file on the Docus platform](https://github.com/nuxt-themes/docus/blob/main/nuxt.config.ts).
|
||||
::
|
||||
|
||||
## Starter Template
|
||||
|
||||
To get started you can initialize a layer with the [nuxt/starter/layer template](https://github.com/nuxt/starter/tree/layer). This will create a basic structure you can build upon. Execute this command within the terminal to get started:
|
||||
|
@ -71,7 +71,7 @@ export default defineNuxtConfig({
|
||||
|
||||
If you plan to add a whole set of pages related with a specific functionality, you might want to use a [Nuxt module](/modules).
|
||||
|
||||
The [Nuxt kit](docs/guide/going-further/kit) provides a few ways [to add routes](/docs/api/advanced/kit#pages):
|
||||
The [Nuxt kit](/docs/guide/going-further/kit) provides a few ways [to add routes](/docs/api/advanced/kit#pages):
|
||||
- extendPages (callback: pages => void)
|
||||
- extendRouteRules (route: string, rule: NitroRouteConfig, options: ExtendRouteRulesOptions)
|
||||
|
||||
|
98
docs/2.guide/3.going-further/9.debugging.md
Normal file
98
docs/2.guide/3.going-further/9.debugging.md
Normal file
@ -0,0 +1,98 @@
|
||||
---
|
||||
title: "Debugging"
|
||||
description: "In Nuxt 3, you can get started with debugging your application directly in the browser as well as in your IDE."
|
||||
---
|
||||
|
||||
## Sourcemaps
|
||||
|
||||
Sourcemaps are enabled for your server build by default, and for the client build in dev mode, but you can enable them more specifically in your configuration.
|
||||
|
||||
```ts
|
||||
export default defineNuxtConfig({
|
||||
// or sourcemap: true
|
||||
sourcemap: {
|
||||
server: true,
|
||||
client: true
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Debugging in Your IDE
|
||||
|
||||
It is possible to debug your Nuxt app in your IDE while you are developing it.
|
||||
|
||||
### Example VS Code Debug Configuration
|
||||
|
||||
You may need to update the config below with a path to your web browser. For more information, visit the [VS Code documentation about debug configuration](https://go.microsoft.com/fwlink/?linkid=830387).
|
||||
|
||||
```json5
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "client: chrome",
|
||||
"url": "http://localhost:3000",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "server: nuxt",
|
||||
"outputCapture": "std",
|
||||
"program": "${workspaceFolder}/node_modules/nuxi/bin/nuxi.mjs",
|
||||
"args": [
|
||||
"dev"
|
||||
],
|
||||
}
|
||||
],
|
||||
"compounds": [
|
||||
{
|
||||
"name": "fullstack: nuxt",
|
||||
"configurations": [
|
||||
"server: nuxt",
|
||||
"client: chrome"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
If you prefer your usual browser extensions, add this to the _chrome_ configuration above:
|
||||
|
||||
```json5
|
||||
"userDataDir": false,
|
||||
```
|
||||
|
||||
### Example JetBrains IDEs Debug Configuration
|
||||
|
||||
You can also debug your Nuxt app in JetBrains IDEs such as IntelliJ IDEA, WebStorm, or PhpStorm.
|
||||
|
||||
1. Create a new file in your project root directory and name it `nuxt.run.xml`.
|
||||
|
||||
2. Open the `nuxt.run.xml` file and paste the following debug configuration:
|
||||
|
||||
```html
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="client: chrome" type="JavascriptDebugType" uri="http://localhost:3000" useFirstLineBreakpoints="true">
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
|
||||
<configuration default="false" name="server: nuxt" type="NodeJSConfigurationType" application-parameters="dev" path-to-js-file="$PROJECT_DIR$/node_modules/nuxi/bin/nuxi.mjs" working-dir="$PROJECT_DIR$">
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
|
||||
<configuration default="false" name="fullstack: nuxt" type="CompoundRunConfigurationType">
|
||||
<toRun name="client: chrome" type="JavascriptDebugType" />
|
||||
<toRun name="server: nuxt" type="NodeJSConfigurationType" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
```
|
||||
|
||||
### Other IDEs
|
||||
|
||||
If you have another IDE and would like to contribute sample configuration, feel free to [open a PR](https://github.com/nuxt/nuxt/edit/main/docs/2.guide/3.going-further/9.debugging.md)!
|
@ -1,74 +1,80 @@
|
||||
---
|
||||
description: useAsyncData provides access to data that resolves asynchronously.
|
||||
---
|
||||
# `useAsyncData`
|
||||
# useAsyncData
|
||||
|
||||
Within your pages, components, and plugins you can use useAsyncData to get access to data that resolves asynchronously.
|
||||
|
||||
::alert{type=warning}
|
||||
[`useAsyncData`](/docs/api/composables/use-async-data) is a composable meant to be called directly in a setup function, plugin, or route middleware. It returns reactive composables and handles adding responses to the Nuxt payload so they can be passed from server to client without re-fetching the data on client side when the page hydrates.
|
||||
::
|
||||
|
||||
## Type
|
||||
|
||||
```ts [Signature]
|
||||
function useAsyncData(
|
||||
function useAsyncData<DataT, DataE>(
|
||||
handler: (nuxtApp?: NuxtApp) => Promise<DataT>,
|
||||
options?: AsyncDataOptions<DataT>
|
||||
): AsyncData<DataT>
|
||||
function useAsyncData(
|
||||
): AsyncData<DataT, DataE>
|
||||
function useAsyncData<DataT, DataE>(
|
||||
key: string,
|
||||
handler: (nuxtApp?: NuxtApp) => Promise<DataT>,
|
||||
options?: AsyncDataOptions<DataT>
|
||||
): Promise<AsyncData<DataT>>
|
||||
): Promise<AsyncData<DataT, DataE>
|
||||
|
||||
type AsyncDataOptions<DataT> = {
|
||||
server?: boolean
|
||||
lazy?: boolean
|
||||
immediate?: boolean
|
||||
default?: () => DataT | Ref<DataT> | null
|
||||
transform?: (input: DataT) => DataT
|
||||
pick?: string[]
|
||||
watch?: WatchSource[]
|
||||
immediate?: boolean
|
||||
}
|
||||
|
||||
interface RefreshOptions {
|
||||
dedupe?: boolean
|
||||
}
|
||||
|
||||
type AsyncData<DataT, ErrorT> = {
|
||||
data: Ref<DataT | null>
|
||||
pending: Ref<boolean>
|
||||
execute: () => Promise<void>
|
||||
refresh: (opts?: RefreshOptions) => Promise<void>
|
||||
refresh: (opts?: AsyncDataExecuteOptions) => Promise<void>
|
||||
execute: (opts?: AsyncDataExecuteOptions) => Promise<void>
|
||||
error: Ref<ErrorT | null>
|
||||
status: Ref<AsyncDataRequestStatus>
|
||||
};
|
||||
|
||||
interface AsyncDataExecuteOptions {
|
||||
dedupe?: boolean
|
||||
}
|
||||
|
||||
|
||||
type AsyncDataRequestStatus = 'idle' | 'pending' | 'success' | 'error'
|
||||
```
|
||||
|
||||
## Params
|
||||
|
||||
* **key**: a unique key to ensure that data fetching can be properly de-duplicated across requests. If you do not provide a key, then a key that is unique to the file name and line number of the instance of `useAsyncData` will be generated for you.
|
||||
* **handler**: an asynchronous function that returns a value
|
||||
* **key**: a unique key to ensure that data fetching can be properly de-duplicated across requests. If you do not provide a key, then a key that is unique to the file name and line number of the instance of [`useAsyncData`](/docs/api/composables/use-async-data) will be generated for you.
|
||||
* **handler**: an asynchronous function that must return a truthy value (for example, it should not be `undefined` or `null`) or the request may be duplicated on the client side
|
||||
* **options**:
|
||||
* _lazy_: whether to resolve the async function after loading the route, instead of blocking client-side navigation (defaults to `false`)
|
||||
* _default_: a factory function to set the default value of the data, before the async function resolves - particularly useful with the `lazy: true` option
|
||||
* _server_: whether to fetch the data on the server (defaults to `true`)
|
||||
* _lazy_: whether to resolve the async function after loading the route, instead of blocking client-side navigation (defaults to `false`)
|
||||
* _immediate_: when set to `false`, will prevent the request from firing immediately. (defaults to `true`)
|
||||
* _default_: a factory function to set the default value of the `data`, before the async function resolves - useful with the `lazy: true` or `immediate: false` option
|
||||
* _transform_: a function that can be used to alter `handler` function result after resolving
|
||||
* _pick_: only pick specified keys in this array from the `handler` function result
|
||||
* _watch_: watch reactive sources to auto-refresh
|
||||
* _immediate_: When set to `false`, will prevent the request from firing immediately. (defaults to `true`)
|
||||
|
||||
Under the hood, `lazy: false` uses `<Suspense>` to block the loading of the route before the data has been fetched. Consider using `lazy: true` and implementing a loading state instead for a snappier user experience.
|
||||
|
||||
## Return Values
|
||||
|
||||
* **data**: the result of the asynchronous function that is passed in
|
||||
* **pending**: a boolean indicating whether the data is still being fetched
|
||||
* **refresh**/**execute**: a function that can be used to refresh the data returned by the `handler` function
|
||||
* **error**: an error object if the data fetching failed
|
||||
* **data**: the result of the asynchronous function that is passed in.
|
||||
* **pending**: a boolean indicating whether the data is still being fetched.
|
||||
* **refresh**/**execute**: a function that can be used to refresh the data returned by the `handler` function.
|
||||
* **error**: an error object if the data fetching failed.
|
||||
* **status**: a string indicating the status of the data request (`"idle"`, `"pending"`, `"success"`, `"error"`).
|
||||
|
||||
By default, Nuxt waits until a `refresh` is finished before it can be executed again.
|
||||
|
||||
::alert{type=warning}
|
||||
If you have not fetched data on the server (for example, with `server: false`), then the data _will not_ be fetched until hydration completes. This means even if you await `useAsyncData` on the client side, `data` will remain `null` within `<script setup>`.
|
||||
If you have not fetched data on the server (for example, with `server: false`), then the data _will not_ be fetched until hydration completes. This means even if you await [`useAsyncData`](/docs/api/composables/use-async-data) on the client side, `data` will remain `null` within `<script setup>`.
|
||||
::
|
||||
|
||||
## Example
|
||||
@ -99,7 +105,7 @@ const { data: posts } = await useAsyncData(
|
||||
```
|
||||
|
||||
::alert{type=warning}
|
||||
`useAsyncData` is a reserved function name transformed by the compiler, so you should not name your own function `useAsyncData`.
|
||||
[`useAsyncData`](/docs/api/composables/use-async-data) is a reserved function name transformed by the compiler, so you should not name your own function [`useAsyncData`](/docs/api/composables/use-async-data) .
|
||||
::
|
||||
|
||||
::ReadMore{link="/docs/getting-started/data-fetching"}
|
||||
|
@ -23,6 +23,11 @@ const cookie = useCookie(name, options)
|
||||
The example below creates a cookie called `counter`. If the cookie doesn't exist, it is initially set to a random value. Whenever we update the `counter` variable, the cookie will be updated accordingly.
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
const counter = useCookie('counter')
|
||||
counter.value = counter.value || Math.round(Math.random() * 1000)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h1>Counter: {{ counter || '-' }}</h1>
|
||||
@ -31,14 +36,9 @@ The example below creates a cookie called `counter`. If the cookie doesn't exist
|
||||
<button @click="counter++">+</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const counter = useCookie('counter')
|
||||
counter.value = counter.value || Math.round(Math.random() * 1000)
|
||||
</script>
|
||||
```
|
||||
|
||||
:button-link[Open on StackBlitz]{href="https://stackblitz.com/github/nuxt/nuxt/tree/main/examples/composables/use-cookie?terminal=dev&file=app.vue" blank}
|
||||
:button-link[Open on StackBlitz]{href="https://stackblitz.com/github/nuxt/examples/tree/main/advanced/use-cookie?terminal=dev&file=app.vue" blank}
|
||||
|
||||
## Options
|
||||
|
||||
@ -143,11 +143,7 @@ Specifies the `boolean` or `string` value for [watch](https://vuejs.org/api/reac
|
||||
**Example 1:**
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>User score: {{ user?.score }}</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
const user = useCookie(
|
||||
'userInfo',
|
||||
{
|
||||
@ -160,21 +156,16 @@ if (user.value && user.value !== null) {
|
||||
user.value.score++; // userInfo cookie not update with this change
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>User score: {{ user?.score }}</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
**Example 2:**
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<h1>List</h1>
|
||||
<pre>{{ list }}</pre>
|
||||
<button @click="add">Add</button>
|
||||
<button @click="save">Save</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
const list = useCookie(
|
||||
'list',
|
||||
{
|
||||
@ -195,6 +186,15 @@ function save() {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h1>List</h1>
|
||||
<pre>{{ list }}</pre>
|
||||
<button @click="add">Add</button>
|
||||
<button @click="save">Save</button>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Handling Cookies in API Routes
|
||||
@ -216,5 +216,5 @@ export default defineEventHandler(event => {
|
||||
})
|
||||
```
|
||||
|
||||
::LinkExample{link="/docs/examples/composables/use-cookie"}
|
||||
::LinkExample{link="/docs/examples/advanced/use-cookie"}
|
||||
::
|
||||
|
@ -1,18 +1,21 @@
|
||||
# `useFetch`
|
||||
|
||||
This composable provides a convenient wrapper around [`useAsyncData`](/docs/api/composables/use-async-data) and [`$fetch`](/docs/api/utils/dollarfetch).
|
||||
|
||||
It automatically generates a key based on URL and fetch options, provides type hints for request url based on server routes, and infers API response type.
|
||||
|
||||
::alert{type=warning}
|
||||
[`useFetch`](/docs/api/composables/use-fetch) is a composable meant to be called directly in a setup function, plugin, or route middleware. It returns reactive composables and handles adding responses to the Nuxt payload so they can be passed from server to client without re-fetching the data on client side when the page hydrates.
|
||||
::
|
||||
|
||||
## Type
|
||||
|
||||
```ts [Signature]
|
||||
function useFetch(
|
||||
function useFetch<DataT, ErrorT>(
|
||||
url: string | Request | Ref<string | Request> | () => string | Request,
|
||||
options?: UseFetchOptions<DataT>
|
||||
): Promise<AsyncData<DataT>>
|
||||
): Promise<AsyncData<DataT, ErrorT>>
|
||||
|
||||
type UseFetchOptions = {
|
||||
type UseFetchOptions<DataT> = {
|
||||
key?: string
|
||||
method?: string
|
||||
query?: SearchParams
|
||||
@ -26,16 +29,23 @@ type UseFetchOptions = {
|
||||
default?: () => DataT
|
||||
transform?: (input: DataT) => DataT
|
||||
pick?: string[]
|
||||
watch?: WatchSource[]
|
||||
watch?: WatchSource[] | false
|
||||
}
|
||||
|
||||
type AsyncData<DataT> = {
|
||||
data: Ref<DataT>
|
||||
type AsyncData<DataT, ErrorT> = {
|
||||
data: Ref<DataT | null>
|
||||
pending: Ref<boolean>
|
||||
refresh: (opts?: { dedupe?: boolean }) => Promise<void>
|
||||
execute: () => Promise<void>
|
||||
error: Ref<Error | boolean>
|
||||
refresh: (opts?: AsyncDataExecuteOptions) => Promise<void>
|
||||
execute: (opts?: AsyncDataExecuteOptions) => Promise<void>
|
||||
error: Ref<ErrorT | null>
|
||||
status: Ref<AsyncDataRequestStatus>
|
||||
}
|
||||
|
||||
interface AsyncDataExecuteOptions {
|
||||
dedupe?: boolean
|
||||
}
|
||||
|
||||
type AsyncDataRequestStatus = 'idle' | 'pending' | 'success' | 'error'
|
||||
```
|
||||
|
||||
## Params
|
||||
@ -55,15 +65,16 @@ All fetch options can be given a `computed` or `ref` value. These will be watche
|
||||
|
||||
* **Options (from `useAsyncData`)**:
|
||||
* `key`: a unique key to ensure that data fetching can be properly de-duplicated across requests, if not provided, it will be generated based on the static code location where `useAsyncData` is used.
|
||||
* `server`: Whether to fetch the data on the server (defaults to `true`).
|
||||
* `default`: A factory function to set the default value of the data, before the async function resolves - particularly useful with the `lazy: true` option.
|
||||
* `pick`: Only pick specified keys in this array from the `handler` function result.
|
||||
* `watch`: Watch an array of reactive sources and auto-refresh the fetch result when they change. Fetch options and URL are watched by default. You can completely ignore reactive sources by using `watch: false`. Together with `immediate: false`, this allows for a fully-manual `useFetch`.
|
||||
* `transform`: A function that can be used to alter `handler` function result after resolving.
|
||||
* `immediate`: When set to `false`, will prevent the request from firing immediately. (defaults to `true`)
|
||||
* `server`: whether to fetch the data on the server (defaults to `true`)
|
||||
* `lazy`: whether to resolve the async function after loading the route, instead of blocking client-side navigation (defaults to `false`)
|
||||
* `immediate`: when set to `false`, will prevent the request from firing immediately. (defaults to `true`)
|
||||
* `default`: a factory function to set the default value of the `data`, before the async function resolves - useful with the `lazy: true` or `immediate: false` option
|
||||
* `transform`: a function that can be used to alter `handler` function result after resolving
|
||||
* `pick`: only pick specified keys in this array from the `handler` function result
|
||||
* `watch`: watch an array of reactive sources and auto-refresh the fetch result when they change. Fetch options and URL are watched by default. You can completely ignore reactive sources by using `watch: false`. Together with `immediate: false`, this allows for a fully-manual `useFetch`.
|
||||
|
||||
::alert{type=warning}
|
||||
If you provide a function or ref as the `url` parameter, or if you provide functions as arguments to the `options` parameter, then the `useFetch` call will not match other `useFetch` calls elsewhere in your codebase, even if the options seem to be identical. If you wish to force a match, you may provide your own key in `options`.
|
||||
If you provide a function or ref as the `url` parameter, or if you provide functions as arguments to the `options` parameter, then the [`useFetch`](/docs/api/composables/use-fetch) call will not match other [`useFetch`](/docs/api/composables/use-fetch) calls elsewhere in your codebase, even if the options seem to be identical. If you wish to force a match, you may provide your own key in `options`.
|
||||
::
|
||||
|
||||
## Return Values
|
||||
@ -72,17 +83,20 @@ If you provide a function or ref as the `url` parameter, or if you provide funct
|
||||
* **pending**: a boolean indicating whether the data is still being fetched.
|
||||
* **refresh**/**execute**: a function that can be used to refresh the data returned by the `handler` function.
|
||||
* **error**: an error object if the data fetching failed.
|
||||
* **status**: a string indicating the status of the data request (`"idle"`, `"pending"`, `"success"`, `"error"`).
|
||||
|
||||
By default, Nuxt waits until a `refresh` is finished before it can be executed again.
|
||||
|
||||
::alert{type=warning}
|
||||
If you have not fetched data on the server (for example, with `server: false`), then the data _will not_ be fetched until hydration completes. This means even if you await `useFetch` on client-side, `data` will remain null within `<script setup>`.
|
||||
If you have not fetched data on the server (for example, with `server: false`), then the data _will not_ be fetched until hydration completes. This means even if you await [`useFetch`](/docs/api/composables/use-fetch) on client-side, `data` will remain null within `<script setup>`.
|
||||
::
|
||||
|
||||
## Example
|
||||
|
||||
```ts
|
||||
const { data, pending, error, refresh } = await useFetch('https://api.nuxtjs.dev/mountains',{
|
||||
const route = useRoute()
|
||||
|
||||
const { data, pending, error, refresh } = await useFetch(`https://api.nuxtjs.dev/mountains/${route.params.slug}`, {
|
||||
pick: ['title']
|
||||
})
|
||||
```
|
||||
@ -114,7 +128,7 @@ const { data, pending, error, refresh } = await useFetch('/api/auth/login', {
|
||||
},
|
||||
onResponse({ request, response, options }) {
|
||||
// Process the response data
|
||||
return response._data
|
||||
localStorage.setItem('token', response._data.token)
|
||||
},
|
||||
onResponseError({ request, response, options }) {
|
||||
// Handle the response errors
|
||||
@ -123,10 +137,13 @@ const { data, pending, error, refresh } = await useFetch('/api/auth/login', {
|
||||
```
|
||||
|
||||
::alert{type=warning}
|
||||
`useFetch` is a reserved function name transformed by the compiler, so you should not name your own function `useFetch`.
|
||||
[`useFetch`](/docs/api/composables/use-fetch) is a reserved function name transformed by the compiler, so you should not name your own function `useFetch`.
|
||||
::
|
||||
|
||||
::LinkExample{link="/docs/examples/advanced/use-custom-fetch-composable"}
|
||||
::
|
||||
|
||||
:ReadMore{link="/docs/getting-started/data-fetching"}
|
||||
|
||||
::LinkExample{link="/docs/examples/composables/use-fetch"}
|
||||
::LinkExample{link="/docs/examples/features/data-fetching"}
|
||||
::
|
||||
|
@ -8,7 +8,7 @@ The `useHeadSafe` composable is a wrapper around the [`useHead`](/docs/api/compo
|
||||
|
||||
## Usage
|
||||
|
||||
You can pass all the same values as `useHead`
|
||||
You can pass all the same values as [`useHead`](/docs/api/composables/use-head)
|
||||
|
||||
```ts
|
||||
useHeadSafe({
|
||||
@ -24,7 +24,7 @@ useHeadSafe({
|
||||
// <meta content="0;javascript:alert(1)">
|
||||
```
|
||||
|
||||
Read more on [unhead documentation](https://unhead.harlanzw.com/guide/composables/use-head-safe).
|
||||
Read more on [unhead documentation](https://unhead.unjs.io/usage/composables/use-head-safe).
|
||||
|
||||
## Type
|
||||
|
||||
|
@ -2,9 +2,9 @@
|
||||
description: useHead customizes the head properties of individual pages of your Nuxt app.
|
||||
---
|
||||
|
||||
# `useHead`
|
||||
# useHead
|
||||
|
||||
The `useHead` composable function allows you to manage your head tags in a programmatic and reactive way, powered by [Unhead](https://unhead.harlanzw.com/). If the data comes from a user or other untrusted source, we recommend you check out [`useHeadSafe`](/docs/api/composables/use-head-safe)
|
||||
The [`useHead`](/docs/api/composables/use-head) composable function allows you to manage your head tags in a programmatic and reactive way, powered by [Unhead](https://unhead.unjs.io/). If the data comes from a user or other untrusted source, we recommend you check out [`useHeadSafe`](/docs/api/composables/use-head-safe)
|
||||
|
||||
:ReadMore{link="/docs/getting-started/seo-meta"}
|
||||
|
||||
@ -14,7 +14,7 @@ The `useHead` composable function allows you to manage your head tags in a progr
|
||||
useHead(meta: MaybeComputedRef<MetaObject>): void
|
||||
```
|
||||
|
||||
Below are the non-reactive types for `useHead`.
|
||||
Below are the non-reactive types for [`useHead`](/docs/api/composables/use-head) .
|
||||
|
||||
```ts
|
||||
interface MetaObject {
|
||||
@ -34,7 +34,7 @@ interface MetaObject {
|
||||
See [@unhead/schema](https://github.com/unjs/unhead/blob/main/packages/schema/src/schema.ts) for more detailed types.
|
||||
|
||||
::alert{type=info}
|
||||
The properties of `useHead` can be dynamic, accepting `ref`, `computed` and `reactive` properties. `meta` parameter can also accept a function returning an object to make the entire object reactive.
|
||||
The properties of [`useHead`](/docs/api/composables/use-head) can be dynamic, accepting `ref`, `computed` and `reactive` properties. `meta` parameter can also accept a function returning an object to make the entire object reactive.
|
||||
::
|
||||
|
||||
## Parameters
|
||||
|
@ -4,36 +4,36 @@ description: This wrapper around useAsyncData triggers navigation immediately.
|
||||
|
||||
# `useLazyAsyncData`
|
||||
|
||||
`useLazyAsyncData` provides a wrapper around `useAsyncData` that triggers navigation before the handler is resolved by setting the `lazy` option to `true`.
|
||||
`useLazyAsyncData` provides a wrapper around [`useAsyncData`](/docs/api/composables/use-async-data) that triggers navigation before the handler is resolved by setting the `lazy` option to `true`.
|
||||
|
||||
## Description
|
||||
|
||||
By default, [useAsyncData](/docs/api/composables/use-async-data) blocks navigation until its async handler is resolved.
|
||||
|
||||
> `useLazyAsyncData` has the same signature as `useAsyncData`.
|
||||
> `useLazyAsyncData` has the same signature as [`useAsyncData`](/docs/api/composables/use-async-data) .
|
||||
|
||||
:ReadMore{link="/docs/api/composables/use-async-data"}
|
||||
|
||||
## Example
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
/* Navigation will occur before fetching is complete.
|
||||
Handle pending and error states directly within your component's template
|
||||
*/
|
||||
const { pending, data: count } = await useLazyAsyncData('count', () => $fetch('/api/count'))
|
||||
|
||||
watch(count, (newCount) => {
|
||||
// Because count might start out null, you won't have access
|
||||
// to its contents immediately, but you can watch it.
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
{{ pending ? 'Loading' : count }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/* Navigation will occur before fetching is complete.
|
||||
Handle pending and error states directly within your component's template
|
||||
*/
|
||||
const { pending, data: count } = useLazyAsyncData('count', () => $fetch('/api/count'))
|
||||
|
||||
watch(count, (newCount) => {
|
||||
// Because count starts out null, you won't have access
|
||||
// to its contents immediately, but you can watch it.
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
::alert{type=warning}
|
||||
|
@ -4,19 +4,30 @@ description: This wrapper around useFetch triggers navigation immediately.
|
||||
|
||||
# `useLazyFetch`
|
||||
|
||||
`useLazyFetch` provides a wrapper around `useFetch` that triggers navigation before the handler is resolved by setting the `lazy` option to `true`.
|
||||
`useLazyFetch` provides a wrapper around [`useFetch`](/docs/api/composables/use-fetch) that triggers navigation before the handler is resolved by setting the `lazy` option to `true`.
|
||||
|
||||
## Description
|
||||
|
||||
By default, [useFetch](/docs/api/composables/use-fetch) blocks navigation until its async handler is resolved.
|
||||
|
||||
> `useLazyFetch` has the same signature as `useFetch`.
|
||||
> [`useLazyFetch`](/docs/api/composables/use-lazy-fetch) has the same signature as `useFetch`.
|
||||
|
||||
:ReadMore{link="/docs/api/composables/use-fetch"}
|
||||
|
||||
## Example
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
/* Navigation will occur before fetching is complete.
|
||||
Handle pending and error states directly within your component's template
|
||||
*/
|
||||
const { pending, data: posts } = await useLazyFetch('/api/posts')
|
||||
watch(posts, (newPosts) => {
|
||||
// Because posts might start out null, you won't have access
|
||||
// to its contents immediately, but you can watch it.
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="pending">
|
||||
Loading ...
|
||||
@ -27,17 +38,6 @@ By default, [useFetch](/docs/api/composables/use-fetch) blocks navigation until
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/* Navigation will occur before fetching is complete.
|
||||
Handle pending and error states directly within your component's template
|
||||
*/
|
||||
const { pending, data: posts } = useLazyFetch('/api/posts')
|
||||
watch(posts, (newPosts) => {
|
||||
// Because posts starts out null, you won't have access
|
||||
// to its contents immediately, but you can watch it.
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
::alert{type=warning}
|
||||
|
@ -5,7 +5,7 @@
|
||||
You can use `useNuxtApp()` within composables, plugins and components.
|
||||
|
||||
```vue [app.vue]
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
const nuxtApp = useNuxtApp()
|
||||
</script>
|
||||
```
|
||||
@ -14,7 +14,7 @@ You can use `useNuxtApp()` within composables, plugins and components.
|
||||
|
||||
### `provide (name, value)`
|
||||
|
||||
`nuxtApp` is a runtime context that you can extend using [Nuxt plugins](/docs/guide/directory-structure/plugins). Use the `provide` function to create Nuxt plugins to make values and helper methods available in your Nuxt application across all composables and components.
|
||||
`nuxtApp` is a runtime context that you can extend using [Nuxt plugins](/docs/guide/directory-structure/plugins). Use the `provide` function to create Nuxt plugins to make values and helper methods available in your Nuxt application across all composables and components.
|
||||
|
||||
`provide` function accepts `name` and `value` parameters.
|
||||
|
||||
@ -52,7 +52,7 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||
})
|
||||
```
|
||||
|
||||
### `callhook(name, ...args)`
|
||||
### `callHook(name, ...args)`
|
||||
|
||||
`callHook` returns a promise when called with any of the existing hooks.
|
||||
|
||||
@ -70,7 +70,7 @@ await nuxtApp.callHook('my-plugin:init')
|
||||
|
||||
- [**component()**](https://vuejs.org/api/application.html#app-component) - Registers a global component if passing both a name string and a component definition, or retrieves an already registered one if only the name is passed.
|
||||
- [**directive()**](https://vuejs.org/api/application.html#app-directive) - Registers a global custom directive if passing both a name string and a directive definition, or retrieves an already registered one if only the name is passed[(example)](/docs/guide/directory-structure/plugins#vue-directives).
|
||||
- [**use()**](https://vuejs.org/api/application.html#app-use) - Installs a **[Vue.js Plugin](https://vuejs.org/guide/reusability/plugins.html)** [(example)](/docs/guide/directory-structure/plugins#vue-plugins).
|
||||
- [**use()**](https://vuejs.org/api/application.html#app-use) - Installs a **[Vue.js Plugin](https://vuejs.org/guide/reusability/plugins.html)** [(example)](/docs/guide/directory-structure/plugins#vue-plugins).
|
||||
|
||||
:ReadMore{link="https://vuejs.org/api/application.html#application-api"}
|
||||
|
||||
@ -87,21 +87,19 @@ await nuxtApp.callHook('my-plugin:init')
|
||||
`payload` exposes data and state variables from server side to client side. The following keys will be available on the client after they have been passed from the server side:
|
||||
|
||||
- **serverRendered** (boolean) - Indicates if response is server-side-rendered.
|
||||
- **data** (object) - When you fetch the data from an API endpoint using either `useFetch` or `useAsyncData`, resulting payload can be accessed from the `payload.data`. This data is cached and helps you prevent fetching the same data in case an identical request is made more than once.
|
||||
- **data** (object) - When you fetch the data from an API endpoint using either [`useFetch`](/docs/api/composables/use-fetch) or [`useAsyncData`](/docs/api/composables/use-async-data) , resulting payload can be accessed from the `payload.data`. This data is cached and helps you prevent fetching the same data in case an identical request is made more than once.
|
||||
|
||||
```vue [app.vue]
|
||||
export default defineComponent({
|
||||
async setup() {
|
||||
<script setup lang="ts">
|
||||
const { data } = await useAsyncData('count', () => $fetch('/api/count'))
|
||||
}
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
After fetching the value of `count` using `useAsyncData` in the example above, if you access `payload.data`, you will see `{ count: 1 }` recorded there. The value of `count` is updated whenever the page count increases.
|
||||
After fetching the value of `count` using [`useAsyncData`](/docs/api/composables/use-async-data) in the example above, if you access `payload.data`, you will see `{ count: 1 }` recorded there. The value of `count` is updated whenever the page count increases.
|
||||
|
||||
When accessing the same `payload.data` from [ssrcontext](#ssrcontext), you can access the same value on the server side as well.
|
||||
|
||||
- **state** (object) - When you use `useState` composable in Nuxt to set shared state, this state data is accessed through `payload.state.[name-of-your-state]`.
|
||||
- **state** (object) - When you use [`useState`](/docs/api/composables/use-state) composable in Nuxt to set shared state, this state data is accessed through `payload.state.[name-of-your-state]`.
|
||||
|
||||
```js [plugins/my-plugin.ts]
|
||||
export const useColor = () => useState<string>('color', () => 'pink')
|
||||
@ -113,23 +111,20 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||
})
|
||||
```
|
||||
|
||||
::alert
|
||||
Normally `payload` must contain only plain JavaScript objects. But by setting `experimental.renderJsonPayloads`, it is possible to use more advanced types, such as `ref`, `reactive`, `shallowRef`, `shallowReactive` and `NuxtError`.
|
||||
It is also possible to use more advanced types, such as `ref`, `reactive`, `shallowRef`, `shallowReactive` and `NuxtError`.
|
||||
|
||||
You can also add your own types. In future you will be able to add your own types easily with [object-syntax plugins](https://github.com/nuxt/nuxt/issues/14628). For now, you must add your plugin which calls both `definePayloadReducer` and `definePayloadReviver` via a custom module:
|
||||
You can also add your own types, with a special plugin helper:
|
||||
|
||||
```ts
|
||||
export default defineNuxtConfig({
|
||||
modules: [
|
||||
function (_options, nuxt) {
|
||||
// TODO: support directly via object syntax plugins: https://github.com/nuxt/nuxt/issues/14628
|
||||
nuxt.hook('modules:done', () => {
|
||||
nuxt.options.plugins.unshift('~/plugins/custom-type-plugin')
|
||||
```ts [plugins/custom-payload.ts]
|
||||
/**
|
||||
* This kind of plugin runs very early in the Nuxt lifecycle, before we revive the payload.
|
||||
* You will not have access to the router or other Nuxt-injected properties.
|
||||
*/
|
||||
export default definePayloadPlugin((nuxtApp) => {
|
||||
definePayloadReducer('BlinkingText', data => data === '<blink>' && '_')
|
||||
definePayloadReviver('BlinkingText', () => '<blink>')
|
||||
})
|
||||
},
|
||||
]
|
||||
})
|
||||
::
|
||||
```
|
||||
|
||||
### `isHydrating`
|
||||
|
||||
@ -149,3 +144,112 @@ export default defineComponent({
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### `runWithContext`
|
||||
|
||||
::alert
|
||||
|
||||
<p>
|
||||
|
||||
You are likely here because you got a "Nuxt instance unavailable" message. Please use this method sparingly, and report
|
||||
examples that are causing issues, so that it can ultimately be solved at the framework level.
|
||||
|
||||
</p>
|
||||
|
||||
::
|
||||
|
||||
The `runWithContext` method is meant to be used to call a function and give it an explicit Nuxt context. Typically, the
|
||||
Nuxt context is passed around implicitly and you do not need to worry about this. However, when working with complex
|
||||
`async`/`await` scenarios in middleware/plugins, you can run into instances where the current instance has been unset
|
||||
after an async call.
|
||||
|
||||
```js
|
||||
export default defineNuxtRouteMiddleware(async (to, from) => {
|
||||
const nuxtApp = useNuxtApp()
|
||||
let user
|
||||
try {
|
||||
user = await fetchUser()
|
||||
// the Vue/Nuxt compiler loses context here because of the try/catch block.
|
||||
} catch (e) {
|
||||
user = null
|
||||
}
|
||||
if (!user) {
|
||||
// apply the correct Nuxt context to our `navigateTo` call.
|
||||
return nuxtApp.runWithContext(() => navigateTo('/auth'))
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### Usage
|
||||
|
||||
```js
|
||||
const result = nuxtApp.runWithContext(() => functionWithContext())
|
||||
```
|
||||
|
||||
##### `functionWithContext`
|
||||
|
||||
Any function that requires the context of the current Nuxt application. This context will be correctly applied automatically.
|
||||
|
||||
##### Return value
|
||||
|
||||
`runWithContext` will return whatever is returned by `functionWithContext`.
|
||||
|
||||
#### A Deeper Explanation of Context
|
||||
|
||||
##### Background
|
||||
|
||||
Vue.js Composition API (and Nuxt composables similarly) work by depending on an implicit context. During the lifecycle, Vue sets the temporary instance of the current component (and Nuxt temporary instance of nuxtApp) to a global variable and unsets it in same tick. When rendering on the server side, there are multiple requests from different users and nuxtApp running in a same global context. Because of this, Nuxt and Vue immediately unset this global instance to avoid leaking a shared reference between two users or components.
|
||||
|
||||
What it does mean? The Composition API and Nuxt Composables are only available during lifecycle and in same tick before any async operation:
|
||||
|
||||
```js
|
||||
// --- Vue internal ---
|
||||
const _vueInstance = null
|
||||
const getCurrentInstance = () => _vueInstance
|
||||
// ---
|
||||
|
||||
// Vue / Nuxt sets a global variable referencing to current component in _vueInstance when calling setup()
|
||||
async function setup() {
|
||||
getCurrentInstance() // Works
|
||||
await someAsyncOperation() // Vue unsets the context in same tick before async operation!
|
||||
getCurrentInstance() // null
|
||||
}
|
||||
```
|
||||
|
||||
The classic solution to this, is caching the current instance on first call to a local variable like `const instance = getCurrentInstance()` and use it in the next composable call but the issue is that any nested composable calls now needs to explicitly accept the instance as an argument and not depend on the implicit context of composition-api. This is design limitation with composables and not an issue per-se.
|
||||
|
||||
To overcome this limitation, Vue does some behind the scenes work when compiling our application code and restores context after each call for `<script setup>`:
|
||||
|
||||
```js
|
||||
const __instance = getCurrentInstance() // Generated by Vue compiler
|
||||
getCurrentInstance() // Works!
|
||||
await someAsyncOperation() // Vue unsets the context
|
||||
__restoreInstance(__instance) // Generated by Vue compiler
|
||||
getCurrentInstance() // Still works!
|
||||
```
|
||||
|
||||
For a better description of what Vue actually does, see [unjs/unctx#2 (comment)](https://github.com/unjs/unctx/issues/2#issuecomment-942193723).
|
||||
|
||||
##### Solution
|
||||
|
||||
This is where `runWithContext` can be used to restore context, similarly to how `<script setup>` works.
|
||||
|
||||
Nuxt 3 internally uses [unjs/unctx](https://github.com/unjs/unctx) to support composables similar to Vue for plugins and middleware. This enables composables like `navigateTo()` to work without directly passing `nuxtApp` to them - bringing the DX and performance benefits of Composition API to the whole Nuxt framework.
|
||||
|
||||
Nuxt composables have the same design as the Vue Composition API and therefore need a similar solution to magically do this transform. Check out [unjs/unctx#2](https://github.com/unjs/unctx/issues/2) (proposal), [unjs/unctx#4](https://github.com/unjs/unctx/pull/4) (transform implementation), and [nuxt/framework#3884](https://github.com/nuxt/framework/pull/3884) (Integration to Nuxt).
|
||||
|
||||
Vue currently only supports async context restoration for `<script setup>` for async/await usage. In Nuxt 3, the transform support for `defineNuxtPlugin()` and `defineNuxtRouteMiddleware()` was added, which means when you use them Nuxt automatically transforms them with context restoration.
|
||||
|
||||
##### Remaining Issues
|
||||
|
||||
The `unjs/unctx` transformation to automatically restore context seems buggy with `try/catch` statements containing `await` which ultimately needs to be solved in order to remove the requirement of the workaround suggested above.
|
||||
|
||||
##### Native Async Context
|
||||
|
||||
Using a new experimental feature, it is possible to enable native async context support using [Node.js `AsyncLocalStorage`](https://nodejs.org/api/async_context.html#class-asynclocalstorage) and new unctx support to make async context available **natively** to **any nested async composable** without needing a transform or manual passing/calling with context.
|
||||
|
||||
::alert{type=warning}
|
||||
Native async context support works currently in Bun and Node.
|
||||
::
|
||||
|
||||
:ReadMore{link="/docs/guide/going-further/experimental-features#asynccontext"}
|
||||
|
@ -1,6 +1,6 @@
|
||||
# `useNuxtData`
|
||||
|
||||
`useNuxtData` gives you access to the current cached value of `useAsyncData`, `useLazyAsyncData`, `useFetch` and `useLazyFetch` with explicitly provided key.
|
||||
`useNuxtData` gives you access to the current cached value of [`useAsyncData`](/docs/api/composables/use-async-data) , `useLazyAsyncData`, [`useFetch`](/docs/api/composables/use-fetch) and [`useLazyFetch`](/docs/api/composables/use-lazy-fetch) with explicitly provided key.
|
||||
|
||||
## Type
|
||||
|
||||
|
@ -12,7 +12,7 @@ Within your pages, components, and plugins you can use `useRequestEvent` to acce
|
||||
const event = useRequestEvent()
|
||||
|
||||
// Get the URL
|
||||
const url = event.node.req.url
|
||||
const url = event.path
|
||||
```
|
||||
|
||||
::alert{icon=👉}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user