我正在用 Svelte 编写一个应用程序,并且有一个页面,其中有一个显示为表格的值列表。我希望能够编辑这些值,并且其中一个值由
select
元素选择,具有大约 100 个可能的值。
有一个按钮可以在表格显示和表单编辑模式之间切换显示(使用
select
元素)。
问题是,如果有多行创建
select
项目,每个项目都具有相同的 100 个可能的选项值,则 Svelte 需要很长时间才能生成表单(当大约有大约 100 个选项值时,大约需要一整秒才能切换到编辑模式) 15 个select
元素)。我在同一个应用程序上使用 React 时没有遇到这个问题(我将大部分应用程序从 React 移植到 Svelte),所以我想我做错了什么,或者有一种微妙的方法可以让事情变得更快。 .
这是一个小的工作示例来说明:
<script>
const pokemonList = ["bulbasaur", "ivysaur", "venusaur", "charmander", "charmeleon", "charizard", "squirtle", "wartortle", "blastoise", "caterpie", "metapod", "butterfree", "weedle", "kakuna", "beedrill", "pidgey", "pidgeotto", "pidgeot", "rattata", "raticate", "spearow", "fearow", "ekans", "arbok", "pikachu", "raichu", "sandshrew", "sandslash", "nidoran-f", "nidorina", "nidoqueen", "nidoran-m", "nidorino", "nidoking", "clefairy", "clefable", "vulpix", "ninetales", "jigglypuff", "wigglytuff", "zubat", "golbat", "oddish", "gloom", "vileplume", "paras", "parasect", "venonat", "venomoth", "diglett", "dugtrio", "meowth", "persian", "psyduck", "golduck", "mankey", "primeape", "growlithe", "arcanine", "poliwag", "poliwhirl", "poliwrath", "abra", "kadabra", "alakazam", "machop", "machoke", "machamp", "bellsprout", "weepinbell", "victreebel", "tentacool", "tentacruel", "geodude", "graveler", "golem", "ponyta", "rapidash", "slowpoke", "slowbro", "magnemite", "magneton", "farfetchd", "doduo", "dodrio", "seel", "dewgong", "grimer", "muk", "shellder", "cloyster", "gastly", "haunter", "gengar", "onix", "drowzee", "hypno", "krabby", "kingler", "voltorb", "electrode", "exeggcute", "exeggutor", "cubone", "marowak", "hitmonlee", "hitmonchan", "lickitung", "koffing", "weezing", "rhyhorn", "rhydon", "chansey", "tangela", "kangaskhan", "horsea", "seadra", "goldeen", "seaking", "staryu", "starmie", "mr-mime", "scyther", "jynx", "electabuzz", "magmar", "pinsir", "tauros", "magikarp", "gyarados", "lapras", "ditto", "eevee", "vaporeon", "jolteon", "flareon", "porygon", "omanyte", "omastar", "kabuto", "kabutops", "aerodactyl", "snorlax", "articuno", "zapdos", "moltres", "dratini", "dragonair", "dragonite", "mewtwo", "mew"];
let team = [
"bulbasaur",
"charmander",
"squirtle",
"caterpie",
"weedle",
"pidgey",
"rattata",
"spearow",
"ekans",
"pikachu",
"sandshrew",
"nidoran-f",
"nidoran-m",
"clefairy",
"vulpix",
"jigglypuff",
];
let isEditing = false;
</script>
<main>
{#if isEditing}
<form>
{#each team as teamMember}
<div>
<select bind:value={teamMember}>
{#each pokemonList as pokemonName}
<option value={pokemonName}>{pokemonName}</option>
{/each}
</select>
</div>
{/each}
</form>
{:else}
<table>
{#each team as teamMember}
<tr>
<td>{teamMember}</td>
</tr>
{/each}
</table>
{/if}
<button on:click={() => (isEditing = !isEditing)}>Toggle</button>
</main>
请注意,生成的所有选择都具有完全相同的选项列表,因此有没有办法生成一次元素并每次重用它?或者任何其他解决方案可以使其响应更快?在示例中,如果我只保留一行,那么切换到编辑模式几乎是瞬时的,但随着添加更多行,它需要的时间明显更长。
请注意,存在多种构建,并且开发 Svelte 构建的性能会比生产构建更差。此外,SSR 与 CSR 可能具有不同的性能特征,并且不同的浏览器以不同的方式处理大型 HTML 片段。
不过,这种场景至少可以通过两种方式进行优化:
对于后一种方法,您可以将选项的生成分离到单独的组件中,例如
<select bind:value={teamMember}>
<LazyOptions list={pokemonList} value={teamMember} />
</select>
<script>
export let list;
export let value;
let focused = false;
function checkFocus(node) {
const select = node.closest('select');
const update = () => focused = document.activeElement == select;
select.addEventListener('focus', update);
select.addEventListener('blur', update);
update();
return {
destroy() {
select.removeEventListener('focus', update);
select.removeEventListener('blur', update);
},
};
}
</script>
<!-- Makes sure that the currently selected value always exists.
Also provides an entry into the DOM to find the select. -->
<option style:display="none" use:checkFocus>{value}</option>
{#if focused}
{#each list as o}
<option>{o}</option>
{/each}
{/if}
还有其他方法可以做到这一点,例如传递焦点状态,但这种方式封装得更整齐。