Svelte 공부하기 - 6일차
포스트
취소

Svelte 공부하기 - 6일차

프론트엔드 프레임워크인 SvelteKit 에 대해 공부한다. Svelte 중에 부족한 부분을 보충한다. (6일차)

1. Svelte Events

1) Event forwarding

기본적인 이벤트 방향은 하위 컴포넌트에서 상위 컴포넌트로 전달된다. 여기서는 중간의 Outter 컴포넌트를 통과해서, 차상위 App 컴포넌트에서 처리되고 있다.

  • App.svelte : message 이벤트를 handleMessage 에 연결한다
    • on:message => handleMessage
    • handleMessage => alert(event.detail.text) 최종 수행
    • Outter.svelte : message 이벤트를 처리 안하고 전달만 한다.
      • Inner => on:message : message 이벤트를 처리할 handler 가 없다
      • Inner.svelte : 클릭 이벤트를 message 이벤트로 변환하여 전달
        • button on:click => sayHello
        • sayHello => dispatch(message)
1
2
3
4
5
6
7
8
9
<script>
  import Outer from './Outer.svelte';

  function handleMessage(event) {
    alert(event.detail.text);
  }
</script>

<Outer on:message={handleMessage}/>
1
2
3
4
5
<script>
  import Inner from './Inner.svelte';
</script>

<Inner on:message/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
  import { createEventDispatcher } from 'svelte';

  const dispatch = createEventDispatcher();

  function sayHello() {
    dispatch('message', {
      text: 'Hello!'
    });
  }
</script>

<button on:click={sayHello}>
  Click to say hello
</button>

비슷한 예제로 DOM 이벤트도 handler 가 없으면 상위로 전달된다.

1
2
3
4
5
6
7
8
9
<script>
  import CustomButton from './CustomButton.svelte';

  function handleClick() {
    alert('Button Clicked');
  }
</script>

<CustomButton on:click={handleClick}/>
1
2
3
<button on:click> <!-- 핸들러가 없다 (이벤트 통과) -->
  Click me
</button>

2. Lifecycle

1) onMount

  • Element 가 처음 DOM에 렌더링된 후에 실행된다.
    • 가장 자주 사용하게 되는 라이프사이클 Hook 함수
  • 수명 주기 함수는 SSR 중에 실행되지 않습니다. (onDestroy는 제외)
  • DOM에 마운트된 후 데이터를 느리게 가져오는 것을 피할 수 있습니다.
    • 서버 사이드 렌더링 할 때 fetch 타이밍을 맞추기 위함 (script 태그에 의한 데이터 로딩 방식보다 나은 방법)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script>
  import { onMount } from 'svelte';

  let photos = [];

  onMount(async () => {
    const res = await fetch(`/tutorial/api/album`);
    photos = await res.json();
  });
</script>

<div class="photos">
  {#each photos as photo}
    <figure>
      <img src={photo.thumbnailUrl} alt={photo.title}>
      <figcaption>{photo.title}</figcaption>
    </figure>
  {:else}
    <!-- this block renders when photos.length === 0 -->
    <p>loading...</p>
  {/each}
</div>

2) onDestroy

  • 구성요소가 소멸될 때 실행되는 라이프 사이클 Hook 함수
  • Timer 소멸시 setInterval 을 clear 처리하는 예제
    • 메모리 누수를 막을 수 있다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script>
  import Timer from './Timer.svelte';

  let open = true;
  let seconds = 0;

  const toggle = () => (open = !open);
  const handleTick = () => (seconds += 1);
</script>

<div>
  <button on:click={toggle}>{open ? 'Close' : 'Open'} Timer</button>
  <p>
    The Timer component has been open for
    {seconds} {seconds === 1 ? 'second' : 'seconds'}
  </p>
  {#if open}
  <Timer callback={handleTick} />
  {/if}
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
  import { onDestroy } from 'svelte';
  function onInterval(callback, milliseconds) {
    const interval = setInterval(callback, milliseconds);

    onDestroy(() => {
      clearInterval(interval);
    });
  }

  export let callback;
  export let interval = 1000;

  onInterval(callback, interval);
</script>

3) beforeUpdate and afterUpdate

  • Update 라이프사이클 Hook 함수
    • beforeUpdate 함수: DOM이 업데이트되기 직전에 작업을 수행
    • afterUpdate 함수: DOM이 데이터와 동기화되면 코드를 실행
  • 함께 사용하면 상태 기반 방식으로 달성하기 어려운 작업을 명령적으로 수행하는 데 유용합니다.
    • 사례) div 요소의 스크롤 위치 업데이트
      • div 요소를 bind:this 로 div 변수에 바인딩
      • update 이전에 채팅의 스크롤 위치를 확인하고, 이후에 스크롤링
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<script>
  import Eliza from 'elizabot';
  import { beforeUpdate, afterUpdate } from 'svelte';

  let div;
  let autoscroll;

  beforeUpdate(() => {
    autoscroll = div && (div.offsetHeight + div.scrollTop) > (div.scrollHeight - 20);
  });

  afterUpdate(() => {
    if (autoscroll) div.scrollTo(0, div.scrollHeight);
  });
</script>

<div class="chat">
  <h1>Eliza</h1>

  <div class="scrollable" bind:this={div}>
    {#each comments as comment}
      <article class={comment.author}>
        <span>{comment.text}</span>
      </article>
    {/each}
  </div>

  <input on:keydown={handleKeydown}>
</div>

3. Special elements

1) svelte:window

  • window 개체에 이벤트를 등록하는 경우에 사용
    • 예) 키 이벤트 인식 및 출력
  • 특정 window 속성에 바인딩 할 수도 있음
    • 연결 가능 속성들 : inner/outer - Width/Height, scrollX/Y, online
    • 예) 윈도우 스크롤 위치

    ```vue

    function handleKeydown(event) { key = event.key; code = event.code; }

    let y; </script>

<svelte:window bind:scrollY={y}/>

<svelte:window on:keydown={handleKeydown}/>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

## 4. [Skeleton & Flowbite](https://www.skeleton.dev/blog/skeleton-plus-flowbite)

Skeleton CSS for SvelteKit 에서 Flowbite 를 사용할 수 있게 되었다. 

- `npm install flowbite` 안해도 됨
- [Flowbite Svelte](https://github.com/themesberg/flowbite-svelte) 컴포넌트는 아니고, 순수 Flowbite 만 가능
  + 여러가지 [HTML Elements](https://flowbite.com/docs/components/) 들을 사용할 수 있다.

### 1) Flowbite 와 Skeleton 의 SvelteKit 프로젝트 생성

```console
$ npm create skeleton-app@latest skeleton-plus-flowbite
    - Choose "Yes, using Typescript syntax"
    - Select "No" for ESLint, Prettier, Playwright, Vitest, Inspector
    - Hit "Enter" to skip Tailwind plugins
    - Select the default "Skeleton" theme
    - Select the "Bare Bones" template

$ cd skeleton-plus-flowbite
$ npm run dev -- --open

블로그에서는 Flowbite Timeline 요소를 사용하는 방법을 설명하고 있다.

  • Timeline 과 TimelineItem 으로 나누고
  • Flowbite 예제 코드에는 색상이 하드코딩 되어 있어, 일부 제거/조정
  • 배지와 아이콘 스타일을 고치고
  • Typography 를 조정하고

The Timeline element presented on the Flowbite website. The Timeline element presented on the Flowbite website.

2) Skeleton Modal

기본적으로 Modal 컴포넌트가 HTML 영역에 선언되어 있어야 한다.

1
2
3
4
5
6
7
8
9
10
11
<script lang="ts">
  // Modals Utils
  import type { ModalSettings, ModalComponent } from '@skeletonlabs/skeleton';
  import { Modal, modalStore } from '@skeletonlabs/skeleton';

  function triggerAlert(): void {
    console.log('working!');
  }
</script>

<Modal />

Alert (기본)

1
2
3
4
5
6
7
8
9
10
11
12
13
<script lang="ts">
  function modalAlert(): void {
    const d: ModalSettings = {
      type: 'alert',
      title: 'Welcome to Skeleton',
      body: 'This simple alert modal examples uses <code>type: alert</code> and makes use of the optional <code>title</code>, <code>body</code>, and <code>image</code> values.',
      image: 'https://i.imgur.com/WOgTG96.gif'
    };
    modalStore.trigger(d);
  }
</script>

<button class="btn variant-ghost-surface" on:click={modalAlert}>Alert</button>

Form (Component 타입)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script lang="ts">
  import ModalForm from '$lib/Modal/ModalForm.svelte';

  function modalComponentForm(): void {
    const c: ModalComponent = { ref: ModalForm };
    const d: ModalSettings = {
      type: 'component',
      title: 'Custom Form Component',
      body: 'Complete the form below and then press submit.',
      component: c,
      response: (r: any) => {
        if (r) console.log('response:', r);
      },
      meta: {
        foo: 'bar',
        fizz: 'buzz',
        fn: triggerAlert
      }
    };
    modalStore.trigger(d);
  }  
</script>

<button class="btn variant-ghost-surface" on:click={modalComponentForm}>Form</button>

9. Review

  • Skeleton 이 유용하긴 한데, HTML 요소들이 다양하지 않아 불편했다.
    • Flowbite 가 필요하던 차에 업데이트가 되어 반갑다.
  • 일단 업로드 하고 나중에 필요한 부분을 업데이트 하자.

Full-Stack CRUD Application Example

SvelteKit Auth Tutorials with Firebase Auth

 
 

끝!   읽어주셔서 감사합니다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.