251 lines
4.2 KiB
Markdown
Executable File
251 lines
4.2 KiB
Markdown
Executable File
# Components, Props, Lifecycle, and Composables
|
|
|
|
This note explains how Vue code is organized into reusable pieces.
|
|
|
|
## Components
|
|
|
|
A component is a reusable UI unit.
|
|
|
|
Examples from your project:
|
|
|
|
- `FlexibilityTable.vue`
|
|
- `FileUpload.vue`
|
|
- `AppHeader.vue`
|
|
|
|
A parent component can render a child component.
|
|
|
|
```vue
|
|
<template>
|
|
<UserCard />
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import UserCard from './UserCard.vue';
|
|
</script>
|
|
```
|
|
|
|
## Props: inputs passed into a component
|
|
|
|
Props are like function arguments for components.
|
|
|
|
Example:
|
|
|
|
```vue
|
|
<template>
|
|
<UserCard :name="username" :age="34" />
|
|
</template>
|
|
```
|
|
|
|
Child component:
|
|
|
|
```vue
|
|
<script setup lang="ts">
|
|
interface Props {
|
|
name: string;
|
|
age: number;
|
|
}
|
|
|
|
const props = defineProps<Props>();
|
|
</script>
|
|
|
|
<template>
|
|
<p>{{ props.name }} is {{ props.age }} years old.</p>
|
|
</template>
|
|
```
|
|
|
|
## Python comparison
|
|
|
|
This is similar to passing constructor arguments or function arguments.
|
|
|
|
```python
|
|
render_user_card(name=username, age=34)
|
|
```
|
|
|
|
## Your real example
|
|
|
|
In FlexibilityTable:
|
|
|
|
```ts
|
|
interface FlexibilityTableProps {
|
|
title: string;
|
|
uploadMode?: 'fsp' | 'tso' | 'both' | 'none';
|
|
}
|
|
|
|
const props = withDefaults(defineProps<FlexibilityTableProps>(), {
|
|
uploadMode: 'none',
|
|
});
|
|
```
|
|
|
|
This means:
|
|
|
|
- the parent must give `title`
|
|
- the parent may give `uploadMode`
|
|
- if `uploadMode` is missing, use `'none'`
|
|
|
|
Then template code can use:
|
|
|
|
```vue
|
|
<v-toolbar-title>
|
|
{{ title }}
|
|
</v-toolbar-title>
|
|
```
|
|
|
|
and script code can use:
|
|
|
|
```ts
|
|
props.uploadMode
|
|
```
|
|
|
|
## Lifecycle: when should code run?
|
|
|
|
Sometimes you want code to run when the component appears.
|
|
|
|
```ts
|
|
import { onMounted } from 'vue';
|
|
|
|
onMounted(() => {
|
|
console.log('Component is mounted');
|
|
});
|
|
```
|
|
|
|
In your component:
|
|
|
|
```ts
|
|
onMounted(async () => {
|
|
await refreshTableDataFromBackend();
|
|
});
|
|
```
|
|
|
|
That means when the table component is loaded, it immediately fetches backend data.
|
|
|
|
## Composables
|
|
|
|
A composable is a reusable function that contains Vue logic.
|
|
|
|
Common pattern:
|
|
|
|
```ts
|
|
export function useCounter() {
|
|
const count = ref(0);
|
|
|
|
function increment() {
|
|
count.value += 1;
|
|
}
|
|
|
|
return {
|
|
count,
|
|
increment,
|
|
};
|
|
}
|
|
```
|
|
|
|
Use it inside a component:
|
|
|
|
```ts
|
|
const { count, increment } = useCounter();
|
|
```
|
|
|
|
## Why composables exist
|
|
|
|
Without composables, components get too big.
|
|
|
|
Instead of putting all API logic, auth logic, upload logic, and export logic directly in one component, you move reusable logic into composables.
|
|
|
|
Your project has examples:
|
|
|
|
- `useJsonDownload()`
|
|
- `useJsonUpload()`
|
|
- `useAuth()`
|
|
|
|
## Real example: `useJsonDownload`
|
|
|
|
In FlexibilityTable:
|
|
|
|
```ts
|
|
const { downloadFlexData, downloadRawFlexData } = useJsonDownload();
|
|
```
|
|
|
|
That means:
|
|
|
|
- this component imports data-loading logic from a composable
|
|
- the composable handles HTTP and backend transformation details
|
|
- the component only calls its functions
|
|
|
|
This is similar to separating Python business logic into a helper module.
|
|
|
|
Python style:
|
|
|
|
```python
|
|
from downloads import download_flex_data
|
|
|
|
rows = download_flex_data(payload)
|
|
```
|
|
|
|
Vue composable style:
|
|
|
|
```ts
|
|
const { downloadFlexData } = useJsonDownload();
|
|
const rows = await downloadFlexData(payload);
|
|
```
|
|
|
|
## Child-to-parent communication
|
|
|
|
Props go down from parent to child.
|
|
|
|
Events often go up from child to parent.
|
|
|
|
Example:
|
|
|
|
```vue
|
|
<FileUpload @upload="onFspUpload" />
|
|
```
|
|
|
|
This means the child component emits an `upload` event, and the parent runs `onFspUpload` when that happens.
|
|
|
|
## Example structure
|
|
|
|
```vue
|
|
<template>
|
|
<SearchBox @search="runSearch" />
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
function runSearch(query: string) {
|
|
console.log('search query:', query);
|
|
}
|
|
</script>
|
|
```
|
|
|
|
The child says: "something happened".
|
|
The parent decides what to do.
|
|
|
|
## Rule of Thumb
|
|
|
|
Put code in a component when:
|
|
|
|
- it is very local to one UI element
|
|
- it mostly controls rendering
|
|
|
|
Put code in a composable when:
|
|
|
|
- it will be reused
|
|
- it handles data fetching
|
|
- it contains business logic
|
|
- it makes the component easier to read
|
|
|
|
## Reading Strategy
|
|
|
|
When you see a component, identify:
|
|
|
|
1. props coming in
|
|
2. local reactive state
|
|
3. composables being used
|
|
4. lifecycle hooks
|
|
5. events going to child components
|
|
|
|
## Related Notes
|
|
|
|
- [[00-Vue for Python Developers]]
|
|
- [[02-Templates, v-model, and Events]]
|
|
- [[04-FlexibilityTable Filter Flow]]
|