Svelte系列 --- 生命周期

1,419 阅读2分钟

组件的生命周期函数不是新鲜玩意,各个主流框架都有,它是一种 Hook 钩子函数,所谓钩子,实际上是一种回调,它提前声明,并将在适当的时机被自动调用。

每个组件从创建开始,到销毁结束这个过程,都有一个个 生命周期函数,可以让我们在生命周期的关键时刻执行我们所需要的操作

Tips:

  1. 在svelte中生命周期函数只能在组件初始化时编写,以便将回调绑定到组件的实例,不可以将生命周期函数放在例如异步的 setTimeout 中。
  2. 如果你使用服务端渲染(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的更改。然后在下一个微任务的时候统一进行修改。这样做可以减少一些无用功,让浏览器更有效地批量处理这些事情。

特点:

  1. tick不一定是在首次初始化时调用,你可以随时调用它
  2. 返回值是一个 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)