如何在 Svelte 中的道具更改时刷新 DOM?
How to refresh DOM when props change in Svelte?
我有一个 grid-like 结构,其指针 object 来自 parent。现在通过一些操作,指针正在更新,但更改没有反映在 child 组件中,DOM 也没有更新。
Parent:
<script>
import Boxes from "./Boxes.svelte";
let boxes = [
["", "", "", "", "", "", "", ""],
["", "", "", "", "", "", "", ""],
["", "", "", "", "", "", "", ""],
["", "", "", "", "", "", "", ""],
["", "", "", "", "", "", "", ""],
["", "", "", "", "", "", "", ""],
["", "", "", "", "", "", "", ""],
["", "", "", "", "", "", "", ""]
];
let activeBox = {
x: 0,
y: 0
};
function handleKeydown(keyEvent) {
let i = activeBox.x;
let j = activeBox.y;
const width = boxes[i].length,
height = boxes.length,
left = 37,
up = 38,
right = 39,
down = 40,
tab = 9,
backspace = 8;
// Loop around single row with right and left arrows
if (keyEvent.keyCode == right) {
activeBox.x = activeBox.x + 1;
if(activeBox.x === boxes[i].length) activeBox.x = 0;
return;
}
$: console.log("^^^ activeBox &&", activeBox);
</script>
<style>
main {
display: flex;
align-items: center;
justify-content: center;
}
@media (min-width: 640px) {
main {
max-width: none;
}
}
</style>
<svelte:window on:keydown="{handleKeydown}" />
<main>
<Boxes boxes={boxes} activeBox={activeBox} />
</main>
child 组件正在使用 activeBox 变量呈现所选框。但这似乎不起作用,因为当第一个渲染器完美运行时,框不会随之更新。
Child:
<script>
export let boxes;
export let activeBox;
$: console.log("^^^ active Box updated");
function getSelectedClass(i, j) {
if (activeBox.x === j && activeBox.y === i) {
return "selected";
}
return "";
}
</script>
<style>
.grid-container {
display: grid;
grid-template-columns: auto auto auto auto auto auto auto auto;
background-color: #2196f3;
padding: 0px;
}
.grid-item {
background-color: rgba(255, 255, 255, 0.8);
border: 1px solid rgba(0, 0, 0, 0.8);
width: 40px;
height: 40px;
padding: 20px;
font-size: 30px;
text-align: center;
}
.selected {
border: 1px solid red;
}
</style>
<main>
<div class="grid-container">
{#each boxes as row, i}
{#each row as column, j}
<div class=" grid-item {getSelectedClass(i, j)}">{boxes[i][j]}</div>
{/each}
{/each}
</div>
</main>
我真的需要一些洞察力,因为我还没有正确掌握 Svelte 的概念。任何帮助将不胜感激。
所以使用 Svelte 时需要注意的几点:
- 不要控制台登录响应式声明 $: console.log() 将在组件创建后立即 运行 但没有其他意义或用途。
- 当你导出的变量与父组件中的变量名相同时,只需在标签中使用{varName},不需要varName={varName}
就更新子组件中的 activeBox 而言,它正在更新,但在您的代码中,它无法知道它需要 运行 再次选择 class 的函数。该函数仅 运行 一次,这就是它在初始渲染上起作用的原因。
保持 class 更新的一种方法是在子项的条件 class 语句中直接使用三元运算符:
<div class=" grid-item {activeBox.x === j && activeBox.y === i? 'selected' : ''}">{boxes[i][j]}</div>
然后删除该函数。喜欢this
使用您的代码,activeBox
的值实际上已传播给子项,但您看不到它,因为子项中没有使用它的反应代码。
当它所依赖的变量值发生变化时,会重新执行和评估反应式代码,但仅限于立即出现在反应式代码中的变量。
因此,在这段代码中:
$: console.log("^^^ active Box updated");
……完全没有变数。所以这个块最初会运行一次,然后再也不会了。
这个:
{#each boxes as row, i}
...
{/each}
只要 boxes
变量发生变化,就会重新计算。
并且在这个:
<div class=" grid-item {getSelectedClass(i, j)}">...</div>
...反应值 {getSelectedClass(i, j)}
仅 "sees" 变量 getSelectedClass
、i
和 j
。 i
和 j
是局部变量,因此 Svelte 不会跟踪它们。 getSelectedClass
是 一个顶级变量,所以它会被跟踪并且如果它的值改变了块重新评估(即 getSelectedClass = 'foo'
),但从来没有这样这里。
所以...如何解决?
只需在响应式表达式中添加 activeBox
即可让编译器在其值更改时重新计算它(即使它实际上并未在函数中使用)。因此,这将按原样修复您的代码:
<div class=" grid-item {getSelectedClass(i, j, activeBox)}">...</div>
另一个选择是使用另一个基于实际包含 activeBox
变量的表达式的构造:
<div class="grid-item" class:selected={activeBox.x === j && activeBox.y === i}>{boxes[i][j]}</div>
这是一个中间立场:
<script>
// NOTE this block contains activeBox variable, so isSelected is recreated when
// activeBox changes
$: isSelected = (i, j) => activeBox.x === j && activeBox.y === i
</script>
<main>
<div class="grid-container">
{#each boxes as row, i}
{#each row as column, j}
<div class="grid-item" class:selected={isSelected(i, j)}>{boxes[i][j]}</div>
{/each}
{/each}
</div>
</main>
我有一个 grid-like 结构,其指针 object 来自 parent。现在通过一些操作,指针正在更新,但更改没有反映在 child 组件中,DOM 也没有更新。
Parent:
<script>
import Boxes from "./Boxes.svelte";
let boxes = [
["", "", "", "", "", "", "", ""],
["", "", "", "", "", "", "", ""],
["", "", "", "", "", "", "", ""],
["", "", "", "", "", "", "", ""],
["", "", "", "", "", "", "", ""],
["", "", "", "", "", "", "", ""],
["", "", "", "", "", "", "", ""],
["", "", "", "", "", "", "", ""]
];
let activeBox = {
x: 0,
y: 0
};
function handleKeydown(keyEvent) {
let i = activeBox.x;
let j = activeBox.y;
const width = boxes[i].length,
height = boxes.length,
left = 37,
up = 38,
right = 39,
down = 40,
tab = 9,
backspace = 8;
// Loop around single row with right and left arrows
if (keyEvent.keyCode == right) {
activeBox.x = activeBox.x + 1;
if(activeBox.x === boxes[i].length) activeBox.x = 0;
return;
}
$: console.log("^^^ activeBox &&", activeBox);
</script>
<style>
main {
display: flex;
align-items: center;
justify-content: center;
}
@media (min-width: 640px) {
main {
max-width: none;
}
}
</style>
<svelte:window on:keydown="{handleKeydown}" />
<main>
<Boxes boxes={boxes} activeBox={activeBox} />
</main>
child 组件正在使用 activeBox 变量呈现所选框。但这似乎不起作用,因为当第一个渲染器完美运行时,框不会随之更新。
Child:
<script>
export let boxes;
export let activeBox;
$: console.log("^^^ active Box updated");
function getSelectedClass(i, j) {
if (activeBox.x === j && activeBox.y === i) {
return "selected";
}
return "";
}
</script>
<style>
.grid-container {
display: grid;
grid-template-columns: auto auto auto auto auto auto auto auto;
background-color: #2196f3;
padding: 0px;
}
.grid-item {
background-color: rgba(255, 255, 255, 0.8);
border: 1px solid rgba(0, 0, 0, 0.8);
width: 40px;
height: 40px;
padding: 20px;
font-size: 30px;
text-align: center;
}
.selected {
border: 1px solid red;
}
</style>
<main>
<div class="grid-container">
{#each boxes as row, i}
{#each row as column, j}
<div class=" grid-item {getSelectedClass(i, j)}">{boxes[i][j]}</div>
{/each}
{/each}
</div>
</main>
我真的需要一些洞察力,因为我还没有正确掌握 Svelte 的概念。任何帮助将不胜感激。
所以使用 Svelte 时需要注意的几点:
- 不要控制台登录响应式声明 $: console.log() 将在组件创建后立即 运行 但没有其他意义或用途。
- 当你导出的变量与父组件中的变量名相同时,只需在标签中使用{varName},不需要varName={varName}
就更新子组件中的 activeBox 而言,它正在更新,但在您的代码中,它无法知道它需要 运行 再次选择 class 的函数。该函数仅 运行 一次,这就是它在初始渲染上起作用的原因。
保持 class 更新的一种方法是在子项的条件 class 语句中直接使用三元运算符:
<div class=" grid-item {activeBox.x === j && activeBox.y === i? 'selected' : ''}">{boxes[i][j]}</div>
然后删除该函数。喜欢this
使用您的代码,activeBox
的值实际上已传播给子项,但您看不到它,因为子项中没有使用它的反应代码。
当它所依赖的变量值发生变化时,会重新执行和评估反应式代码,但仅限于立即出现在反应式代码中的变量。
因此,在这段代码中:
$: console.log("^^^ active Box updated");
……完全没有变数。所以这个块最初会运行一次,然后再也不会了。
这个:
{#each boxes as row, i}
...
{/each}
只要 boxes
变量发生变化,就会重新计算。
并且在这个:
<div class=" grid-item {getSelectedClass(i, j)}">...</div>
...反应值 {getSelectedClass(i, j)}
仅 "sees" 变量 getSelectedClass
、i
和 j
。 i
和 j
是局部变量,因此 Svelte 不会跟踪它们。 getSelectedClass
是 一个顶级变量,所以它会被跟踪并且如果它的值改变了块重新评估(即 getSelectedClass = 'foo'
),但从来没有这样这里。
所以...如何解决?
只需在响应式表达式中添加 activeBox
即可让编译器在其值更改时重新计算它(即使它实际上并未在函数中使用)。因此,这将按原样修复您的代码:
<div class=" grid-item {getSelectedClass(i, j, activeBox)}">...</div>
另一个选择是使用另一个基于实际包含 activeBox
变量的表达式的构造:
<div class="grid-item" class:selected={activeBox.x === j && activeBox.y === i}>{boxes[i][j]}</div>
这是一个中间立场:
<script>
// NOTE this block contains activeBox variable, so isSelected is recreated when
// activeBox changes
$: isSelected = (i, j) => activeBox.x === j && activeBox.y === i
</script>
<main>
<div class="grid-container">
{#each boxes as row, i}
{#each row as column, j}
<div class="grid-item" class:selected={isSelected(i, j)}>{boxes[i][j]}</div>
{/each}
{/each}
</div>
</main>