【问题标题】:In Blazor, is there a better way to add elements from a list?在 Blazor 中,是否有更好的方法从列表中添加元素?
【发布时间】:2021-01-24 20:04:48
【问题描述】:

我正在使用 DotNet 5rc1 和 Blazor 进行一些测试,并注意到一些让我觉得我做的不对的事情。

我有一个演示页面(下面的代码),它为用户提供了一组按钮,他们可以单击一个按钮来进行掷骰。然后,在客户端 C# 中,我滚动骰子并将滚动字符串添加到列表中。在模板中,然后我执行一次 foreach 并呈现用户过去的滚动。

问题是我一直在关注 Websocket 消息,每次添加新元素时,传递的消息都会变得越来越大。这是因为每次点击都会重新呈现整个列表。

有没有更好的方法来做到这一点,以便 Blazor 只在 DOM 中插入新项目并发送一条微不足道的消息,而不是重新呈现页面的整个部分?对于这个例子,这无关紧要,但如果我正在构建一个包含大量交互的更大应用程序,我可能会发现这会成为一个瓶颈。

这是我的页面的代码 - 这只是全新 Blazor 项目中的一个新页面。 (我的代码不止一个 1d20 按钮,但为了简洁起见,我从这里删除了它。)

@page "/dieroller"

<h1>Die Roller Utility</h1>

...

<button class="btn btn-primary" @onclick="@(e => RollDie(20))">
    <i class="fas fa-dice-d20"></i>
</button>

<h2 class='mt-5'>Your Roll History</h2>

@foreach (string roll in rolls) {
    <p>@roll</p>
}

@code {
    @using System.Threading;
    private List<string> rolls = new List<string>();

    private void RollDie(int upper)
    {
        Random rand = new Random();
        rolls.Insert(0, string.Format("1d{0} = {1}", upper, rand.Next(1, upper)));
    }
}

【问题讨论】:

  • 这是服务器端的 blazor 吗?
  • 你可以试试

    元素上的@key,但我认为它需要一个引用而不是一个字符串。

  • 旁注:您的骰子将产生 1-19 范围内的结果,而不是 1-20 。
  • @HenkHolterman 是的,我注意到了。 :)。在我下面的答案中修复。
  • @BrianParker - 现场。我不知道 Key 存在,不,你不能在字符串上正确地做到这一点。我正在根据密钥添加解决方案的答案。

标签: .net-core blazor


【解决方案1】:

您的问题的直接答案是使用@key。当涉及到循环时,这总是一个好主意。

问题是您需要 唯一 键,而您的字符串不符合该要求。

一个直截了当的解决方案是引入一个类(不是结构),只是为了获得唯一的键。我将您的代码修改为以下内容:

@foreach (Roll roll in rolls)
{
    <p @key="roll">@($"d={roll.Upper} {roll.Value}")</p>
}

@code {

    class Roll
    {
        public int Upper { get; set; }
        public int Value { get; set; }
    }

    private List<Roll> rolls = new List<Roll>();

    private void RollDie(int upper)
    {
        Random rand = new Random();
        rolls.Insert(0, new Roll { Upper = upper, Value = rand.Next(1, upper + 1) });
    }
}

使用这个你可以获得稳定的、非增长的 WS 数据包。

【讨论】:

  • 是的,这几乎就是我所做的 - 请参阅我发布的答案。在我以某种方式发布我的答案之前,我没有看到你的答案......很奇怪。
【解决方案2】:

根据上面@BrianParker 的评论 - 是的,您需要一个密钥,以便 Blazor 知道哪些项目需要更新。该 Key 也必须是唯一的,否则您会因密钥冲突而产生错误。

因为这些是字符串,所以它们不是很好的键 - 因此我最终用一个可以放入列表的 DieRoll 类对其进行了重组。这是后面的新代码:

using System;
using System.Collections.Generic;

namespace BlazorApp.Pages
{
    public partial class DieRoller
    {
        private List<DieRoll> rolls = new List<DieRoll>();

        private void RollDie(int upper)
        {
            rolls.Insert(0, new DieRoll(upper));
        }
    }

    public class DieRoll
    {
        public int Roll;
        public int DieType;

        public DieRoll(int DieType) {
            this.DieType = DieType;
            RollDie();
        }

        public int RollDie() {
            Random rand = new Random();
            this.Roll = rand.Next(1, DieType + 1);
            return Roll;
        }

        public override string ToString()
        {
            return string.Format("1d{0} = {1}", this.DieType, this.Roll);
        }

    }
        
}

下面是 p 标签处的新模板代码:

@foreach (DieRoll die in rolls) {
    <p @key="die">@die</p>
}

显然这更复杂,但这是可行的。来自服务器的消息要小得多,而且永远不会变大。

此外,如果我没有将项目添加到列表中,这可能无关紧要。附加到列表可能让 Blazor 更容易了解元素的创建位置。但我没有费心去验证这个理论。

您可以在此处查看另一个示例:https://blazor-university.com/components/render-trees/optimising-using-key/

【讨论】:

    【解决方案3】:

    List.Add 的消息大小保持不变,那么为什么 List.Insert 会有所不同?大概是由于 List.Insert(index: 0) 产生的固有性能损失,因为底层数组需要重新洗牌,请参见此处:https://stackoverflow.com/a/18587349/4000335 为了验证这一点,即使使用 List.Insert(List.Count) 显示消息大小保持不变(与 Add 一样)。

    如果要求将最后一卷放在最上面,那么最佳方法可能会涉及 JavaScript,这首先违背了使用 blazor 服务器的目的,但是 blazor webassembly 没有需要与服务器通信以完成此操作,因为这一切都在客户端完成。

    【讨论】:

      猜你喜欢
      • 2017-01-22
      • 2021-09-05
      • 2017-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-23
      • 1970-01-01
      相关资源
      最近更新 更多