如何在 Svelte 中拼接后更新数组?

Posted

技术标签:

【中文标题】如何在 Svelte 中拼接后更新数组?【英文标题】:How to update an array after splice in Svelte? 【发布时间】:2020-03-16 17:54:12 【问题描述】:

我正在学习 Svelte,并阅读了需要重新分配数组才能让组件或页面更新它的文档。为此,他们设计了一个更惯用的解决方案。而不是写:

messages.push('hello');
messages = messages;

你可以改写:

messages = [...messages, 'hello'];

好吧,有道理。但是文档说:

您可以使用类似的模式来替换 pop、shift、unshift 和 splice。

但是怎么做呢?我看不到如何从数组中删除项。更重要的是,我怎样才能更惯用地写以下内容?

messages.splice(messages.indexOf('hello'), 1);
messages = messages;

【问题讨论】:

Svelte 的反应是由分配触发的。因此推送、弹出、切片等不起作用。请在分配值时使用“=”。 【参考方案1】:

你可以例如使用filter数组方法创建一个没有'hello'元素的新数组:

messages = messages.filter(m => m !== 'hello');

【讨论】:

哦,这是一个优雅的解决方案!我实际上应该更好地学习所有 JS 数组函数以了解可能的情况。但是,是的,这将起作用。谢谢! :)【参考方案2】:

如前所述,Svelte 的反应是由分配触发的。 current Svelte tutorial 使用 javascript 的 (ES6) spread syntax(三个点)将下一个更高的数字添加到数组中,提供比使用 push 的冗余分配更惯用的解决方案:

function pushNumber()      
    numbers = [...numbers, lastnumber]; // 1, 2, 3, 4, 5

您可以使用扩展语法替换 popshiftunshiftsplice,尽管在某些情况下这可能会增加操作的时间和复杂性:

function unshiftNumber()   
    numbers = [firstnumber, ...numbers]; // 0, 1, 2, 3, 4


function popNumber() 
    numbers = [...numbers.slice(0,numbers.length - 1)]; // 1, 2, 3


function shiftNumber() 
    numbers = [...numbers.slice(1,numbers.length)]; // 2, 3, 4


function spliceNumber() 
    numbers = [firstnumber, ...numbers.slice(0,numbers.length-1)];// 0, 1, 2, 3
   

不过,传播只是实现这一目标的一种方式。不使用 pop/push 等的目的是鼓励不变性。因此,例如,任何删除都可以只是一个过滤器。

【讨论】:

这是一个更规范的答案,虽然接受的答案确实完全回答了这个问题,但它更彻底并且指的是惯用的苗条......【参考方案3】:

这里有几件事情需要考虑。 鉴于此代码:

messages.splice(messages.indexOf('hello'), 1);
messages = messages;

这里发生的事情是:

    寻找字符串"hello"在数组中第一次出现 根据找到的索引从数组中删除此类元素。

这里的假设是"hello"需要存在,否则可能会从数组中删除最后一个项(因为indexOf返回-1) .

因此,原始数组是变异的:取决于上下文,有时比将整个数组复制到新数组更可取;否则,通常最好避免这种突变。

所以。如果您希望完全具有这种行为,这可能是您可以拥有的最佳代码。以filter为例:

messages = messages.filter(message => message !== "hello")

这里发生的事情是:

    过滤掉 any 元素等于"hello" 返回一个没有此类元素的数组

所以它与原始代码完全不同:首先,它总是循环整个数组。如果您有数千个元素,即使您在第二个索引处只有一个 "hello",它也会始终迭代所有元素。也许这是你想要的,也许不是。如果元素是唯一的,比如一个 id,也许你一旦找到它就想停下来。 其次,它返回一个新数组。同样,这通常比改变数组更好,但在某些情况下,最好改变它而不是创建一个新数组。

所以,如果你想改变原始数组,最好还是坚持原来的代码。

如果你不关心(比如push的例子),我相信按照svelte开发者的意图,你的代码会大致翻译成:

let i = messages.indexOf("hello"); 
messages = [...messages.slice(0, i), ...messages.slice(i + 1)];

(仍然假设有 "hello" 消息并且您只对第一次出现感兴趣)。

很遗憾,JS 没有更好的语法来处理切片。

【讨论】:

完美回答每一个案例。【参考方案4】:

您可以执行通常的pushpop 或`在您的阵列上拼接

但是因为 Svelte 的响应是由赋值触发的,所以使用 push 和 splice 等数组方法不会自动导致更新。

根据All about Immutable Arrays and Objects in JavaScript,你可以这样做...

let messages = ['something', 'another', 'hello', 'word', 'another', 'again'];

const indexOfHello = messages.indexOf('hello');

messages = [...messages.slice(0, indexOfHello), ...messages.slice(indexOfHello + 1)];

注意spliceslice

的区别

splice() 方法在数组中添加/删除项目,并返回 删除的项目。 注意:此方法会更改原始数组。 语法array.splice(start, deleteCount, itemstoAdd, addThisToo);

但是

slice() 方法将数组中的选定元素作为新数组对象返回。 slice() 方法选择从给定的 start 参数开始的元素,并在给定的 end 参数处结束,但不包括。 注意:原数组不会改变。

按顺序

它将数组的一部分的浅拷贝返回到新数组中 从头到尾选择的对象(不包括结尾)。原本的 数组不会被修改。 语法: array.slice(start, end);

【讨论】:

【参考方案5】:

如果你在徘徊,filter 也可以用于使用给定的index 删除元素:

let elements = ['a','b', 'c'];
let idx = 1;
elements = elements.filter( (e,i) => i !== idx );
// => ['a', 'c']

【讨论】:

【参考方案6】:

传播拼接的数组以将其重新分配给自身;)

messages = [...messages.splice(messages.indexOf('hello'), 1)];

目标是让 Svelte 检测到数组 messages(组件的属性或 Svelte 存储中的变量)已更改。这就是为什么数组messages 必须用letvar 关键字声明,而不是const。这样你就可以重新分配它。而重新赋值操作本身就足以让 Svelte 检测到数组发生了变化。

也许,仅仅这样做也可以:

messages = messages.splice(messages.indexOf('hello'), 1);

【讨论】:

hrm,这个不准确,splice方法原地修改数组,返回删除的元素developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…

以上是关于如何在 Svelte 中拼接后更新数组?的主要内容,如果未能解决你的问题,请参考以下文章

如何触发/强制更新 Svelte 组件

Svelte + Typescript = 如何使错误消失?

如何在 Svelte 的类属性中使用反应函数?

如何专注于 Svelte 中新增的输入?

Svelte 3 - 如何循环每个块 X 次

Svelte - 更新值时防止重新渲染