MST

星途 面试题库

面试题:Svelte性能优化之常见渲染陷阱

在Svelte开发中,哪些常见的操作可能会导致不必要的组件重新渲染,从而引发性能瓶颈?请举例说明,并阐述如何避免这些情况。
17.4万 热度难度
前端开发Svelte

知识考点

AI 面试

面试题答案

一键面试

常见导致不必要组件重新渲染的操作及示例

  1. 频繁更新响应式变量
    • 示例:在组件内部有一个响应式变量count,并且在setInterval中频繁更新它,如下:
<script>
    let count = 0;
    setInterval(() => {
        count++;
    }, 100);
</script>

<div>{count}</div>
  • 原因:每次count更新,Svelte会重新渲染包含它的组件,即使DOM更新可能没有实际视觉变化,过于频繁的更新就会导致性能问题。
  1. 传递不必要的动态props
    • 示例:父组件向子组件传递一个动态变化但子组件实际并不依赖的props。比如父组件有一个不断变化的时间戳timestamp,传递给一个只显示静态文本的子组件:
<!-- 父组件 -->
<script>
    import Child from './Child.svelte';
    let timestamp = new Date().getTime();
    setInterval(() => {
        timestamp = new Date().getTime();
    }, 1000);
</script>

<Child {timestamp}/>

<!-- 子组件 -->
<script>
    export let timestamp;
</script>

<p>这是一个静态文本</p>
  • 原因:子组件虽然不依赖timestamp进行渲染,但由于props的变化,Svelte默认会重新渲染子组件。
  1. 在组件生命周期函数中进行大量计算
    • 示例:在onMount生命周期函数中进行复杂的数组排序等计算:
<script>
    import { onMount } from'svelte';
    let largeArray = Array.from({ length: 10000 }, (_, i) => i);
    let sortedArray;
    onMount(() => {
        sortedArray = largeArray.sort((a, b) => a - b);
    });
</script>
  • 原因:每次组件挂载(或者重新挂载)时,都会执行这些计算,可能会阻塞主线程,并且如果组件频繁重新渲染(比如因为父组件传递的props变化导致组件重新创建挂载),这些计算会重复执行,影响性能。

避免方法

  1. 控制响应式变量更新频率
    • 解决方案:可以使用requestAnimationFramelodashdebounce/throttle函数来控制更新频率。例如使用throttle
<script>
    import { throttle } from 'lodash';
    let count = 0;
    const updateCount = throttle(() => {
        count++;
    }, 500);
    setInterval(updateCount, 100);
</script>

<div>{count}</div>
  • 原理throttle函数会限制updateCount函数的调用频率,每500毫秒最多调用一次,减少了不必要的重新渲染。
  1. 使用bind:group$$props优化props传递
    • 解决方案:对于子组件不依赖的props,可以使用bind:group来减少不必要的重新渲染。例如,在上述父 - 子组件示例中,如果子组件确实不需要timestamp,可以这样优化:
<!-- 父组件 -->
<script>
    import Child from './Child.svelte';
    let timestamp = new Date().getTime();
    setInterval(() => {
        timestamp = new Date().getTime();
    }, 1000);
</script>

<Child bind:group={[/* 子组件真正需要的props */]}/>

<!-- 子组件 -->
<script>
    // 这里不接收timestamp
</script>

<p>这是一个静态文本</p>
  • 原理bind:group会告诉Svelte只有组内的props变化才会触发子组件重新渲染。另外,也可以在子组件中使用$$props来过滤不需要的props,只对需要的props做出响应。
  1. 将复杂计算移出组件生命周期
    • 解决方案:可以将复杂计算提前到组件外部进行,或者使用derived来缓存计算结果。例如,对于上述数组排序的示例:
<script>
    import { onMount } from'svelte';
    import { derived } from'svelte/store';
    let largeArray = Array.from({ length: 10000 }, (_, i) => i);
    let sortedArray = derived(largeArray, ($largeArray) => {
        return $largeArray.sort((a, b) => a - b);
    });
    onMount(() => {
        // 这里只需要处理已缓存的sortedArray
    });
</script>
  • 原理derived会缓存计算结果,只有当依赖的largeArray真正发生变化时,才会重新计算,避免了在组件生命周期中重复进行高成本计算。