Сервер Blazor — Большой Вес Страницы

#blazor #blazor-server-side

Вопрос:

Я заметил в своих серверных приложениях blazor, что иногда вес страницы может увеличиваться.

Например, у меня есть страница с длинным списком, каждый элемент списка содержит множество компонентов. Когда я просматриваю страницу в инструментах разработки и сохраняю html в виде текста, размер сохраненного файла может составлять 500 КБ. Когда я проверяю подключение к websocket и просматриваю сообщения, сообщение, возвращающее html-код страницы, может быть размером 1000 КБ или больше. Я думаю, что это как-то связано с javascript, который вводится, но я не уверен.

Кто-нибудь еще заметил это и нашел решения?

—- Обновление —-

Было указано, что я не предоставил образец кода, хороший момент! Вот код бритвы, о котором идет речь, _itemsSorted может содержать до 1000 элементов.

 @foreach (var item in _itemsSorted) {  lt;div class="rounded d-flex flex-row justify-content-between @GetItemCssClasses(item)" id="rank_@item.Rank"gt;  lt;divgt;  lt;div class="d-flex align-items-center"gt;  @item.Text  @if (item.Attributes.HasAttribute(Constants.item_Category))  {  string categoryName = GetCategoryDisplayName(Convert.ToInt32(item.Attributes.GetAttributeValue(Constants.item_Category)));  if (!string.IsNullOrEmpty(categoryName))  {  lt;spangt;amp;nbsp;amp;nbsp;lt;/spangt;lt;span class="badge badge-default badge-small"gt;@categoryNamelt;/spangt;  }  }  @if (!item.Active)  {  lt;spangt;amp;nbsp;amp;nbsp;lt;/spangt;lt;span class="badge badge-warning badge-small"gt;Inactivelt;/spangt;  }  @if (item.Attributes.HasAttribute(Constants.Ignore) amp;amp; item.Attributes.GetAttributeValue(Constants.Ignore) == "1")  {  lt;spangt;amp;nbsp;amp;nbsp;lt;/spangt;lt;span class="badge badge-danger badge-small"gt;Ignorelt;/spangt;  }  lt;/divgt;   @if (item.itemType != itemTypes.Folder amp;amp; !string.IsNullOrEmpty(item.SubText))  {  lt;div class="text-muted"gt;  @item.SubText  lt;/divgt;  }  lt;/divgt;  lt;div class="text-nowrap ml-2"gt;    lt;button class="@GetButtonsCssClasses(item)" title="@moveUpTitle" @onclick="@(e=gt; MoveUp(item))" disabled="@moveUpDisabled"gt;lt;span class="fas fa-arrow-up"gt;lt;/spangt;lt;/buttongt;  lt;button class="@GetButtonsCssClasses(item)" title="@moveDownTitle" @onclick="@(e=gt; MoveDown(item))" disabled="@moveDownDisabled"gt;lt;span class="fas fa-arrow-down"gt;lt;/spangt;lt;/buttongt;  lt;button class="@GetButtonsCssClasses(item)" title="Move to a new location" @onclick="@(e=gt; ShowMoveModal(item))" disabled="@_savingMoveStarted"gt;lt;span class="fas fa-exchange-alt"gt;lt;/spangt;lt;/buttongt;  lt;button class="@GetButtonsCssClasses(item)" title="Edit this item" @onclick="@(e=gt; ShowEdititemModal(item))" disabled="@_savingMoveStarted"gt;lt;span class="fas fa-edit"gt;lt;/spangt;lt;/buttongt;  @if (!_photoListInUse)  {  lt;button class="@GetButtonsCssClasses(item)" title="Delete this item" @onclick="@(e =gt; ShowConfirmDeleteitemModal(item))" disabled="@_savingMoveStarted"gt;lt;span class="fas fa-trash"gt;lt;/spangt;lt;/buttongt;  }  lt;div class="d-inline"gt;  lt;button class="@GetButtonsCssClasses(item)" data-toggle="dropdown" title="Add new item" disabled="@_savingMoveStarted"gt;lt;span class="fas fa-plus"gt;lt;/spangt;lt;/buttongt;  lt;div class="dropdown-menu pl-2 font-weight-normal" style="width:200px;"gt;  @if (showAddFolderAbove)  {  lt;a href="" @onclick:preventDefault @onclick="@(e=gt; ShowInsertItemModal(item, itemTypes.Folder, false, false))" class="text-dark px-2 py-1 d-block"gt;lt;span class="fas fa-folder-plus"gt;lt;/spangt;amp;nbsp;amp;nbsp;Insert lt;bgt;folderlt;/bgt; abovelt;/agt;  }  @if (showAddSubfolderAbove)  {  lt;a href="" @onclick:preventDefault @onclick="@(e=gt; ShowInsertItemModal(item, itemTypes.Folder, true, false))" class="text-dark px-2 py-1 d-block"gt;lt;span class="fas fa-folder-plus"gt;lt;/spangt;amp;nbsp;amp;nbsp;Insert lt;bgt;subfolderlt;/bgt; abovelt;/agt;  }  @if (showAddPhotoAbove)  {  lt;a href="" @onclick:preventDefault @onclick="@(e=gt; ShowInsertItemModal(item, itemTypes.Photo, false, false))" class="text-dark px-2 py-1 d-block"gt;lt;span class="fas fa-image"gt;lt;/spangt;amp;nbsp;amp;nbsp;Insert lt;bgt;photolt;/bgt; abovelt;/agt;  }  lt;hr /gt;  @if (showAddFolderBelow)  {  lt;a href="" @onclick:preventDefault @onclick="@(e=gt; ShowInsertItemModal(item, itemTypes.Folder, false, true))" class="text-dark px-2 py-1 d-block"gt;lt;span class="fas fa-folder-plus"gt;lt;/spangt;amp;nbsp;amp;nbsp;Insert lt;bgt;folderlt;/bgt; belowlt;/agt;  }  @if (showAddSubfolderBelow)  {  lt;a href="" @onclick:preventDefault @onclick="@(e=gt; ShowInsertItemModal(item, itemTypes.Folder, true, true))" class="text-dark px-2 py-1 d-block"gt;lt;span class="fas fa-folder-plus"gt;lt;/spangt;amp;nbsp;amp;nbsp;Insert lt;bgt;subfolderlt;/bgt; belowlt;/agt;  }  @if (showAddPhotoBelow)  {  lt;a href="" @onclick:preventDefault @onclick="@(e=gt; ShowInsertItemModal(item, itemTypes.Photo, false, true))" class="text-dark px-2 py-1 d-block"gt;lt;span class="fas fa-image"gt;lt;/spangt;amp;nbsp;amp;nbsp;Insert lt;bgt;photolt;/bgt; belowlt;/agt;  }  lt;/divgt;  lt;/divgt;  lt;div class="d-inline"gt;  lt;button type="button" class="@GetButtonsCssClasses(item)" data-toggle="dropdown" title="Additional settings"gt;lt;span class="fas fa-ellipsis-h"gt;lt;/spangt;lt;/buttongt;  lt;div class="dropdown-menu pl-2" style="width:200px;"gt;  lt;div class="h6"gt;Categorieslt;/divgt;  lt;a href="" @onclick="@(e=gt; ShowSelectCategoryModal(item))" @onclick:preventDefault class="text-dark px-2 py-1 d-block font-weight-normal" title="Categorize"gt;lt;span class="fas fa-list text-dark"gt;lt;/spangt;amp;nbsp;amp;nbsp;Categorizelt;/agt;  lt;a href="" @onclick="@(e=gt; RemoveCategoryFromItem(item))" @onclick:preventDefault class="text-dark px-2 py-1 d-block font-weight-normal" title="Uncategorize"gt;lt;span class="ion ion-md-close text-dark"gt;lt;/spangt;amp;nbsp;amp;nbsp;Uncategorizelt;/agt;  @if (PlType == ePlTypes.Package.ToInt32() || PlType == PlsViewModel.SiteOwnerPlTypeValue)  {  lt;div class="dropdown-divider"gt;lt;/divgt;  lt;div class="h6 pt-2"gt;Closeout Settingslt;/divgt;  lt;a href="" @onclick="@(e=gt; Exclude(item, true))" @onclick:preventDefault class="text-dark px-2 py-1 d-block font-weight-normal" title="Exclude."gt;lt;span class="fas fa-ban text-dark"gt;lt;/spangt;amp;nbsp;amp;nbsp;Excludelt;/agt;  lt;a href="" @onclick="@(e=gt; Exclude(item, false))" @onclick:preventDefault class="text-dark px-2 py-1 d-block font-weight-normal" title="Include."gt;lt;span class="fas fa-check-circle text-dark"gt;lt;/spangt;amp;nbsp;amp;nbsp;Includelt;/agt;  }  @if (item.itemType == itemTypes.Folder)  {  lt;div class="dropdown-divider"gt;lt;/divgt;   lt;a href="" @onclick="@(e=gt; ShowDuplicateFolderModal(item))" @onclick:preventDefault class="text-dark px-2 py-1 d-block font-weight-normal" title="Duplicate Folder."gt;lt;span class="fas fa-copy text-dark"gt;lt;/spangt;amp;nbsp;amp;nbsp;Duplicate Folderlt;/agt;  }  lt;/divgt;  lt;/divgt;  lt;/divgt;  lt;/divgt;  }  

Комментарии:

1. Хотя «статическая» страница может иметь размер всего 500 КБ, объем трафика SignalR будет зависеть от того, как вы написали код для создания обновления страницы. Интенсивный трафик часто является признаком того, что что-то не так в дизайне страницы. Без какого-либо кода трудно не только строить догадки.

2. @MrCakaShaunCurtis, хорошая мысль, я добавил несколько образцов бритвы. С тех пор как я начал использовать Blazor, у меня возникли проблемы с поиском того, что представляет собой «хороший» дизайн страницы.

3. Именно для этого существует компонент виртуализации. Он будет отображать только видимые элементы длинного списка

4. Этот вопрос слишком расплывчат — вы должны попытаться создать простой воспроизводимый образец.

5. @Liero, компонент виртуализации-отличный вызов, который значительно повышает производительность страницы. Единственным недостатком является то, что у меня есть навигация типа «оглавление», поэтому пользователи могут быстро перемещаться между разделами длинного списка. К сожалению, компонент виртуализации нарушает это, потому что элементы дальше по списку (или вверх по списку, если вы находитесь внизу) еще не были отрисованы. Я пытался использовать FocusAsync (), но это тоже не работает, та же проблема,

Ответ №1:

Вы не показали логику получения _itemsSorted , которая может быть основной причиной ваших проблем. Из того, что вы показали, вы можете использовать @key , чтобы помочь визуализатору идентифицировать элементы в цикле и не перерисовывать их без необходимости

 @foreach (var item in _itemsSorted) {  lt;div class="rounded d-flex flex-row justify-content-between @GetItemCssClasses(item)" id="rank_@item.Rank" @key=itemgt;  

При проектировании форм рассмотрите возможность использования компонентов для любых повторяющихся блоков визуализации. Например, ссылки с @if можно было бы преобразовать в компонент, похожий на этот:

Управление условной связью.бритва

 @if (Show) {  lt;a href="" @onclick:preventDefault @onclick="e=gt; this.OnClickEvent(1)" class="text-dark px-2 py-1 d-block"gt;lt;span class="fas @this.IconClass"gt;lt;/spangt;@this.Labellt;/agt; }  @code {   [Parameter] public bool Show { get; set; }   [Parameter] public EventCallbacklt;intgt; OnClick { get; set; }   [Parameter] public string IconClass { get; set; } = "fa-folder-plus";   [Parameter] public MarkupString Label { get; set; }   private void OnClickEvent(int item)  {  if (this.OnClick.HasDelegate)  this.OnClick.InvokeAsync(item);  } }  

который вы затем используете в качестве:

 lt;ConditionalLinkControl Show"@ShowFolderAbove" OnClick="LinkEventHandler"/gt;  

Комментарии:

1. Спасибо за советы. Мне любопытно использовать компоненты для повторяющихся блоков визуализации. Это действительно кажется лучшим дизайном с точки зрения кода, но помогает ли это с производительностью?