组件的生命周期函数不是新鲜玩意,各个主流框架都有,它是一种 Hook 钩子函数,所谓钩子,实际上是一种回调,它提前声明,并将在适当的时机被自动调用。
每个组件从创建开始,到销毁结束这个过程,都有一个个 生命周期函数,可以让我们在生命周期的关键时刻执行我们所需要的操作
Tips:
- 在svelte中生命周期函数只能在组件初始化时编写,以便将回调绑定到组件的实例,不可以将生命周期函数放在例如异步的
setTimeout中。 - 如果你使用服务端渲染(SSR),除了
onDestroy,其余生命周期函数不会在 SSR 运行期间执行。
onMount
onMount是最为常见的一个生命周期函数。它在组件首次渲染到 DOM 后立即触发
<script>
// 使用svelte的生命周期函数之前,需要先引入
import { onMount } from 'svelte';
let photos = [];
// onMount是一个函数,接收一个callback
// 如果 onMount 回调返回一个函数,该函数将在组件被销毁时被调用
onMount(async () => {
const res = await fetch(`https://jsonplaceholder.typicode.com/photos?_limit=20`);
photos = await res.json();
});
</script>
onDestroy
要在销毁组件时做一些收尾或清除工作,请使用onDestroy。
<script>
import { onDestroy } from 'svelte';
let seconds = 0;
const interval = setInterval(() => seconds += 1, 1000);
// 清除定时器
onDestroy(() => clearInterval(interval));
</script>
<p>
The page has been open for
{seconds} {seconds === 1 ? 'second' : 'seconds'}
</p>
虽然在组件初始化期间调用生命周期函数很重要,但是从何处调用它们都无关紧要。
只要对应的生命周期函数可以在组件被初始化的时候执行到即可
<script>
// onDestory方法并没有被定义在组件中,而是被定义在了工具文件中
import { onInterval } from './utils.js';
let seconds = 0;
onInterval(() => seconds += 1, 1000);
</script>
<p>
The page has been open for
{seconds} {seconds === 1 ? 'second' : 'seconds'}
</p>
uitls.js
import { onDestroy } from 'svelte';
export function onInterval(callback, milliseconds) {
const interval = setInterval(callback, milliseconds);
onDestroy(() => clearInterval(interval));
}
beforeUpdate 和 afterUpdate
beforeUpdate 函数会在 DOM 更新之前执行,而相应的 afterUpdate 则在数据同步到 DOM 之后执行
beforeUpdate(() => {
// 因为svelte中的生命周期函数是在组件被初始化的时候就被调用的
// 初次调用是在onMount之前,因此需要判断div是否存在
autoscroll = div && (div.offsetHeight + div.scrollTop) > (div.scrollHeight - 20);
});
afterUpdate(() => {
if (autoscroll) div.scrollTo(0, div.scrollHeight);
});
tick
在 Svelte 中,当你更新DOM状态时,并不会立即执行,而是会进行等待,期间查看是否需要应用其他的更改,包括其他组件上DOM的更改。然后在下一个微任务的时候统一进行修改。这样做可以减少一些无用功,让浏览器更有效地批量处理这些事情。
特点:
- tick不一定是在首次初始化时调用,你可以随时调用它
- 返回值是一个 Promise
<script>
let bookName = 'js starter'
let bookNode
function upperBookName() {
bookName = bookName.toUpperCase()
// DOM的更新不是马上更新的,而是会有一段时间的延迟
console.log(bookNode.textContent) // => 书名: js starter
// 在输出后,svelte才完成了对于dom的更新操作
}
</script>
<span bind:this={bookNode}>书名:{bookName}</span>
<button on:click={upperBookName}>大写</button>
解决方法
// 将要做的时候,放置到定时器中,因为定时器是宏任务,宏任务的执行一定是在微任务之后的
let timer = setTimeout(() => console.log(bookNode.textContent), 0)
// 相应操作放置到afterUpdate中,此时DOM一定已经更新完毕
afterUpdate(() => console.log(bookNode.textContent))
bookName = bookName.toUpperCase()
await tick() // 等待DOM更新完毕以后才执行后续代码
console.log(bookNode.textContent)