面试题答案
一键面试可能遇到的性能问题
- 过多的重新渲染:
- 原因:在Svelte中,当
writable
状态发生变化时,依赖它的组件会重新渲染。如果频繁更新writable
状态,可能导致不必要的组件重新渲染,特别是在复杂应用中,一些组件可能并不需要每次状态变化都重新渲染。例如:
<script> import { writable } from'svelte/store'; const count = writable(0); </script> <div> <p>{$count}</p> <button on:click={() => $count++}>Increment</button> <!-- 每次点击按钮,包含<p>的整个组件都会重新渲染 --> </div>
- 对于
derived
:如果derived
依赖的writable
状态频繁变化,并且derived
在多个地方被使用,那么每次依赖的writable
变化时,所有依赖该derived
的组件都会重新渲染。例如:
<script> import { writable, derived } from'svelte/store'; const count = writable(0); const doubleCount = derived(count, $count => $count * 2); </script> <div> <p>{$doubleCount}</p> <button on:click={() => $count++}>Increment</button> <!-- 每次点击按钮,依赖doubleCount的组件都会重新渲染 --> </div>
- 原因:在Svelte中,当
- 计算开销:
- 对于
derived
:derived
状态是基于其他状态计算得出的。如果计算逻辑复杂,频繁更新依赖的writable
状态会导致频繁执行复杂的计算逻辑,消耗性能。例如:
<script> import { writable, derived } from'svelte/store'; const numbers = writable([1, 2, 3]); const sumOfSquares = derived(numbers, $numbers => { return $numbers.reduce((acc, num) => acc + num * num, 0); }); </script> <div> <p>{$sumOfSquares}</p> <button on:click={() => $numbers.push(4)}>Add number</button> <!-- 每次添加数字,sumOfSquares的复杂计算逻辑都会重新执行 --> </div>
- 对于
优化方法
- 减少不必要的重新渲染:
- 使用
$:
声明局部变量:如果一个组件只需要状态的某个衍生值,而不需要整个状态对象,可以使用$:
声明局部变量,这样只有这个局部变量相关的代码会在状态变化时重新执行,而不是整个组件重新渲染。例如:
<script> import { writable } from'svelte/store'; const count = writable(0); $: doubled = $count * 2; </script> <div> <p>{doubled}</p> <button on:click={() => $count++}>Increment</button> <!-- 这里只有doubled相关代码会在count变化时重新执行,而不是整个组件重新渲染 --> </div>
- 组件拆分:将依赖不同状态的部分拆分成不同的组件。例如:
<!-- Parent.svelte --> <script> import { writable } from'svelte/store'; import Child1 from './Child1.svelte'; import Child2 from './Child2.svelte'; const count = writable(0); const message = writable('Hello'); </script> <div> <Child1 {count}/> <Child2 {message}/> </div>
<!-- Child1.svelte --> <script> export let count; </script> <p>{$count}</p>
这样,<!-- Child2.svelte --> <script> export let message; </script> <p>{$message}</p>
count
状态变化不会导致Child2
组件重新渲染,message
状态变化也不会导致Child1
组件重新渲染。 - 使用
- 优化
derived
计算:- 缓存计算结果:可以使用一个变量来缓存
derived
的计算结果,只有当依赖的状态发生有意义的变化时才重新计算。例如:
<script> import { writable, derived } from'svelte/store'; const numbers = writable([1, 2, 3]); let cachedSumOfSquares; const sumOfSquares = derived(numbers, ($numbers, set) => { if (cachedSumOfSquares === undefined || $numbers.length!== cachedNumbers.length) { const newSum = $numbers.reduce((acc, num) => acc + num * num, 0); cachedSumOfSquares = newSum; cachedNumbers = $numbers.slice(); set(newSum); } else { set(cachedSumOfSquares); } }); </script> <div> <p>{$sumOfSquares}</p> <button on:click={() => $numbers.push(4)}>Add number</button> <!-- 只有在数字数组长度变化时才重新计算sumOfSquares --> </div>
- 防抖或节流:如果状态更新过于频繁,可以对更新操作进行防抖或节流。例如,使用
lodash
的debounce
函数:
<script> import { writable } from'svelte/store'; import { debounce } from 'lodash'; const count = writable(0); const updateCountDebounced = debounce(() => $count++, 300); </script> <div> <p>{$count}</p> <button on:click={updateCountDebounced}>Increment</button> <!-- 点击按钮后,300毫秒内多次点击只会触发一次count更新 --> </div>
- 缓存计算结果:可以使用一个变量来缓存