当 Svelte 中的道具发生变化时更新深层组件
Updating a deep component when prop changes in Svelte
我有一个二维数组,表示在 App.svelte
处初始化和处理的板。然后我将它传递给 Board
组件,该组件通过绑定每个单元格(因为它可以自行更改)将每个单元格呈现为 Cell
组件。
该应用程序模拟了 DFS 搜索算法,因此在某个点上该算法会在二维数组上运行并更新它,从而在 App.svelte
.
上注册
如何将更改注册到 Board
并因此注册到每个 Cell
组件?我需要强制 Board
重新渲染吗?
目前,当二维数组在 App
上发生变化时,整个板似乎恢复到其原始状态(尽管板包含特殊单元格)
截断 App.svelte
:
<script>
import Board from "./Components/Board.svelte";
import { beforeUpdate } from "svelte";
var board = [];
beforeUpdate(() => {
const rows = LIMIT;
const columns = LIMIT;
board = new Array(rows).fill(0).map(() => new Array(columns).fill(0));
});
function startSearch() { // Board changes in here
});
}
/**
* State indexes:
* 0 = white - Not, but can traverse
* 1 = blue - Starting point
* 2 = aquamarine - current cell
* 3 = orange - End point
* 4 = gray - wall
* 5 = black - finished traversing
*/
</script>
<div>
<Board {board} />
</div>
Board
:
<script>
export let board;
import Cell from "./Cell.svelte";
</script>
<div class="board">
{#each board as row, i}
<div class="row">
{#each board[i] as cell, j}
<Cell bind:cell={cell} {i} {j} />
{/each}
</div>
{/each}
</div>
Cell
:
<script>
export let cell = 0,
i,
j;
let cellDiv;
import { start, end, state, mouse } from "../stores.js";
import { afterUpdate } from "svelte";
let _state = state;
state.subscribe((val) => {
_state = val;
});
afterUpdate(() => {
//After updating check if my state changed
if ($start !== cellDiv && cell === 3) {
//Check if I'm still endpoint
cell = $end === cellDiv ? 3 : 0;
cellDiv.style.backgroundColor = cellToClass();
}
if ($end !== cellDiv && cell === 1) {
//Check if I'm still starting-point
cell = $start === cellDiv ? 1 : 0;
cellDiv.style.backgroundColor = cellToClass();
}
if (cell === 0 || cell === 5 || cell === 2) {
cellDiv.style.backgroundColor = cellToClass();
}
if (cell === 5 || cell === 2) {
console.log("x");
cellDiv.style.backgroundColor = cellToClass();
}
});
function clickHandler() {
if (_state === 1) {
//Start point
if ($end === cellDiv) $end = null;
$start = cellDiv;
cell = 1;
} else if (_state === 3) {
//End point
if ($start === cellDiv) $start = null;
$end = cellDiv;
cell = 3;
} else if (_state === 4) {
//Barriers
if ($start === cellDiv) $start = null;
if ($end === cellDiv) $end = null;
cell = 4;
}
cellDiv.style.backgroundColor = cellToClass();
}
function handleMouse() {
if ($mouse && _state === 4) clickHandler();
}
function cellToClass() {
switch (cell) {
case 0:
return "white";
case 1:
return "dodgerblue";
case 2:
return "mediumaquamarine";
case 3:
return "rgb(240, 63, 10)";
case 4:
return "gray";
case 5:
return "black";
default:
return "black";
}
}
</script>
<div
class="cell"
id="{i} {j}"
bind:this={cellDiv}
on:mousemove={handleMouse}
on:click={clickHandler} />
How can I register the change to Board and consequently to each of the Cell components? Do I need to force Board to re-render?
呃……嗯,是的。如果不触发重新渲染,您将看不到任何变化。不过,重新渲染在这里有点误导,就 Svelte 正在做的事情而言,“更新”可能会更准确。 运行ning Svelte 应用程序(即一旦编译)基本上知道从状态变化(让变量...)到需要更新/重新创建的相应 DOM 元素的直接路径,或需要重新计算的状态片段。 Svelte 中的“重新渲染”绝不意味着重新计算或重新创建所有内容,即使是在组件级别也是如此。
Currently when the 2D array changes on App the entire board seems to revert to it's original state (although the board contains special cells)
那是因为这段代码:
beforeUpdate(() => {
const rows = LIMIT;
const columns = LIMIT;
board = new Array(rows).fill(0).map(() => new Array(columns).fill(0));
});
每当 App
中的某些内容发生变化时,它 运行s。这不是你想要的。太过分了!
您可能只需要 运行 一次:
let board = new Array(rows).fill(0).map(() => new Array(columns).fill(0))
如果你打算让它可配置,你可以让它响应:
export let rows = LIMIT
export let columns = LIMIT
$: board = new Array(rows).fill(0).map(() => new Array(columns).fill(0))
您可能认为 onMount 可能是一个更好的事件,但我认为这也不是您想要的,因为在 [=14= 之前,您将对空板进行无用的渲染循环] 是 运行。可以说,这不是戏剧性的,但在这种情况下,立即启动变量会稍微好一些。
beforeUpdate
挂钩也是导致 App
组件中的商店自动订阅错误并阻止您使用 $start
语法的原因。
我有一个二维数组,表示在 App.svelte
处初始化和处理的板。然后我将它传递给 Board
组件,该组件通过绑定每个单元格(因为它可以自行更改)将每个单元格呈现为 Cell
组件。
该应用程序模拟了 DFS 搜索算法,因此在某个点上该算法会在二维数组上运行并更新它,从而在 App.svelte
.
如何将更改注册到 Board
并因此注册到每个 Cell
组件?我需要强制 Board
重新渲染吗?
目前,当二维数组在 App
上发生变化时,整个板似乎恢复到其原始状态(尽管板包含特殊单元格)
截断 App.svelte
:
<script>
import Board from "./Components/Board.svelte";
import { beforeUpdate } from "svelte";
var board = [];
beforeUpdate(() => {
const rows = LIMIT;
const columns = LIMIT;
board = new Array(rows).fill(0).map(() => new Array(columns).fill(0));
});
function startSearch() { // Board changes in here
});
}
/**
* State indexes:
* 0 = white - Not, but can traverse
* 1 = blue - Starting point
* 2 = aquamarine - current cell
* 3 = orange - End point
* 4 = gray - wall
* 5 = black - finished traversing
*/
</script>
<div>
<Board {board} />
</div>
Board
:
<script>
export let board;
import Cell from "./Cell.svelte";
</script>
<div class="board">
{#each board as row, i}
<div class="row">
{#each board[i] as cell, j}
<Cell bind:cell={cell} {i} {j} />
{/each}
</div>
{/each}
</div>
Cell
:
<script>
export let cell = 0,
i,
j;
let cellDiv;
import { start, end, state, mouse } from "../stores.js";
import { afterUpdate } from "svelte";
let _state = state;
state.subscribe((val) => {
_state = val;
});
afterUpdate(() => {
//After updating check if my state changed
if ($start !== cellDiv && cell === 3) {
//Check if I'm still endpoint
cell = $end === cellDiv ? 3 : 0;
cellDiv.style.backgroundColor = cellToClass();
}
if ($end !== cellDiv && cell === 1) {
//Check if I'm still starting-point
cell = $start === cellDiv ? 1 : 0;
cellDiv.style.backgroundColor = cellToClass();
}
if (cell === 0 || cell === 5 || cell === 2) {
cellDiv.style.backgroundColor = cellToClass();
}
if (cell === 5 || cell === 2) {
console.log("x");
cellDiv.style.backgroundColor = cellToClass();
}
});
function clickHandler() {
if (_state === 1) {
//Start point
if ($end === cellDiv) $end = null;
$start = cellDiv;
cell = 1;
} else if (_state === 3) {
//End point
if ($start === cellDiv) $start = null;
$end = cellDiv;
cell = 3;
} else if (_state === 4) {
//Barriers
if ($start === cellDiv) $start = null;
if ($end === cellDiv) $end = null;
cell = 4;
}
cellDiv.style.backgroundColor = cellToClass();
}
function handleMouse() {
if ($mouse && _state === 4) clickHandler();
}
function cellToClass() {
switch (cell) {
case 0:
return "white";
case 1:
return "dodgerblue";
case 2:
return "mediumaquamarine";
case 3:
return "rgb(240, 63, 10)";
case 4:
return "gray";
case 5:
return "black";
default:
return "black";
}
}
</script>
<div
class="cell"
id="{i} {j}"
bind:this={cellDiv}
on:mousemove={handleMouse}
on:click={clickHandler} />
How can I register the change to Board and consequently to each of the Cell components? Do I need to force Board to re-render?
呃……嗯,是的。如果不触发重新渲染,您将看不到任何变化。不过,重新渲染在这里有点误导,就 Svelte 正在做的事情而言,“更新”可能会更准确。 运行ning Svelte 应用程序(即一旦编译)基本上知道从状态变化(让变量...)到需要更新/重新创建的相应 DOM 元素的直接路径,或需要重新计算的状态片段。 Svelte 中的“重新渲染”绝不意味着重新计算或重新创建所有内容,即使是在组件级别也是如此。
Currently when the 2D array changes on App the entire board seems to revert to it's original state (although the board contains special cells)
那是因为这段代码:
beforeUpdate(() => {
const rows = LIMIT;
const columns = LIMIT;
board = new Array(rows).fill(0).map(() => new Array(columns).fill(0));
});
每当 App
中的某些内容发生变化时,它 运行s。这不是你想要的。太过分了!
您可能只需要 运行 一次:
let board = new Array(rows).fill(0).map(() => new Array(columns).fill(0))
如果你打算让它可配置,你可以让它响应:
export let rows = LIMIT
export let columns = LIMIT
$: board = new Array(rows).fill(0).map(() => new Array(columns).fill(0))
您可能认为 onMount 可能是一个更好的事件,但我认为这也不是您想要的,因为在 [=14= 之前,您将对空板进行无用的渲染循环] 是 运行。可以说,这不是戏剧性的,但在这种情况下,立即启动变量会稍微好一些。
beforeUpdate
挂钩也是导致 App
组件中的商店自动订阅错误并阻止您使用 $start
语法的原因。