init at work
This commit is contained in:
+250
@@ -0,0 +1,250 @@
|
||||
# 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]]
|
||||
Reference in New Issue
Block a user