컴포넌트가 처음 DOM에 렌더링 된 후 실행되는 함수입니다.
import { onMount } from 'svelte';
let photos = [];
onMount(async () => {
const res = await fetch(`/tutorial/api/album`);
photos = await res.json();
});
fetch
를 함수 안에 넣는 것을 추천합니다.
onDestroy
를 제외하고 생명 주기 함수는 SSR에서 작동하지 않기 때문에fetch를 onMout
함수 안에 넣게 되면 컴포넌트에 fetching 된 데이터가 느리게 로딩 되는 것을 피할 수 있습니다.
컴포넌트가 소멸될 때 onDestroy
를 사용합니다.
예를 들어 setInterval 함수를 컴포넌트가 초기화 될 때 추가하고, 더 이상 관련이 없을 때 정리할 수 있습니다. 이렇게 하면 메모리 누수를 방지할 수 있습니다.
import { onDestroy } from 'svelte';
let counter = 0;
const interval = setInterval(() => counter += 1, 1000);
onDestroy(() => clearInterval(interval));
</script>
컴포넌트를 초기화하는 동안 lifecycle 함수를 호출하는 것은 중요하지만 호출하는 위치는 중요하지 않습니다. 따라서 원하는 경우 interval 로직을 utils.js에 넣고 컴포넌트에 import하여 사용할 수 있습니다.
import { onDestroy } from 'svelte';
export function onInterval(callback, milliseconds) {
const interval = setInterval(callback, milliseconds);
onDestroy(() => {
clearInterval(interval);
});
}
import { onInterval } from './utils.js';
let counter = 0;
onInterval(() => counter += 1, 1000);
</script>
import Something from './Something.svelte'
let toggle = false
</script>
<button on:click={()=>{toggle = !toggle}}>
Toggle
</button>
{#if toggle}
<Something />
{/if}
import { onMount, onDestroy } from 'svelte'
onMount(()=>{
console.log('Mounted!')
return()=>{
console.log('Destroy in mount')
}
//onMount의 return문은 onDestroy와 동일한 역할을 한다
//onMount에서 return을 사용해 onDestroy를 구현할때는 비동기가 없는 상황에서만 가능하다
})
onDestroy(()=>{
console.log('destroy~')
})
</script>
<h1>Something...</h1>
onMount
는 something 컴포넌트가 나타난 직후 바로 실행되는 것을 볼 수 있으며 onDestroy
는 컴포넌트가 사라지기 직전에 실행되는 것을 볼 수 있습니다.
beforeUpdate
함수는 DOM이 업데이트 되기 직전, 반응성을 가지는 데이터가 갱신이 되기 직전에 작업이 수행됩니다.afterUpdate
함수는 DOM이 데이터와 동기화 된 후(업데이트 직후), 반응성을 가지는 데이터가 갱신이 된 직후에 작업이 수행됩니다.beforeUpdate
, afterUpdate
안에 있게 되면 무한 루프에 빠질 수 있기 때문에 내부에 넣지 않는 것이 좋습니다.이 두 함수를 함께 사용하면 요소의 스크롤 위치를 업데이트하는 동작과 같이 단순히 상태 기반으로 작업하기 어려운 작업을 수행하는데 유용합니다.
공식 문서 예제)
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);
});
const eliza = new Eliza();
let comments = [
{ author: 'eliza', text: eliza.getInitial() }
];
function handleKeydown(event) {
if (event.key === 'Enter') {
const text = event.target.value;
if (!text) return;
comments = comments.concat({
author: 'user',
text
});
event.target.value = '';
const reply = eliza.transform(text);
setTimeout(() => {
comments = comments.concat({
author: 'eliza',
text: '...',
placeholder: true
});
setTimeout(() => {
comments = comments.filter(comment => !comment.placeholder).concat({
author: 'eliza',
text: reply
});
}, 500 + Math.random() * 500);
}, 200 + Math.random() * 200);
}
}
</script>
beforeUpdate
가 컴포넌트가 마운트 되기 전에 먼저 실행되므로 div가 해당 속성을 읽어오기 전에 존재 여부를 체크해야 합니다.
간단한 예제)
import Something from './Something.svelte'
let toggle = false
</script>
<button on:click={()=>{toggle = !toggle}}>
Toggle
</button>
{#if toggle}
<Something />
{/if}
import { onMount, onDestroy, beforeUpdate, afterUpdate } from 'svelte'
let name = 'Something..'
let h1
function moreDot() {
name += '.'
}
beforeUpdate(()=>{
console.log('Before update!')
console.log(h1 && h1.innerText)
})
afterUpdate(()=>{
console.log('After update!')
console.log(h1 && h1.innerText)
})
onMount(()=>{
console.log('Mounted!')
h1 = document.querySelector('h1')
})
onDestroy(()=>{
console.log('Before destroy~')
})
</script>
<h1 on:click={moreDot}>Something...</h1>
위 예제를 실행해보면 .
이 추가되기 직전과 something 컴포넌트가 나타나기 직전 beforeUpdate 실행되고, .
이 추가된 직후와 something 컴포넌트가 나타난 후 beforeUpdate가 실행된다.
toggle 버튼을 눌렀을 때 실행 순서
Before Update → Mounted → After update
tick
함수는 promise를 반환하는 비동기 함수이며 데이터가 갱신 되고 나서 화면이 바뀌는 반응성을 가질 때까지 기다려줍니다.
컴포넌트가 처음 초기화 될 때만 아니라 언제든지 호출할 수 있다는 점에서 다른 lifecycle함수와 다릅니다.
pending 중인 상태가 변경되어 DOM에 반영되는 즉시 ( 또는 pending 되지 않은 상태가 변경되는 즉시 ) promise를 반환합니다.
svelte에서 컴포넌트의 상태를 업데이트하면 즉시 DOM에 업데이트 되지는 않습니다. 대신 브라우저가 불필요한 작업을 피하고, 더 효과적으로 일괄 처리하기 위하여 다른 컴포넌트를 포함해서 다른 변경 사항의 적용이 필요한지 확인하기 위하여 microtask 작업까지 기다립니다.
import { tick } from 'svelte';
await tick();
this.selectionStart = selectionStart;
this.selectionEnd = selectionEnd;
import { tick } from 'svelte';
let text = `Select some text and hit the tab key to toggle uppercase`;
async function handleKeydown(event) {
if (event.key !== 'Tab') return;
event.preventDefault();
const { selectionStart, selectionEnd, value } = this;
const selection = value.slice(selectionStart, selectionEnd);
const replacement = /[a-z]/.test(selection)
? selection.toUpperCase()
: selection.toLowerCase();
text = (
value.slice(0, selectionStart) +
replacement +
value.slice(selectionEnd)
);
await tick();
this.selectionStart = selectionStart;
this.selectionEnd = selectionEnd;
}
</script>
<style>
textarea {
width: 100%;
height: 200px;
}
</style>
<textarea value={text} on:keydown={handleKeydown}></textarea>
간단한 예제)
import {tick} from 'svelte'
let name = 'world'
async function handler(){
name = 'heejin'
await tick()
const h1 = document.querySelector('h1')
console.log(h1.innerText)
}
</script>
<h1 on:click={handler}>Hello {name}! </h1>
tick()
을 사용하지 않았을 경우 handler 함수 안에서 name의 값을 할당해주었어도 함수 내에서 바로 변경되지 않기 때문에 즉각적으로 변경되지 않습니다.. 그러나 tick
을 사용하게 되면 name을 할당된 값으로 변경될 때까지 기다려 준 후 다음 코드를 수행하게 됩니다.
tick
은 promise를 반환하는 비동기 함수이기 때문에 async await과 함께 사용합니다.
import { lifecycle, delayRender } from './lifecycle.js'
let done = delayRender()
lifecycle()
</script>
{#if $done}
<h1>Hello Lifecycle!</h1>
{/if}
import { onMount, onDestroy, beforeUpdate, afterUpdate } from 'svelte'
import { writable } from 'svelte/store'
export function lifecycle(){
beforeUpdate(()=>{
console.log('Before update!')
})
afterUpdate(()=>{
console.log('After update!')
})
onMount(()=>{
console.log('Mounted!')
})
onDestroy(()=>{
console.log('Before destroy!')
})
}
export function delayRender(delay = 3000){ //인수가 없으면 3000으로 세팅
let render = writable(false)
onMount(()=>{
setTimeout(() => {
console.log(render)
render.set(true)
},delay)
})
}
lifecylce을 다른 js파일로 분리하여 import 해서 사용할 수 있습니다.