# Nuxt 3
## :sparkles: Install
```terminal
npx nuxi@latest init project_name
pnpm dlx nuxi@latest init project_name
```
## :sparkles: Configuration
#### App config
```javascript
// app.config.ts
export default defineAppConfig({
title: 'Hello Nuxt',
theme: {
dark: true,
colors: {
primary: '#ff0000'
}
}
})
// script setup
const appConfig = useAppConfig()
const title = appConfig.title // Hello Nuxt
```
#### Runtime config
```javascript
// nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
// The private keys which are only available server-side
API_KEY: process.env.API_KEY,
// Keys within public are also exposed client-side
public: {
API_BASE_URL: 'oh-my-god/api'
}
}
})
// script setup
const runtimeConfig = useRuntimeConfig();
runtimeConfig.public.API_BASE_URL
```
#### App config vs Runtime config
- **App config** is used for buildtime config and public information when **Runtime config** is used to expose env or sensitive infomation like private token
- Env support only to **Runtime config**
- Client-side can access **App config** and public **Runtime config**, but can't access private **Runtime config**
- Server-side can access all
#### Environment overrides
```javascript
export default defineNuxtConfig({
$production: {
routeRules: {
'/**': { isr: true }
}
},
$development: {
//
}
})
```
#### Path alias
```javascript
alias: {
"@utils": "../utils",
}
```
#### Enable experiment features
```javascript
// nuxt.config.js
export default defineNuxtConfig({
vue: {
defineModel: true,
propsDestructure: true
}
})
```
##### defineModel
```jsx
<script setup>
const modelValue = defineModel()
console.log(modelValue.value)
</script>
<template>
<input v-model="modelValue" />
</template>
```
## :sparkles: Views
#### Nitro plugin
```javascript
// 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);
});
});
```
## Assets
#### Public
Referencing an image file in the public/img/nuxt.png directory, available at the static URL /img/nuxt.png
```jsx
<template>
<img src="/img/nuxt.png" alt="Discover Nuxt 3" />
</template>
```
#### Assets
Global style import
```javascript
export default defineNuxtConfig({
vite: {
css: {
preprocessorOptions: {
scss: {
additionalData: '@use "@/assets/_colors.scss" as *;'
}
}
}
}
})
```
Local component style import
```sass
<style lang="scss" scoped>
@import "~/assets/test.scss";
@import "~/assets/css/test.css";
</style>
```
## Styling
#### Font
```css
/* main.css */
@font-face {
font-family: 'Minecraft';
src: url('~/fonts/Minecrafter.Alt.ttf') format('truetype');
font-weight: normal;
font-style: normal;
font-display: swap;
}
```
#### Postcss
```javascript
// nuxt.config.ts
export default defineNuxtConfig({
postcss: {
plugins: {
'postcss-autoprefixer': {}
}
}
})
```
```javascript
// plugins/postcss-autoprefixer.js
module.exports = {
plugins: {
autoprefixer: {}
}
}
```
## Routing
#### Middleware
- kebab-case
- to make it global, use suffix .global: auth.global.ts
```javascript
// middleware/auth.global.ts
export default defineNuxtRouteMiddleware(() => {
console.log('global middleware')
})
```
```javascript
// pages/test.vue
<script setup lang="ts">
definePageMeta({
middleware: "local",
});
</script>
```
#### Route validation
- if validate false, redirect to 404 page
```javascript
definePageMeta({
validate: async (route) => {
// Check if the id is made up of digits
return /^\d+$/.test('a');
},
});
```
## Fetching Data
#### $fetch
- Built-in library ofetch
- Can use anywhere
- Using $fetch in components without wrapping it with useAsyncData causes fetching the data twice: initially on the server, then again on the client-side during hydration, because $fetch does not transfer state from the server to the client
#### useFetch
- Composable
- Avoid refetching the same data when the code is executed in the browser
- Shorthand of useAsyncData with $fetch
#### useAsyncData
- Composable
- Avoid refetching the same data when the code is executed in the browser
- More options than useFetch
```jsx
<script setup lang="ts">
// During SSR data is fetched twice, once on the server and once on the client.
const dataTwice = await $fetch('/api/item')
// During SSR data is fetched only on the server side and transferred to the client.
const { data } = await useAsyncData('item', () => $fetch('/api/item'))
// You can also useFetch as shortcut of useAsyncData + $fetch
const { data } = await useFetch('/api/item')
</script>
```
## State management
- useState is an SSR-friendly ref replacement. Its value will be preserved after server-side rendering (during client-side hydration) and shared across all components using a unique key
- To globally invalidate cached state, use clearNuxtState(id?)
```jsx
<script setup lang="ts">
const counter = useState('counter', () => Math.round(Math.random() * 1000))
// const counter = useCounter()
</script>
<template>
<div>
Counter: {{ counter }}
<button @click="counter++">
+
</button>
<button @click="counter--">
-
</button>
</div>
</template>
```
## Error handling
#### During rendering lifecycle
- You can hook into Vue errors using onErrorCaptured. Or global propagate up by using vue:error
- For using an error reporting framework, you can provide a global handler through vueApp.config.errorHandler
```javascript
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.config.errorHandler = (error, context) => {
// ...
}
})
```
## Key concept
#### Auto import
- Components
```javascript
// change directory for auto import
export default defineNuxtConfig({
components: {
dirs: []
}
})
```
- Composables and Utils
```javascript
// disable autoImport
export default defineNuxtConfig({
imports: {
autoImport: false
}
})
```
- Built-in: fetch, useAsyncData (nuxt), ref, computed (Vue)
- Cannot use them outside a Nuxt plugin, Nuxt route middleware or Vue setup function
```javascript
const config = useRuntimeConfig()
export const useMyComposable = () => {
// accessing runtime config here => error
}
```
```javascript
export const useMyComposable = () => {
// Because your composable is called in the right place in the lifecycle => work
const config = useRuntimeConfig()
}
```
- Third-party package
```javascript
export default defineNuxtConfig({
imports: {
presets: [
{
from: 'vue-i18n',
imports: ['useI18n']
}
]
}
})
```
#### Components
- Custom auto import components
```javascript
export default defineNuxtConfig({
components: [
{ path: '~/user-module/components', options },
//
path: '~/components',
+ extensions: ['.vue'],
pathPrefix: false,
prefix: false,
]
})
```
- Dynamic component
```jsx
<script setup lang="ts">
import { SomeComponent } from '#components'
// or
const MyButton = resolveComponent('MyButton')
</script>
<template>
<component :is="clickable ? MyButton : 'div'" />
</template>
```
- Lazy-loading component
```jsx
<template>
<div>
<h1>Mountains</h1>
<LazyMountainsList v-if="show" /> // component: MountainsList
<button v-if="!show" @click="show = true">Show List</button>
</div>
</template>
```
- Client-only component
```jsx
<template>
<div>
<Sidebar />
<!-- This renders the "span" element on the server side -->
<ClientOnly fallbackTag="span">
<!-- this component will only be rendered on client side -->
<Comments />
<template #fallback>
<!-- this will be rendered on server side -->
<p>Loading comments...</p>
</template>
</ClientOnly>
</div>
</template>
```
Or use suffix component name: Comments.client.vue
- Server-only component, Dev-only component