> ## Documentation Index
> Fetch the complete documentation index at: https://learn.narau.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Vue

> Reactive data, components, composition API, and more.

[Vue](https://vuejs.org/) is a progressive framework for building user interfaces. It is designed to be incrementally adoptable and scalable.

1. Basically, it does the same thing as [React](https://react.dev/) but better.

### Vue SFC Basics

Most Vue apps use [Single File Components](https://vuejs.org/api/sfc-spec.html).

```diff lang="vue" twoslash theme={null}
#sfc.vue
+ `setup` is the new recommended syntax for composition API.
+ It provides better TS support and less boilerplate.

<script setup lang="ts">
import { ref } from "vue";

const count = ref(0);
</script>

+ template can access reactive state directly without `.value`
<template>
	<button @click="count++">Count: {{ count }}</button>
</template>

+ scoped styles only apply to this component
<style scoped>
button {
	font-weight: 600;
}
</style>
```

## Reactivity Essentials

For reactivity, Vue provides `ref`, `reactive`, `computed`, and `watch`.

1. `ref` creates a reactive primitive value.
2. `reactive` creates a reactive object.

> \[!WARNING] `reactive` vs `ref`
> Unlike `ref`, which requires you to append `.value`, `reactive` properties are accessed directly, making the code cleaner.

<iframe className="w-full aspect-video rounded-xl" src="https://www.youtube.com/embed/o8B4SguvUqk" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen />

```ts twoslash wrap theme={null}
import { reactive } from 'vue';

// @log: reactive can not hold primitives like numbers or strings
const framework = reactive({
  name: 'Vue',
  version: '3.0'
})

framework.name = 'Vue.js';

// @errors: 2345
const count = reactive(0)
```

```ts twoslash wrap theme={null}
import { ref } from 'vue';

const framework = ref({
  name: 'Vue',
  version: '3.0'
})

framework.value.name = 'Vue.js';

const count = ref(0);
```

3. `computed` creates a derived reactive value.

```ts twoslash theme={null}
import { computed, ref } from 'vue';

const count = ref(0);

// @log: double will be automatically updated when count changes
const double = computed(() => {
  return count.value * 2;
});

// @errors: 2540
double.value = 4;
```

4. `watch` runs a side-effect function when a reactive source changes.
   * Learn more about [Watchers](https://vuejs.org/guide/essentials/watchers).

```ts twoslash theme={null}
import { ref, watch } from 'vue';

const count = ref(0);

watch(count, (newValue, oldValue) => {
  console.log(`Count changed from ${oldValue} to ${newValue}`);
});
```

```ts twoslash ins="Do something when the score changes" theme={null}
import { reactive, watch } from 'vue';

const user = reactive({
  id: 1,
  name: 'Mahraib Fatima',
  score: 100,
});

// @log: fine grained watching with a getter function
watch(() => user.score, (value) => {
  // Do something when the score changes
});
```

5. `shallowRef` is typically used for performance optimizations of large data structures that don't require deep reactivity.

```ts twoslash theme={null}
import { shallowRef } from 'vue';

const state = shallowRef({ count: 1 })

// @log: does NOT trigger change
state.value.count = 2

// @log: does trigger change
state.value = { count: 2 }
```

Check [Reactivity API: Core](https://vuejs.org/api/reactivity-core) and [Reactivity API: Advanced](https://vuejs.org/api/reactivity-advanced).

## Template Syntax

[Vue directives](https://vuejs.org/api/built-in-directives.html) are special attributes with `v-` prefix that apply reactive behavior to the DOM.

1. `v-bind` can spread multiple props from one object. `:` is shorthand for `v-bind:` btw.

```diff lang="vue" theme={null}
<script setup lang="ts">
import User from './components/user.vue';

const user = {
  name: 'Mahraib Fatima',
  score: 100,
}
</script>

<template>
- <User :name="user.name" :score="user.score" />
+ <User v-bind="user" />
</template>
```

2. Similarly, `v-on` can attach multiple listeners from one object, and `@` is shorthand for `v-on:`.

### Dynamic components

Use this pattern for tabs, widget renderers, or configurable UI blocks.

```vue theme={null}
<script setup lang="ts">
import { shallowRef } from 'vue';
import User from './components/user.vue';

const component = shallowRef(User)

const user = {
  msg: 'Mahraib Fatima',
  score: 99,
}
</script>

<template>
  <component :is="component" v-bind="user" />
</template>
```

More about [Built-in Special Elements](https://vuejs.org/api/built-in-special-elements.html).

### Async Components

[Async components](https://vuejs.org/api/general.html#defineasynccomponent) are loaded on demand, which can improve performance for large apps and reduce initial bundle size.

```vue wrap "defineAsyncComponent" theme={null}
#async-components.vue
<script setup lang="ts">
import { defineAsyncComponent } from 'vue';

const user = {
  msg: 'Novid Azhar',
  score: 12,
}

const LazyUser = defineAsyncComponent(() => import('./components/user.vue'));

</script>

<template>
  <LazyUser v-bind="user" />
</template>
```

### Props

1. `defineProps` is the recommended way to declare props.

```vue del="no need for props. prefix in template" theme={null}
#props.vue 
<script setup lang="ts">
const props = defineProps<{
	title: string;
	done?: boolean;
}>();
</script>

<template>
  <!-- no need for props. prefix in template -->
  <h2>{{ title }}</h2>
</template>
```

Check [Props](https://vuejs.org/guide/components/props.html) and [Typing Component Props](https://vuejs.org/guide/typescript/composition-api.html#typing-component-props).

### Emits

1. `defineEmits` is the recommended way to declare emitted events.

> \[!WARNING] Emits are used to for child-to-parent communication.
> They allow a `child` component to send a custom event with optional data payload up to its `parent` component

```vue theme={null}
<script setup lang="ts">
const emit = defineEmits<{
	save: [id: string];
}>();

function onSave() {
	emit("save", "task-1");
}
</script>

<template>
	<button @click="onSave">Save</button>
</template>
```

### Models

1. `defineModel` is used for building two-way component bindings.

```vue theme={null}
#component.vue
<script setup lang="ts">
import { watch } from 'vue';

const title = defineModel<string>("title", { required: true });
const done = defineModel<boolean>("done", { default: false });

watch(title, (newTitle) => {
  console.log("Title changed to:", newTitle);
});
</script>

<template>
  <input v-model="title" />
  <input type="checkbox" v-model="done" />
</template>
```

> \[!WARNING] Parent usage
>
> ```vue theme={null}
> <script setup lang="ts">
> import { ref } from 'vue';
> import User from './components/user.vue';
>
> const title = ref("")
> const done = ref(false)
> </script>
>
> <template>
>  <User :title :done />
> </template>
> ```

### Expose

1. `defineExpose` controls what methods$/$properties are accessible by the parent when using template refs.

```vue theme={null}
#component.vue
<script setup lang="ts">
import { ref } from 'vue';

const title = ref("")

defineExpose({
  title,
})
</script>

<template>
  <input v-model="title" />
</template>
```

```vue theme={null}
#app.vue
<script setup lang="ts">
import { ref, watch } from 'vue';
import User from './components/user.vue';

const userRef = ref<InstanceType<typeof User> | null>(null)

watch(() => userRef.value?.title, (newValue) => {
  console.log("Title changed to:", newValue);
})
</script>

<template>
  <User ref="userRef" />
</template>
```

<iframe className="w-full aspect-video rounded-xl" src="https://www.youtube.com/embed/PLQTJ5qL1rg" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen />

## Transparent Components

[Transparent components](https://vuejs.org/api/options-misc.html#inheritattrs) are wrapper components that forward all parent-provided attributes and listeners to an inner element, bypassing the root element's automatic attribute inheritance.

```vue theme={null}
#component.vue
<script setup lang="ts">
import { ref } from 'vue';

defineOptions({ inheritAttrs: false })

const title = ref("")
</script>

<template>
  <div>{{ title }}</div>
  <input v-model="title" v-bind="$attrs" />
</template>
```

> \[!WARNING] Parent usage
> All of these `attributes` will become attributes of `input`.
>
> ```vue theme={null}
> <User aria-label="User input" id="user" />
> ```
>
> `props` will not be forwarded to the inner element.

## Slots

1. `<slot>` is used inside a child component to declare insertion points.
2. `v-slot` is used in the parent when providing content to named$/$scoped slots. `#` is shorthand for `v-slot:`.

```vue del="Child" theme={null}
#user.vue
<!-- Child -->
<template>
  <slot name="header" title="Mahraib" description="a simple blog" />
  <slot name="default" />
</template>
```

```vue del="Parent" ins="slot name is optional, defaults to" del="default" theme={null}
#app.vue
<!-- Parent -->
<template>
  <User>
    <template #header="props"> <!-- or v-slot:header -->
      <h1>{{ props.title }}</h1>
      <p>{{ props.description }}</p>
    </template>

    <!--  slot name is optional, defaults to default  -->
    <template v-slot> <!-- or just # -->
      <p>Here goes the content of the blog</p>
    </template>
  </User>
</template>
```

1. You can `destructure` slot props as well.

```vue theme={null}
<template #header="{ title, description }">
  <h1>{{ title }}</h1>
  <p>{{ description }}</p>
</template>
```

2. Use the `$slots` property with a `v-if` to achieve [Conditional Slots](https://vuejs.org/guide/components/slots.html#conditional-slots).

```vue ins="header will render if parent provides content for #header" theme={null}
<template>
  <!-- header will render if parent provides content for #header -->
  <header v-if="$slots.header">
    <slot name="header" title="Mahraib" description="a blog" />
  </header>
  <slot name="default" />
</template>
```

Check [Slots](https://vuejs.org/guide/components/slots).

## Template Refs

1. [Template Refs](https://vuejs.org/guide/essentials/template-refs.html): There may be cases where we need direct access to the underlying DOM elements. To achieve this, we can use the special `ref` attribute.

2. To obtain the reference with Composition API, we can use the `useTemplateRef()`  helper.

```vue del="the first argument must match the ref value in the template" theme={null}
<script setup>
import { useTemplateRef, onMounted } from 'vue'

// the first argument must match the ref value in the template
const input = useTemplateRef('my-input')

onMounted(() => {
  input.value.focus()
})
</script>

<template>
  <input ref="my-input" />
</template>
```

## Composables

1. [Composables](https://vuejs.org/guide/reusability/composables.html) are Vue-aware functions that use reactive APIs$/$lifecycle hooks.
2. `Utils` are plain framework-agnostic functions (formatters, parsers, helpers, etc.)

<iframe className="w-full aspect-video rounded-xl" src="https://www.youtube.com/embed/N0QrFKBZuqA" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen />

> \[!WARNING] Flexible Composable Inputs
> Try to keep composable arguments flexible by accepting static values, refs, or getters.
>
> 1. Use `toValue()` or `toRef()` to normalize them. [Reactivity API: Utilities](https://vuejs.org/api/reactivity-utilities.html)

```ts twoslash theme={null}
#useFetch.ts
import { ref, toValue, type MaybeRefOrGetter } from 'vue'

export function useFetch(url: MaybeRefOrGetter<string>) {
  const finalUrl = toValue(url)

  const data = ref<unknown>(null)
  const error = ref<unknown>(null)

  fetch(finalUrl)
    .then((res) => res.json())
    .then((json) => (data.value = json))
    .catch((err) => (error.value = err))

  return { data, error }
}
```

```ts twoslash del="Getter" theme={null}
// @noErrors
// @log: reusable fetch composable with flexible input
import { useFetch } from './fetch.js'
import { ref } from 'vue';

const url = ref('')

const { data, error } = useFetch('https://api.example.com/data')

// @log: works with refs and getters as well
const { data, error } = useFetch(url)
const { data, error } = useFetch(() => url.value)
```

Check [Utility Types](https://vuejs.org/api/utility-types.html#maybereforgetter).

> \[!TIP] A simple alternative to a full state management library for small features.
> `Singleton shared state` can be implemented with a composable that holds reactive state.

```ts twoslash theme={null}
#useTask.ts
import { readonly, ref } from "vue";

const tasks = ref<string[]>([]);

function addTask(task: string) {
	tasks.value.push(task);
}

export function useTask() {
	return {
		tasks: readonly(tasks),
		addTask,
	};
}
```

## Routing

[Vue Router](https://router.vuejs.org/guide/) is the official router for Vue.js.

### File-based routing

File-based routing maps file paths to routes automatically, common in [Nuxt](https://nuxt.com/docs/4.x/getting-started/routing). You can use it in [Vue](https://vuejs.org/) with [unplugin-vue-router](https://uvr.esm.is/introduction.html).

1. It generates typed routes from your file structure and gives type-safe navigation.

## VueUse

[VueUse](https://vueuse.org/) is a collection of high-quality composables.

<iframe className="w-full aspect-video rounded-xl" src="https://www.youtube.com/embed/HaeNQXJ-sgs" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen />

## State Management

You can use [composables](https://vuejs.org/guide/scaling-up/state-management.html#simple-state-management-with-reactivity-api) for simple shared state, but for larger apps, a state management library like [Pinia](https://pinia.vuejs.org/) is recommended.

1. Avoid a single giant global store. Instead, create multiple small stores focused on specific domains or features.

<iframe className="w-full aspect-video rounded-xl" src="https://www.youtube.com/embed/zPeA1q00A54" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen />

<iframe className="w-full aspect-video rounded-xl" src="https://www.youtube.com/embed/Md8bNJVQQFA" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen />
