# Rush: Svelte
:::info
đź”—**References**:
- https://svelte.dev/
:::
:::success
:::spoiler **Table of content**
[ToC]
:::
<hr>
## 1. # Rush: Svelte
:::info
đź”—**References**:
- https://svelte.dev/
:::
:::success
:::spoiler **Table of content**
[ToC]
:::
<hr>
## 1. Fundamentals
### Svelte Component
```xml
<script>
(1) <-- Business Logic
</script>
(2) <-- Markups
<style>
(3) <-- Styling
</style>
```
Every component needs to be putted into a `.svelte` file
#### `<script>` block
- `export` keyword mark a variable declaration as a **prop** (*component consumer is able to use it*).
- export `const`, `class`, `function`: will be **read-only**
- use cannot `export default` any props.
- Assignments are **reactive**: re-render component when a value changed
- Assignments are the trigger for re-render, using array-methods (like `.push()`, `.splice()`) do not trigger it.
- `$` marks a statement reactive:
- **Eg**: if `title` changed, `document.title` would change respectively
```jsx
<script>
$: document.title = title
</script>
```
- `$storeValue`: access the store in ***store contract***.
- A stored value must be declared at **outmost scope**.
- To assign new value: use `storeValue.set(newValue)`
- `<script context="module">`:
- make the content ***run once when the module firstly evaluted*** rather than each components.
#### `<style>` block
To store CSS :v obviously
- `:global(X)`: to apply CSS for X component for all, not just inside this component
### Basic component
#### Import & use a component
```jsx
<script>
import Widget from './Widget.svelte'
</script>
<div>
<Widget /> /* <-- this is a component */
</div>
```
#### Attributes
Work as same as HTML counterpart. However, there are some empowered syntatic sugars:
- Embedding attribute / content value:
```jsx
<a href="/account/{ id }"> { id } </a>
```
- Appearance of boolean attributes based on boolean (truth / falsy) value:
```jsx
<a href="/account/{ id }" disabled={ isBanned }> { id } </a>
```
(*As you can see, when `isBanned` is falsy, the attribute `disabled` will not be included*)
- Respectively, an attribute may not be included when passed a nullish value (`null`, `undefined`)
```jsx
<a href="/account/{ id }" id={ null }> { id } </a>
```
- Shorthand syntax for the attributes which has the same name with passed values:
```jsx
<a href="/account/{ id }" { id } { disabled } > { id } </a>
```
#### Props
As same as attributes, we pass them into a component like this:
```jsx
<Component value={31} { flag } otherValue="testValue" />
```
Or can be written using ***Spread attributes***
```jsx
<Component {...args} />
```
It's replacable with `$$props` - a reference to all props which are passed into the component.
```jsx
<Component {...$$props} />
```
- **`$$restProps`**: refers to passed props without `export` declaration.
#### Branching statement
```jsx
{#if expression}
...
{:else if expression}
...
{:else}
...
{/if}
```
#### For-each loop
```jsx
{#each expression as name}
...
{:else}
...
{/each}
```
The way we write the `each` open-statement can be differential:
```jsx
{#each expression as name, index}
{#each expression as name (key)}
{#each expression as name, index (key)}
```
We can also use **destructuring & rest patterns**:
```jsx
{#each objects as { id, ...rest }}
<li><span>{id}</span><MyComponent {...rest} /></li>
{/each}
```
```jsx
{#each items as [id, ...rest]}
<li><span>{id}</span><MyComponent values={rest} /></li>
{/each}
```
#### Asynchronous with Promise
```jsx
{#await expression}...{:then name}...{:catch name}...{/await}
```
We can rewrite the `await` clause like this:
```jsx
{#await expression then name}...{/await}
{#await expression catch name}...{/await}
```
#### Recreation
Key blocks destroy and recreate their contents when the value of an expression changes.
```hbs
{#key expression}
...
{/key}
```
### Special tags
- **`{@html validHtmlExpression}`**: render escaped HTML expression
- **`{@debug val}`**: equivalents with `console.log(X)`
## SvelteKit
A **framework** for Svelte (*as same as NextJS for React*)
To create a new project, run: `npm create svelte@latest <APP_NAME>`
### Project structure
- `src/lib`: library code (ultilities & components), which can be imported by using **`$lib`**
- `src/param`: param matchers
- `src/routes`: routing (*optional*)
### Core concepts: Routing
**Filesystem-based routing approach.** Each route dir contains 1/n **route files** (with `+` prefix).
:::warning
Note that SvelteKit uses `<a>` elements to navigate between routes, rather than a framework-specific `<Link>` component.
:::
- `src/routes/`: root route.
- `src/routes/+page.svelte`: component defines a page for the app
- `src/routes/+page.js`: contains **exported `load()` function**, use for pre-rendering logics (*such as loading data*)
- `src/routes/+page.server.js`: to identify that the `load()` function is server-only runnable
- `src/routes/+error.svelte`: default error page when `load()` has failed.
- `src/routes/+error.server.js`: respectively
- `src/routes/about`: create an `/about` route
- `src/routes/products/[product-name]`: create a `/products` route with path param `[product-name]`.
<br>
- `src/routes/+layout.svelte`: create a layout that applies to every page. Used with `<slot>` tag.
- `src/routes/+layout.js`: as same as other `.js` files
1. You define your `+page.js` file to load some data:
```typescript
/** @type {import('./$types').PageLoad} */
export function load({ params }) {
return {
post: {
title: `Title for ${params.slug} goes here`,
content: `Content for ${params.slug} goes here`
}
};
}
```
2. You consume the data and render it in `+page.svelte`:
```jsx
<script>
/** @type {import('./$types').PageData} */
export let data;
</script>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
```
#### Get the data of parent `load()`
- `src/routes/+layout.js`
```typescript
/** @type {import('./$types').LayoutLoad} */
export function load() {
return { a: 1 };
}
```
- `src/routes/abc/+layout.js`
```typescript
/** @type {import('./$types').LayoutLoad} */
export async function load({ parent }) {
const { a } = await parent();
return { b: a + 1 };
}
```
### Core concepts: Form actions
```jsx
<form
method="POST"
use:enhance={({ formElement, formData, action, cancel, submitter }) => {
// `formElement` is this `<form>` element
// `formData` is its `FormData` object that's about to be submitted
// `action` is the URL to which the form is posted
// calling `cancel()` will prevent the submission
// `submitter` is the `HTMLElement` that caused the form to be submitted
return async ({ result, update }) => {
// `result` is an `ActionResult` object
// `update` is a function which triggers the default logic that would be triggered if this callback wasn't set
};
}}
>
```
- **`formElement`**: refers to `<form>` element
- **`formData`**: is its `FormData` object that's about to be submitted
- **`action`**: is the URL to which the form is posted
- calling `cancel()` will prevent the submission
- **`submitter`**: is the `HTMLElement` that caused the form to be submitted
### Core concept: State management
There are some rules:
1. Browser are ***stateful*** while server is ***stateless***
2. No side-effects in `load()`
**NEVER DO THIS:**
```typescript
import { user } from '$lib/user';
/** @type {import('./$types').PageLoad} */
export async function load({ fetch }) {
const response = await fetch('/api/user');
// NEVER DO THIS!
user.set(await response.json());
}
```
Use **CONTEXT API** instead.
- `src/routes/+layout.svelte`
```typescript
<script>
import { setContext } from 'svelte';
import { writable } from 'svelte/store';
/** @type {import('./$types').LayoutData} */
export let data;
// Create a store and update it when necessary...
const user = writable();
$: user.set(data.user);
// ...and add it to the context for child components to access
setContext('user', user);
</script>
```
- `src/routes/+page.svelte`
```jsx
<script>
import { getContext } from 'svelte';
// Retrieve user store from context
const user = getContext('user');
</script>
<p>Welcome {$user.name}</p>
```
### Hooks
Hooks are **app-wide functions** which will be called in response to specific events. You can declare within 2 files:
- `src/hooks.servers.js`
- `src/hooks.client.js`
### Svelte Component
```xml
<script>
(1) <-- Business Logic
</script>
(2) <-- Markups
<style>
(3) <-- Styling
</style>
```
Every component needs to be putted into a `.svelte` file
#### `<script>` block
- `export` keyword mark a variable declaration as a **prop** (*component consumer is able to use it*).
- export `const`, `class`, `function`: will be **read-only**
- use cannot `export default` any props.
- Assignments are **reactive**: re-render component when a value changed
- Assignments are the trigger for re-render, using array-methods (like `.push()`, `.splice()`) do not trigger it.
- `$` marks a statement reactive:
- **Eg**: if `title` changed, `document.title` would change respectively
```jsx
<script>
$: document.title = title
</script>
```
- `$storeValue`: access the store in ***store contract***.
- A stored value must be declared at **outmost scope**.
- To assign new value: use `storeValue.set(newValue)`
- `<script context="module">`:
- make the content ***run once when the module firstly evaluted*** rather than each components.
#### `<style>` block
To store CSS :v obviously
- `:global(X)`: to apply CSS for X component for all, not just inside this component
### Basic component
#### Import & use a component
```jsx
<script>
import Widget from './Widget.svelte'
</script>
<div>
<Widget /> /* <-- this is a component */
</div>
```
#### Attributes
Work as same as HTML counterpart. However, there are some empowered syntatic sugars:
- Embedding attribute / content value:
```jsx
<a href="/account/{ id }"> { id } </a>
```
- Appearance of boolean attributes based on boolean (truth / falsy) value:
```jsx
<a href="/account/{ id }" disabled={ isBanned }> { id } </a>
```
(*As you can see, when `isBanned` is falsy, the attribute `disabled` will not be included*)
- Respectively, an attribute may not be included when passed a nullish value (`null`, `undefined`)
```jsx
<a href="/account/{ id }" id={ null }> { id } </a>
```
- Shorthand syntax for the attributes which has the same name with passed values:
```jsx
<a href="/account/{ id }" { id } { disabled } > { id } </a>
```
#### Props
As same as attributes, we pass them into a component like this:
```jsx
<Component value={31} { flag } otherValue="testValue" />
```
Or can be written using ***Spread attributes***
```jsx
<Component {...args} />
```
It's replacable with `$$props` - a reference to all props which are passed into the component.
```jsx
<Component {...$$props} />
```
- **`$$restProps`**: refers to passed props without `export` declaration.
#### Branching statement
```jsx
{#if expression}
...
{:else if expression}
...
{:else}
...
{/if}
```
#### For-each loop
```jsx
{#each expression as name}
...
{:else}
...
{/each}
```
The way we write the `each` open-statement can be differential:
```jsx
{#each expression as name, index}
{#each expression as name (key)}
{#each expression as name, index (key)}
```
We can also use **destructuring & rest patterns**:
```jsx
{#each objects as { id, ...rest }}
<li><span>{id}</span><MyComponent {...rest} /></li>
{/each}
```
```jsx
{#each items as [id, ...rest]}
<li><span>{id}</span><MyComponent values={rest} /></li>
{/each}
```
#### Asynchronous with Promise
```jsx
{#await expression}...{:then name}...{:catch name}...{/await}
```
We can rewrite the `await` clause like this:
```jsx
{#await expression then name}...{/await}
{#await expression catch name}...{/await}
```
#### Recreation
Key blocks destroy and recreate their contents when the value of an expression changes.
```hbs
{#key expression}
...
{/key}
```
### Special tags
- **`{@html validHtmlExpression}`**: render escaped HTML expression
- **`{@debug val}`**: equivalents with `console.log(X)`
## SvelteKit
A **framework** for Svelte (*as same as NextJS for React*)
To create a new project, run: `npm create svelte@latest <APP_NAME>`
### Project structure
- `src/lib`: library code (ultilities & components), which can be imported by using **`$lib`**
- `src/param`: param matchers
- `src/routes`: routing (*optional*)
### Core concepts: Routing
**Filesystem-based routing approach.** Each route dir contains 1/n **route files** (with `+` prefix).
:::warning
Note that SvelteKit uses `<a>` elements to navigate between routes, rather than a framework-specific `<Link>` component.
:::
- `src/routes/`: root route.
- `src/routes/+page.svelte`: component defines a page for the app
- `src/routes/+page.js`: contains **exported `load()` function**, use for pre-rendering logics (*such as loading data*)
- `src/routes/+page.server.js`: to identify that the `load()` function is server-only runnable
- `src/routes/+error.svelte`: default error page when `load()` has failed.
- `src/routes/+error.server.js`: respectively
- `src/routes/about`: create an `/about` route
- `src/routes/products/[product-name]`: create a `/products` route with path param `[product-name]`.
<br>
- `src/routes/+layout.svelte`: create a layout that applies to every page. Used with `<slot>` tag.
- `src/routes/+layout.js`: as same as other `.js` files
1. You define your `+page.js` file to load some data:
```typescript
/** @type {import('./$types').PageLoad} */
export function load({ params }) {
return {
post: {
title: `Title for ${params.slug} goes here`,
content: `Content for ${params.slug} goes here`
}
};
}
```
2. You consume the data and render it in `+page.svelte`:
```jsx
<script>
/** @type {import('./$types').PageData} */
export let data;
</script>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
```
#### Get the data of parent `load()`
- `src/routes/+layout.js`
```typescript
/** @type {import('./$types').LayoutLoad} */
export function load() {
return { a: 1 };
}
```
- `src/routes/abc/+layout.js`
```typescript
/** @type {import('./$types').LayoutLoad} */
export async function load({ parent }) {
const { a } = await parent();
return { b: a + 1 };
}
```
### Core concepts: Form actions
```jsx
<form
method="POST"
use:enhance={({ formElement, formData, action, cancel, submitter }) => {
// `formElement` is this `<form>` element
// `formData` is its `FormData` object that's about to be submitted
// `action` is the URL to which the form is posted
// calling `cancel()` will prevent the submission
// `submitter` is the `HTMLElement` that caused the form to be submitted
return async ({ result, update }) => {
// `result` is an `ActionResult` object
// `update` is a function which triggers the default logic that would be triggered if this callback wasn't set
};
}}
>
```
- **`formElement`**: refers to `<form>` element
- **`formData`**: is its `FormData` object that's about to be submitted
- **`action`**: is the URL to which the form is posted
- calling `cancel()` will prevent the submission
- **`submitter`**: is the `HTMLElement` that caused the form to be submitted
### Core concept: State management
There are some rules:
1. Browser are ***stateful*** while server is ***stateless***
2. No side-effects in `load()`
**NEVER DO THIS:**
```typescript
import { user } from '$lib/user';
/** @type {import('./$types').PageLoad} */
export async function load({ fetch }) {
const response = await fetch('/api/user');
// NEVER DO THIS!
user.set(await response.json());
}
```
Use **CONTEXT API** instead.
- `src/routes/+layout.svelte`
```typescript
<script>
import { setContext } from 'svelte';
import { writable } from 'svelte/store';
/** @type {import('./$types').LayoutData} */
export let data;
// Create a store and update it when necessary...
const user = writable();
$: user.set(data.user);
// ...and add it to the context for child components to access
setContext('user', user);
</script>
```
- `src/routes/+page.svelte`
```jsx
<script>
import { getContext } from 'svelte';
// Retrieve user store from context
const user = getContext('user');
</script>
<p>Welcome {$user.name}</p>
```
### Hooks
Hooks are **app-wide functions** which will be called in response to specific events. You can declare within 2 files:
- `src/hooks.servers.js`
- `src/hooks.client.js`