Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions content/en/01.vue/04.lifecycle/.template/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { GuideMeta } from '~/types/guides'

export const meta: GuideMeta = {
startingFile: 'app.vue',
features: {
terminal: false,
fileTree: false,
navigation: false,
},
}
3 changes: 3 additions & 0 deletions content/en/01.vue/04.lifecycle/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Vue Lifecycle Hooks

// TODO:
10 changes: 10 additions & 0 deletions content/en/01.vue/05.composition-api/.template/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { GuideMeta } from '~/types/guides'

export const meta: GuideMeta = {
startingFile: 'app.vue',
features: {
terminal: false,
fileTree: false,
navigation: false,
},
}
3 changes: 3 additions & 0 deletions content/en/01.vue/05.composition-api/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Vue Composition API

// TODO:
10 changes: 10 additions & 0 deletions content/en/01.vue/06.components/.template/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { GuideMeta } from '~/types/guides'

export const meta: GuideMeta = {
startingFile: 'app.vue',
features: {
terminal: false,
fileTree: false,
navigation: false,
},
}
1 change: 1 addition & 0 deletions content/en/01.vue/06.components/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Components
10 changes: 10 additions & 0 deletions content/en/01.vue/07.summary/.template/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { GuideMeta } from '~/types/guides'

export const meta: GuideMeta = {
startingFile: 'app.vue',
features: {
terminal: false,
fileTree: false,
navigation: false,
},
}
5 changes: 5 additions & 0 deletions content/en/01.vue/07.summary/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Vue Basics Summary

Great! You've learned the basics of Vue. If you are interested in learning more about Vue, check out the [official Vue documentation](https://vuejs.org/) for more in-depth guides and tutorials.

Up next, let's start to explore how Nuxt would make your development experience even better.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<template>
<CounterA />
<CounterB />
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script setup lang="ts">
const count = useState('count', () => 0)
</script>

<template>
<div>
<h2>Counter A</h2>
<p>Count: {{ count }}</p>
<button type="button" @click="count++">
Increment
</button>
</div>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script setup lang="ts">
const count = useState('count', () => 0)
</script>

<template>
<div>
<h2>Counter B</h2>
<p>Count: {{ count }}</p>
<button type="button" @click="count++">
Increment
</button>
</div>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { GuideMeta } from '~/types/guides'

export const meta: GuideMeta = {
startingFile: 'app.vue',
features: {
fileTree: true,
},
}
32 changes: 32 additions & 0 deletions content/en/02.concepts/10.state-manegement/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
ogImage: true
---

# State Management

In Vue.js, state management refers to managing reactive state within an application.\
[Vue.js Official Documentation: State Management](https://vuejs.org/guide/scaling-up/state-management)

When sharing state across multiple components, Vue.js allows for simple state management using the reactivity API.\
[Vue.js Official Documentation: Simple State Management with Reactivity API](https://vuejs.org/guide/scaling-up/state-management#simple-state-management-with-reactivity-api)

However, as mentioned in the [SSR Considerations](https://vuejs.org/guide/scaling-up/state-management#ssr-considerations), using SSR with Nuxt may lead to [certain issues](https://vuejs.org/guide/scaling-up/ssr#cross-request-state-pollution).

Although the official Vue.js documentation introduces a state management library called [Pinia](https://pinia.vuejs.org/), the `useState()` composable provided by Nuxt is also one of the solutions.\
(Of course, [using Pinia in Nuxt](https://nuxt.com/docs/getting-started/state-management#usage-with-pinia) is also possible.)

## useState()

The [useState() composable](https://nuxt.com/docs/api/composables/use-state) provides a simple way to manage SSR-friendly state and share it across components.\
`useState()` is an SSR-friendly `ref()` used to define shared state.\
As mentioned earlier, using Vue.js’s reactivity API such as `ref()` for cross-component state management in SSR may lead to issues.\
Therefore, in Nuxt, it is considered best practice to avoid defining state with `ref()` **outside** of `<script setup>` or the `setup()` function, and instead use `useState()`.

In the example in this playground, the state is shared between the `CounterA` and `CounterB` components using `"count"` as the key.\
Note that when the button rendered by `CounterA` is clicked, the state in `CounterB` also updates.

For more details, refer to the [Nuxt Official Documentation: State Management](https://nuxt.com/docs/getting-started/state-management).

:::note
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.
:::
15 changes: 15 additions & 0 deletions content/en/02.concepts/11.data-fetching/.template/files/app.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script setup lang="ts">
const { data: todos, refresh } = useFetch('/api/todos')
</script>

<template>
<button type="button" @click="refresh()">
Refresh
</button>

<ul>
<li v-for="todo in todos" :key="todo.id">
{{ todo.title }}
</li>
</ul>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default defineEventHandler(() => {
return [
{ id: 1, title: 'Todo 1' },
{ id: 2, title: 'Todo 2' },
{ id: 3, title: 'Todo 3' },
]
})
8 changes: 8 additions & 0 deletions content/en/02.concepts/11.data-fetching/.template/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { GuideMeta } from '~/types/guides'

export const meta: GuideMeta = {
startingFile: 'app.vue',
features: {
fileTree: true,
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script setup lang="ts">
const { data: todos, refresh } = useFetch('/api/todos')
</script>

<template>
<button type="button" @click="refresh()">
Refresh
</button>

<ul>
<li v-for="todo in todos" :key="todo.id">
<span :class="{ completed: todo.completed }">{{ todo.title }}</span>
</li>
</ul>
</template>

<style scoped>
.completed {
text-decoration: line-through;
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default defineEventHandler(() => {
return [
{ id: 1, title: 'Todo 1', completed: false },
{ id: 2, title: 'Todo 2', completed: false },
{ id: 3, title: 'Todo 3', completed: true },
{ id: 4, title: 'Todo 4', completed: false },
]
})
55 changes: 55 additions & 0 deletions content/en/02.concepts/11.data-fetching/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
ogImage: true
---

# Data Fetching

When building practical applications, data fetching is an essential feature.\
Data fetching refers to retrieving data from APIs or databases.

Nuxt provides useful functions like `useFetch`, `useAsyncData`, and `$fetch` to handle data fetching conveniently.

In short:

- `useFetch` is the simplest way to handle data fetching in a component's setup function.
- `$fetch` is ideal for making network requests based on user interactions.
- `useAsyncData` works with `$fetch` to provide more granular control.

https://nuxt.com/docs/getting-started/data-fetching

Among these, `useFetch` is the easiest to use. In fact, it is a convenient wrapper around `useAsyncData` and `$fetch`.

Here’s how to use it:

```vue
<script setup lang="ts">
const { data, pending, error, refresh, clear } = await useFetch('/api/modules')
</script>
```

Specifically, it offers the following features:

- Runs on both server and client\
Since `useFetch` works on both the server and client sides, it allows easy data fetching even during universal rendering.
- Data caching\
When the API is called on the server, the data is transferred to the client, preventing redundant fetching on the client side.
- Typed request URLs and responses\
By implementing the API inside the `server` directory, request URLs and response types are automatically inferred.

For more details, refer to the [official documentation](https://nuxt.com/docs/api/composables/use-fetch).

If you need finer control, you can use `useAsyncData` or `$fetch` for more advanced data fetching.

https://nuxt.com/docs/api/composables/use-async-data

https://nuxt.com/docs/api/utils/dollarfetch

## Challenge

1. Check that the API works
Add a fourth Todo item to `server/api/todos/index.ts`, then click the refresh button to verify that the data updates.

2. Check type inference
Add a `completed` property to the Todo in `server/api/todos/index.ts`, and confirm that the `useFetch` type updates accordingly.

:ButtonShowSolution{.bg-faded.px4.py2.mb3.rounded.border.border-base.hover:bg-active.hover:text-primary.hover:border-primary:50}
7 changes: 7 additions & 0 deletions content/en/02.concepts/12.error-handling/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
ogImage: true
---

# Error Handling

// TODO:
8 changes: 8 additions & 0 deletions content/ja/01.vue/04.lifecycle/.template/files/Child.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script setup lang="ts">
</script>

<template>
<div>
<h2>子コンポーネント</h2>
</div>
</template>
10 changes: 10 additions & 0 deletions content/ja/01.vue/04.lifecycle/.template/files/app.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<script setup lang="ts">
</script>

<template>
<div>
<button>子コンポーネントを切り替え</button>
<p>マウント回数: </p>
<p>アンマウント回数: </p>
</div>
</template>
10 changes: 10 additions & 0 deletions content/ja/01.vue/04.lifecycle/.template/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { GuideMeta } from '~/types/guides'

export const meta: GuideMeta = {
startingFile: 'app.vue',
features: {
terminal: false,
fileTree: true,
navigation: false,
},
}
20 changes: 20 additions & 0 deletions content/ja/01.vue/04.lifecycle/.template/solutions/Child.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<script setup lang="ts">
const emit = defineEmits<{
mounted: []
beforeUnmount: []
}>()

onMounted(() => {
emit('mounted')
})

onUnmounted(() => {
emit('beforeUnmount')
})
</script>

<template>
<div>
<h2>子コンポーネント</h2>
</div>
</template>
30 changes: 30 additions & 0 deletions content/ja/01.vue/04.lifecycle/.template/solutions/app.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<script setup lang="ts">
import Child from './Child.vue'

const isVisible = ref(true)
const mountCount = ref(0)
const unmountCount = ref(0)

function toggleVisibility() {
isVisible.value = !isVisible.value
}

function onMounted() {
mountCount.value++
}

function onBeforeUnmount() {
unmountCount.value++
}
</script>

<template>
<div>
<button @click="toggleVisibility">
子コンポーネントを切り替え
</button>
<p>マウント回数: {{ mountCount }}</p>
<p>アンマウント回数: {{ unmountCount }}</p>
<Child v-if="isVisible" @mounted="onMounted" @before-unmount="onBeforeUnmount" />
</div>
</template>
Loading