[ props ]
<script>
import LayerdButton from './LayerdButton.svelte'
let content = ''
let isClicked = false
const onHandlerClick = () => isClicked = !isClicked
$: content = isClicked ? "to close" : "to open"
</script>
<LayerdButton {onHandlerClick} {content} />
<script>
export let onHandlerClick;
export let content;
</script>
<button on:click={onHandlerClick}>{content}</button>
Button
-> LayerdButton
컴포넌트로 변수와 함수를 props
로 이동
반응성 구문
인 $:
를 통해 반응에 대한 변화를 지정해주면 연관된 값
도 변경된다
- (장점)
svelte
에서는 반응성 구문 $:
을 통해서 함수
나 변수
에 자유롭게 반응 관계
를 만들 수 있다
[ bind ]
- svelte 장점 중 하나가 바로
bind:키워드
를 통해서 양방향 바인딩
을 쉽게 할 수 있는 것
bind:this
: dom element와 양방향 바인딩
bind:value
: input 태그에서 value와 양방향 바인딩
bind:checked
: 체크박스에서 배열 state와 양방향 바인딩
bind:group
: 라디오 타입에서 배열 state와 양방향 바인딩
bind:this
document.querySelector()
대신, bind:this
로 바로 특정 dom에 접근 및 사용 가능
- REPL 예시
<script>
import { onMount } from 'svelte';
let canvasElement;
onMount(() => {
const ctx = canvasElement.getContext('2d');
drawStuff(ctx);
});
</script>
<canvas bind:this={canvasElement}></canvas>
[ 반응성 ]
.push()
/ .splice()
같은 메소드 사용은 반응성
을 갱신할 수 없다
- 물론 사용된 원본 값은 바뀌겠지만, svelte는 대입문 = 을 통해 반응성을 반영하기 때문에 리렌더링 X
- 특정 object에서 여러개 변경이 있을 때 하나라도 = 로 재할당되면 전체적으로 반응성이 반영됨
-> 반응성 코드로 바뀌면서 객체 전체를 할당하기 때문
- REPL 예시
- 반응성 구문인
$:
은 반응성을 계측하는 것일 뿐, 즉각 반영은 아님
- 조금의
시간 차이
가 있어서 연이은 작업이 있을 때 오류가 생길 수 있다
- 그래서,
svelte
의 lifecycle
중 하나인, tick
을 이용해서 반응성 반영
을 보장
받을 수 있도록 함께 사용
- REPL 예제
[ if ~ else ]
{#if count > 3}
<div>count > 3</div>
{:else if count === 3}
<div>count === 3</div>
{:else}
<div>count < 3</div>
{/if}
[ for ]
- 일반 템플릿 엔진의 반복문과 크게 다르지 않으나, svelte
형식이 다양하다는 것
이 차이점
반복 데이터
들에 key값을 할당
해서, 변경된 항목만 효율적으로 변경하도록 해줘야 한다
()
괄호를 이용해서 키(key)
를 지정
- 그 외
다양한 형식
의 반복문이 존재
- REPL 예시
<script>
let fruits = [
{ id: 1, name: 'Apple' },
{ id: 2, name: 'Banana' },
{ id: 3, name: 'Cherry' },
{ id: 4, name: 'Apple' },
{ id: 5, name: 'Orange' }
]
</script>
<section>
<h2>아이템 고유화(key)</h2>
<!-- {#each 배열 as 속성, 순서 (키)} {/each} -->
{#each fruits as fruit, index (fruit.id)}
<div>{index} / {fruit.name}</div>
{/each}
</section>
[ Event Dispatcher ]
- 자식에서 부모로 데이터(이벤트)를 전달하는 방법
on:이벤트 이름
으로 잡아서 처리할 수 있음
- 이벤트의 핸들러를 명시하지 않으면
Event Forwarding
이 수행됨 (그냥 지나감)
- REPL 예시
<script>
import { createEventDispatcher } from 'svelte'
const dispatch = createEventDispatcher()
const sendData = '나는 데이터!'
dispatch('childSendEvent', sendData)
</script>
<script>
import Child from './Child.svelte'
</script>
<Child on:childSendEvent={event => {
console.log(event.detail)
}} />
[ writable Store (읽기/쓰기 스토어) ]
- store 종류
writable store
- 값을 읽고 / 저장할 때 사용
- 기본 제공 api :
set()
/ update()
/ subscribe()
writable(값, 콜백)
형태를 취하며, 자동이든 수동이든 구독할 때 수행될 콜백 지정 가능
readable stroe
- 존재하는 store로 새로운 store를 만들 때 :
Derived store
사용
- Store docs
- 사용 방법
- 수동 구독 : 직접
subscribe
하는 코드와 자원을 해제하는 코드를 직접 작성
- 자동 구독 : 변수 앞에
$
를 붙여서 자동으로 구독과 해제를 하도록 해주는 설정
- 구독 없이 값 읽기 :
svelte/store
에 있는 get
은 구독하지 않고 store의 객체
만 얻는 방법이다
import { writable } from 'svelte/store';
export const count = writable(0);
<script>
import { count } from './stores.js';
import Incrementer from './Incrementer.svelte';
import Decrementer from './Decrementer.svelte';
import Resetter from './Resetter.svelte';
</script>
<h1>The count is {$count}</h1>
<!-- Incrementer.svelte -->
<script>
import { count } from './stores.js';
function increment() {
count.update(n => n + 1);
}
</script>
<button on:click={increment}>
+
</button>
[ Derived Store (계산된 스토어) ]
- 쓰기가능(writable) 하거나 읽기 전용(readable) 스토어를 통해 새롭게 계산한 값을 가지는 스토어를 생성
- 사용 패턴이 많아 헷갈릴 수 있지만, derived 매개변수 3개와 콜백이 가지는 매개변수를 이해하면 쉽다
- REPL 예시
derived(스토어, 콜백)
derived(스토어, 콜백, 초깃값)
derived([스토어1, 스토어2], 콜백)
derived([스토어1, 스토어2], 콜백, 초깃값)
function ($스토어) {
return 계산된_값
}
function ([$스토어1, $스토어2]) {
return 계산된_값
}
function ($스토어, set) {
set(계산된_값)
return 구독이_모두_취소되면_실행할_함수
}
[ use 지시어 ]
- use 지시어를 통해 연결된 dom이 생성될 때 호출할 함수를 지정할 수 있다
- DOMContentLoad 나 load 와 같은 리스너와 비슷하지만 더 다양한 기능 제공
- 해당 요소(dom)를 사용하는 플러그인(모듈)을 제작할 때 유용
<요소 use:함수이름></요소>
<요소 use:함수이름={인수}></요소>
function 함수이름(요소, 인수) {
return {
update(인수) {},
destroy() {}
}
}
[ svelte의 lifecycle ]
- 5가지 기본적인 lifecycle을 가진다
onMount
: 컴포넌트가 돔에 마운트 되면 실행
onDestroy
: 컴포넌트가 해제된 후 실행
beforeUpdate
: 컴포넌트가 마운트 되기 전 실행
afterUpdate
: 컴포넌트가 마운트 된 후 실행
tick
: 컴포넌트 변경이 완료되면 실행
- 전체적인 실행 순서
beforeUpdate
-> onMount
-> afterUpdate
-> onDestroy
- 부모와 자식 사이의 생명주기
- 부모의
beforeUpdate
이후 onMount
사이에 자식의 모든 라이프사이클
이 수행
import { onMount, onDestroy, beforeUpdate, afterUpdate, tick } from 'svelte'
...
onMount(async() => {
console.log('App onMount')
})
onDestroy(async() => {
console.log('App onDestroy')
})
beforeUpdate(async() => {
console.log('App beforeUpdate')
})
afterUpdate(async() => {
console.log('App afterUpdate')
})
[ head / body ]
- 현재 문서의 head값인
document.head
를 통해 정보 요소를 삽입해준다
document.body
에 특정 이벤트를 추가할 수 있다
<svelte:head>
<script src="https://js.stripe.com/v3/" on:load={stripeLoaded}{></script>
<svelte:head>
<svelte:body on:mousemove={e => console.log(e.clientX, e.clientY)} />
[ $$props
/ $$restProps
]
$$props
: 컴포넌트가 전달받는 모든 Props의 정보를 가진 객체
$$restProps
: 명시한 props 제외한 나머지 다룰 수 있는 객체
[ key 블록 ]
- 특정 state에 따른, 컴포넌트 초기화 및 리렌더링 수행
<script>
import Count from './Count.svelte'
let reset = false
</script>
{#key reset}
<Count />
{/key}
<button on:click={() => reset = !reset}>
Reset!
</button>
[ 비동기 블록 ]
<input bind:value={title} />
<button on:click={() => promise = searchMovies()}>검색!</button>
{#await promise}
<!-- pending(대기) -->
<p style="color: royalblue;">loading...</p>
{:then movies}
<!-- fulfilled(이행) -->
<ul>
{#each movies as movie}
<li>{movie.Title}</li>
{:else}
<li>검색된 결과가 없어요...</li>
{/each}
</ul>
{:catch err}
<!-- rejected(거부) -->
<p style="color: red;">{err.message}</p>
{/await}
- (추가) fetch / axios 를 통한 api call
[ 다중 이벤트 핸들링 / 이벤트 수식어 ]
- 한 요소에 같은 이벤트를 여러개 연결할 수 있음
- svelte에서는 DOM 이벤트를 위한
이벤트 수식어
를 제공
-> |
기호를 이용해서 체이닝
가능
- 자세한 예제
<a
href="#"
on:click|preventDefault={() => console.log('link!')}>
Internal link..
</a>
<div on:click|preventDefault|capture|self|once={() => console.log('!')}>
Chaining..
</div>
[ svelte:component ]
- 컴포넌트를 동적으로 렌더링할 때 사용
- this 속성에 컴포너트 객체를 연결
- 컴포넌트 이름과 값을 매핑한다면 쉽게 선택한 특정 컴포넌트를 렌더링 할 수 있다
<svelte : component />
<script>
import Heropy from './Heropy.svelte'
import Neo from './Neo.svelte'
import Anderson from './Anderson.svelte'
let components = [
{ name: 'Heropy', comp: Heropy },
{ name: 'Neo', comp: Neo },
{ name: 'Anderson', comp: Anderson }
]
let index = 2
let selected = components[index - 1].comp
</script>
{#each components as {name, comp}, i (name)}
<label>
<input
type="radio"
value={comp}
bind:group={selected}
on:change={() => index = i + 1} />
{name}
</label>
{/each}
<svelte:component
this={selected}
{index} />
[ svelte:options - immutable ]
- 객체나 배열같은 것들은 일부분만 변경되어도, svelte 특성상 전체가 재할당 되어서 불필요한 재 랜더링이 발생할 수 있다
- options를 통해서 불변성 선언을 하면 실제로 값이 변경된 부분만 재 렌더링을 수행하게 할 수 있다
- 컴포넌트가 전달받은 props의 데이터 불변성을 선언
- REPL 예시
<svelte:options immutable={true} />
<svelte:options immutable />
[ svelte:options - accessors ]
- 외부에서 컴포넌트의 데이터 혹은 함수에 접근을 허용할 때 사용하는 기능
- 허용할 데이터가 함수에
export
를 한 뒤, <svelte:options accessor />
선언
- 이 설정 후, 컴포넌트에 bind:this를 통해서 특정 변수를 지정하면, 함수나 변수에 접근할 수 있다
- REPL 예시
<svelte:options accessors={true} />
<svelte:options accessors />