当 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} />

Full untruncated repository

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 语法的原因。