Таки да, скоро выходит net core 3.0 и там будет шаблон проекта с Blazor как один из дефолтных. Название у фреймворка, по-моему, похоже на название какого-нибудь покемона. Блазор вступает в бой! Решил я значит глянуть что за зверь такой и с чем его едят поэтому сделал на нем Todo лист. Ну и на Vue.js тоже, для сравнения с сабжем потому что по моему они похожи система компонентов в обоих и реактивность и вот это все. Больше тудушек богу тудушек! По факту это Гайд для юных, не окрепших умов которым лень TypeScript или JavaScript учить а кнопочки и инпуты на сайте сделать хочется. Как в том меме -«Технарь хотел написать книгу но получилась инструкция». Кому интересны мои похождения в фронт энде или узнать что за Blazor такой добро пожаловать под кат.
Введение
Была когда-то у Майкрософт идея работы C# в браузере и звали эту идею Силверлайт. Не взлетело. Эти ваши тырнеты были тогда другие как собственно и браузеры. Почему я думаю что сейчас взлетит? Потому что сейчас веб ассембли есть во всех современных браузерах по дефолту. Нет необходимости в установке отдельного расширения. Другая проблема размеры приложения. Если на Vue.js SPA весит 1.7 мегабайт, то точно такое же на Blazor 21 мегабайт. Сейчас интернет быстрее и надежнее стал чем во времена Сильверлайта да и скачивать приложение надо один раз, а дальше там кеш и все дела. Вообще Blazor мне показался очень похожим на Vue.js. И так, как дань уважения Silverligtht, WPF и UWP да и просто потому что у шарпистов так принято я решил использовать паттерн MVVM для своего проекта. Так для справки — Я вообще бекэндшик и мне Blazor понравился. Слабонервных предупреждаю — Дизайн и верстка в моих примерах ужасные, а в проекте с Vue.js опытный фронтэндшик может узреть много говнокода. Ну и с орфографией и пунктуацией дела тоже так себе.
Ссылки
Пример Todo на Vue + Vuex
Пример Todo на Blazor
Модели размещения
- На стороне клиента. Стандартное SPA которое можно раздавать различными способами. В моем примере я использовал шаблон в котором файлы приложения отдает браузеру сервер на asp.net core. Минус этого подхода в тех самых 21 мегабайтах которые нужно скачать браузеру.
- На стороне сервера. Все происходит на сервере, а клиенту через сокеты передается готовый DOM. Браузеру вообще почти ничего не надо скачивать в начале, но зато вместо этого постоянно по кускам скачивать обновленный DOM. Ну и вся нагрузка по клиентскому коду внезапно взваливается на сервер.
Мне лично первый вариант больше нравиться и его можно использовать во всех тех случаях когда вам не нужно беспокоиться о конверсии пользователей. Например это какая-то внутренняя информационная система компании или специализированное B2B решение потому что Blazor долго скачивается в первый раз. Если ваши пользователи постоянно заходят в ваше приложение, то они не заметят никакой разницы с JS версией. Если пользователь заходит по рекламной ссылке просто глянуть что там за сайт какой-то скорее всего он не будет долго ждать пока сайт загрузиться и просто уйдет. В этом случае лучше использовать второй вариант размещения т.e. Server Side Blazor.
Создание проекта
Скачайте net core 3.0 dotnet.microsoft.com/download/dotnet-core/3.0
Выполните в терминале команду которая загрузить вам необходимые шаблоны.
dotnet new -i Microsoft.AspNetCore.Blazor.Templates
Для создания Server Side
dotnet new blazorserverside -o MyWebApp
Для Client Side файлики которого будет раздавать сервер asp.net core
dotnet new blazorhosted -o MyWebApp
Если вам захотелось экзотики и вдруг решили не использовать в качестве сервера asp.net core а что-то другое (А оно вам надо вообще?) можете создать только клиент без сервера вот такой командой.
dotnet new blazor -o MyWebApp
Биндинги
Поддерживается односторонняя привязка и двусторонняя. Таки да, не надо никаких OnPropertichanged как в WPF. При изменении Вью Модели разметка меняется автоматически.
<label>One way binding:</label>
<br />
<input type="text" value=@Text />
<br />
<label>Two way binding:</label>
<br />
<input type="text" @bind=@Text />
<br />
<label>Two way binding и смена события при которов будет меняться поле Text на событие oninput:</label>
<br />
<input type="text" @bind=@Text @bind:event="oninput" />
//ViewModel
@code{
string Text;
async Task InpuValueChanged()
{
Console.WriteLine("Input value changed");
}
}
И так, тут у нас есть ViewModel (анонимная) у которой есть поле Text.
В первом инпуте через «value=@Text» мы сделали одностороннюю привязку. Теперь когда мы изменим Text в коде тут же изменится текст внутри input. Только вот чтобы мы не печатали в нашем инпуте это никак не повлияет на нашу VM. Во втором input через "@bind=@Text" мы сделали двухстороннюю привязку. Теперь если мы напишем что-то новое в нашем input тут же поменяется наша VM, и обратное тоже верно т.е. если мы поменяем поле Text в коде то наш input тут же отобразит новое значение. Тут есть одно НО — по дефолту изменения привязаны к событию onchange нашего input поэтому VM поменяться только тогда когда мы завершим ввод. В третьем input "@bind:event=«oninput»" мы изменили событие для передачи данных VM на oninput теперь каждый раз когда мы печатаем какой-нибудь символ новое значение тут же передается нашей VM. Так же для DateTime можно указать формат например так.
<input @bind=@Today @bind:format="yyyy-MM-dd" />
View Model
Можно ее делать анонимкой тогда ее нужно помешать внутри блока "@code {}"
@page "/todo"
<p> Привет @UserName </p>
@code{
public string UserName{get; set;}
}
или можно вынести ее в отдельный файл. Тогда ее надо наследовать от ComponentBase и в начале страницы указать ссылку на нашу VM c помошью "@inherits"
Например
TodoViewModel.cs:
public class TodoViewModel: ComponentBase{
public string UserName{get; set;}
}
Todo.razor:
@page "/todo"
@inherits MyWebApp.ViewModels.TodoViewModel
<p> Привет @UserName </p>
Маршрутизация
Маршруты на которые будет реагировать страница указываются в ее начале с помощью "@page". Причем их может быть несколько. Будет выбран первый точно соответствующий в порядке сверху вниз. Например:
@page "/todo"
@page "/todo/delete"
<h1> Hello!</h1>
Эта страница будет открываться по адресу "/todo" или «todo/delete»
Лайауты
В общем-то сюда обычно помещают одинаковые для нескольких страниц вещи. Вроде сайдбара и прочего.
Для того чтобы использовать лайаут во первых, нужно его создать. Он должен наследоваться от LayotComponentBase с помощью "@inherits". Например
@inherits LayoutComponentBase
<div class="sidebar">
<NavMenu />
</div>
<div class="main">
<div class="top-row px-4">
<a href="http://blazor.net" target="_blank" class="ml-md-auto">About</a>
</div>
<div class="content px-4">
@Body
</div>
</div>
Во-вторых, его нужно импортировать. Для этого в директории со страницами которые его будут использовать нужно создать файл _imports.razor потом добавить в этот файл строчку "@layout"
@layout MainLayout
@using System
В третьих можно у страницы указать какой именно лайаут она использует напрямую
@layout MainLayout
@page "/todo"
@inherits BlazorApp.Client.Presentation.TodoViewModel
<h3>Todo</h3>
Вообще _imports.razor и using в нем действуют на все страницы которые находятся с ним в одной папке.
Параметры маршрутов
Во первых — указать параметр и его тип в фигурных скобках в нашем маршруте (регистра независим). Поддерживаются стандартные типы дотнет. Таки да, опциональных параметров нет т.е. значение нужно передавать всегда.
Само значение можно получить создав у нашей ViewModel свойство с именем таким же как у параметра и с атрибутом [Parameter] БТВ — забегая в перед — данные и события в дочерние компоненты из родительских передаются тоже с помощью атрибута [Parameter] так же есть каскадные параметры. Они передаются от родительского компонента всем его дочерним компонентам и их дочерним компонентам. Они используются в основном для стилей а стили лучше все же просто делать в CSS поэтому ну его нафиг.
@page "/todo/delete/{id:guid}"
<h1> Hello!</h1>
@code{
[Parameter]
public Guid Id { get; set; }
}
DI
Все регистрируется в Startup.cs, как в обычном asp.net core приложении. Тут ничего нового. А вот внедрение зависимостей для нашей VM таки происходит через публичные свойства а не через конструктор. Свойство просто нужно декорировать атрибутом [Inject]
public class DeleteTodoViewModel : ComponentBase
{
[Parameter]
private Guid Id { get; set; }
[Inject]
public ICommandDispatcher CommandDispatcher { get; set; }
По умолчанию есть уже подключенных 3 сервиса. HttpClient — ну вы знаете зачем он. IJSRuntime — вызов JS кода из C#. IUriHelper — с помощью не его можно делать переадресацию на другие страницы.
Пример приложения
Компонет таблицы Todo
TodoTableComponent.razor:
//1)
<table class="table table-hover">
<thead>
<th>Задача выполнена</th>
<th>Название</th>
<th>Дата создания</th>
<th>Действия</th>
</thead>
<tbody>
//2)
@foreach (var item in Items)
{
//3)
<tr @onclick=@(()=>ClickRow(item.Id)) class="@(item.Id == Current?"table-primary":null)">
<td><input type="checkbox" checked="@item.IsComplite" disabled="disabled" /></td>
<td>@item.Name</td>
<td>@item.Created.ToString("dd.MM.yyyy HH:mm:ss")</td>
<td><a href="/todo/delete/@item.Id" class="btn btn-danger">Удалить</a></td>
</tr>
}
</tbody>
</table>
@code {
//4)
[Parameter]
private List<BlazorApp.Client.Presentation.TodoDto> Items { get; set; }
[Parameter]
private EventCallback<UIMouseEventArgs> OnClick { get; set; }
[Parameter]
private Guid Current { get; set; }
private async Task ClickRow(Guid id)
{
//5
await OnClick.InvokeAsync(CreateArgs(id));
}
private ClickTodoEventArgs CreateArgs(Guid id)
{
return new ClickTodoEventArgs { Id = id };
}
//6)
public class ClickTodoEventArgs : UIMouseEventArgs
{
public Guid Id { get; set; }
}
}
- Так как это компонент нам тут не нужны "@page" и "@layout" потому что он не будет участвовать в маршрутизации а лайаут он будет использовать от родительского компонента
- С символа @ начинается C# код. Собственно так же как и в Razor
Привязывает событие нажатия на строку к методу ClickRow нашей ViewModel@onclick=@(()=>ClickRow(item.Id))
- Указываем какие параметры будут передаваться из родительского компонента или страницы в наш с помощью атрибута [Parameter]
- Вызываем функцию обратного вызова которую получили из родительского компонента. Так родительский компонент узнает что в дочернем произошло какое-то событие. Функции можно передавать только завернутыми в EventCallback<> параметризованный EventArgs. Возможный список EventArgs можно посмотреть тут — docs.microsoft.com/ru-ru/aspnet/core/blazor/components?view=aspnetcore-3.0#event-handling
- Так как список возможных типов EventArgs ограничен а нам нужно передать дополнительное свойство Id в обработчик события на стороне родительского компонента то мы создаем свой собственный класс параметра унаследованный от базового и передаем уже его в событие. Таки да — в родительский компонент, в функцию обработчик события прилетит обычный UIMouseEventArgs и его нужно будет привести к нашему типу например с помощью оператора as
Пример использования:
<TodoTableComponent Items=@Items OnClick=@Select Current=@(Selected?.Id??Guid.Empty)></TodoTableComponent>
Страница для удаления Todo
Наша ViewModel aka VM — DeleteTodoViewModel.cs:
public class DeleteTodoViewModel : ComponentBase
{
//1)
[Parameter]
private Guid Id { get; set; }
//2)
[Inject]
public ICommandDispatcher CommandDispatcher { get; set; }
[Inject]
public IQueryDispatcher QueryDispatcher { get; set; }
[Inject]
public IUriHelper UriHelper { get; set; }
//3)
public TodoDto Todo { get; set; }
protected override async Task OnInitAsync()
{
var todo = await QueryDispatcher.Execute<GetById,TodoItem>(new GetById(Id));
if (todo != null)
Todo = new TodoDto { Id = todo.Id, IsComplite = todo.IsComplite, Name = todo.Name, Created = todo.Created };
await base.OnInitAsync();
}
//4)
public async Task Delete()
{
if (Todo != null)
await CommandDispatcher.Execute(new Remove(Todo.Id));
Todo = null;
//5)
UriHelper.NavigateTo("/todo");
}
}
- Параметр маршрута "/todo/delete/{id:guid}" сюда передаться Guid если мы перейдем например по адресу localhost/todo/delete/ae434aae44...
- Инжектим сервисы из DI контейнера в нашу VM.
- Просто свойство нашей VM. Ее значение мы устанавливаем сами, какое хотим.
- Это метод вызывается автоматически при инициализации страницы. Тут мы устанавливаем нужные значения для свойств нашей VM
- Метод нашей VM. Мы можем привязать его например к событию нажатия какой нибудь кнопки нашей View
- Переход на другую страницу которая находиться по адресу "/todo" т.е. у нее в начале есть строчка "@page "/todo""
Наша View — DeleteTodo.razor:
//1) @page "/todo/delete/{id:guid}" @using BlazorApp.Client.TodoModule.Presentation @using BlazorApp.Client.Shared; //2) @layout MainLayout //3) @inherits DeleteTodoViewModel <h3>Удалить Todo </h3> @if (Todo != null) { <div class="row"> <div class="col"> <input type="checkbox" checked=@Todo.IsComplite disabled="disabled" /> <br /> <label>@Todo.Name</label> <br /> //4) <button class="btn btn-danger" onclick=@Delete>Удалить</button> </div> </div> } else { <p><em>Такой Todo не найден</em></p> }
- Указываем что эта страна будет доступна по адресу {корневой адрес нашего сайта} +"/todo/delete/"+{какой то Guid}. Например localhost/todo/delete/ae434aae44...
- Указываем что наша страница будет рендериться внутри MainLayout.razor
- Указываем что наша страница будет использовать свойства и методы класса DeleteTodoViewModel
- Узказываем что при нажатии на эту кнопку будет вызываться метод Delete() нашей VM
Главная страница Todo
TodoViewModel.cs:
public class TodoViewModel : ComponentBase { [Inject] public ICommandDispatcher CommandDispatcher { get; set; } [Inject] public IQueryDispatcher QueryDispatcher { get; set; } //1) [Required(ErrorMessage = "Введите название Todo")] public string NewTodo { get; set; } public List<TodoDto> Items { get; set; } public TodoDto Selected { get; set; } protected override async Task OnInitAsync() { await LoadTodos(); await base.OnInitAsync(); } public async Task Create() { await CommandDispatcher.Execute(new Add(NewTodo)); await LoadTodos(); NewTodo = string.Empty; } //2) public async Task Select(UIMouseEventArgs args) { //3) var e = args as TodoTableComponent.ClickTodoEventArgs; if (e == null) return; var todo = await QueryDispatcher.Execute<GetById, TodoItem>(new GetById(e.Id)); if (todo == null) { Selected = null; return; } Selected = new TodoDto { Id = todo.Id, IsComplite = todo.IsComplite, Name = todo.Name, Created = todo.Created }; } public void CanselEdit() { Selected = null; } public async Task Update() { await CommandDispatcher.Execute(new Update(Selected.Id, Selected.Name, Selected.IsComplite)); Selected = null; await LoadTodos(); } private async Task LoadTodos() { var todos = await QueryDispatcher.Execute<GetAll, List<TodoItem>>(new GetAll()); Items = todos.Select(t => new TodoDto { Id = t.Id, IsComplite = t.IsComplite, Name = t.Name, Created = t.Created }) .ToList(); } }
- Поддерживаются стандартные атрибуты валидации из System.ComponentModel.DataAnnotations. Конкретно тут мы указываем что это поле обязательное и тот текст который будет отображаться если пользователь не укажет значение в том input который будет связан с этим полем.
- Метод для обработки события с параметром. Этот метод будет обрабатывать событие из дочернего компонента
- Приводем аргумент к типу который мы создали в дочернем компоненте
Todo.razor:
@layout MainLayout @page "/todo" @inherits BlazorApp.Client.Presentation.TodoViewModel <h3>Todo</h3> <h4>Список</h4> <div class="row"> <div class="col"> @if (Items == null) { <p><em>Загрузка...</em></p> } else if (Items.Count == 0) { <p><em>Нет задач для отображения. Пожалуйсте добавте какую нибудь.</em></p> } else { //1) <TodoTableComponent Items=@Items OnClick=@Select Current=@(Selected?.Id??Guid.Empty)></TodoTableComponent> } </div> </div> <br /> <h4>Создать Todo</h4> <div class="row"> <div class="col"> @if (Items != null) { //2) <EditForm name="addForm" Model=@this OnValidSubmit=@Create> //3) <DataAnnotationsValidator /> //4) <ValidationSummary /> <div class="form-group"> //5) <InputText @bind-Value=@NewTodo /> //6) <ValidationMessage For="@(() => this. NewTodo)" /> //7) <button type="submit" class="btn btn-primary">Создать</button> </div> </EditForm> } </div> </div> <br /> <h4>Редактировать Todo</h4> <div class="row"> <div class="col"> @if (Items != null) { @if (Selected != null) { <EditForm name="editForm" Model=@Selected OnValidSubmit=@Update> <DataAnnotationsValidator /> <ValidationSummary /> <div class="form-group"> <InputCheckbox @bind-Value=@Selected.IsComplite /> <InputText @bind-Value=@Selected.Name /> <button type="submit" class="btn btn-primary">Сохранить</button> <button type="reset" class="btn btn-warning" @onclick=@CanselEdit>Отмена</button> </div> </EditForm> } else { <p><em>Кликните на задаче чтобы ее редактировать</em></p> } } </div> </div>
- Вызываем дочерний компонент и передаем ему в качестве параметров свойства и методы нашей VM.
- Встроенный компонент формы с валидацией данных. Указываем в нем что в качеств е модели он будет использовать нашу VM и при отправке валидных данных он будет вызывать ее метод Create()
- Валидация будет выполняться с помощью атрибутов модели вроде [Requared] и т.п.
- Здесь буду отображаться общие ошибки валицадии
- Создаст input с валидацией. Список возможных тегов — InputText, InputTextArea, InputSelect, InputNumber, InputCheckbox, InputDate
- Здесь будут отображаться ошибки валидации для свойства public string NewTodo{get;set;}
- При нажатии на эту кнопку будет вызываться событие OnValidSubmit нашей формы
Файл Startup.cs
Тут мы регистрируем наши сервисы
public class Startup { public void ConfigureServices(IServiceCollection services) { //Добавляем LocalStorage и SessionStorage как синглтоны чтобы сохранять данные на //стороне клиента в браузере // Тут нужно подключить черед Nuget пакет Blazor.Extensions.Storage services.AddStorage(); services.AddSingleton<ITodoRepository, TodoRepository>(); services.AddSingleton<ICommandDispatcher, CommandDispatcher>(); services.AddSingleton<IQueryDispatcher, QueryDispatcher>(); services.AddSingleton<IQueryHandler<GetAll, List<TodoItem>>, GetAllHandler>(); services.AddSingleton<IQueryHandler<GetById, TodoItem>, GetByIdHandler>(); services.AddSingleton<ICommandHandler<Add>, AddHandler>(); services.AddSingleton<ICommandHandler<Remove>, RemoveHandler>(); services.AddSingleton<ICommandHandler<Update>, UpdateHandler>(); } public void Configure(IComponentsApplicationBuilder app) { //Указываем что корневым компонентом нашего приложения будет App.razor // и его содержимое будет помещаться внутри тега <app></app> app.AddComponent<App>("app"); } }
Эпилог
Эта статься была написана, чтобы разыграть аппетит и подтолкнуть к дальнейшему изучению Blazor. Надеюсь, поставленной цели я достиг. Ну а чтобы изучить его получше, рекомендую почитать официальное руководство от Майкрософт.
Благодарности
Спасибо AndreyNikolin, win32nipuh, SemenPV за найденные орфографические и грамматические ошибки в тексте.
Комментарии (70)
shaukote
12.08.2019 03:03Если на Vue.js SPA вести 1.7 мегабайт, то точно такое же на Blazor 21 мегабайт.
Кхм, а есть какое-то примерное понимание, откуда там такие размеры-то? Очень хочется пошутить про то, что он тянет с собой весь CLR, но это уж явно не имеет отношения к реальности.
mayorovp
12.08.2019 09:03Тем не менее, это именно так. А вы видите какой-то другой вариант запуска управляемого кода?
shaukote
12.08.2019 11:55А, точно, C# же managed… Я было думал, что оно полностью компилируется в WebAssembly, без использования CLR в run-time.
Впрочем, если я правильно понимаю, с появлением GC integration в WebAssembly, можно ожидать отказ от CLR и уменьшения размеров сборки. Впрочем, не очень понятно, когда это ещё будет.
some_x
12.08.2019 13:40На сколько я знаю так и есть. На данном этапе решили не заморачиваться с компиляцией IL в WASM, вместо этого скомпилировали сам .NET в WASM, который интерпретирует обычный IL.
VanquisherWinbringer Автор
12.08.2019 14:30Ну на Rust и Yew (Фреймворк под веб ассембли на Rust) приложение весит почти столько же сколько и на Vue так что таки да, дело в рантайме который Blazor тащит с собой.
shaukote
12.08.2019 15:46А вот это, кстати, любопытно уже с другой стороны. :)
Если Rust-приложения не тянут собой лишний рантайм и GC, то почему они не весят меньше?
Вроде как резонно предплоложить (да, я знаю, что хожу по тонкому льду), что логика там плюс-минус та же, что и во Vue-приложениях (или любых других JS-приложениях), но при этом как минимум байт-код должен быть более компактен, чем синтаксис JS.
shaukote
12.08.2019 03:06Все происходит на сервере, а клиенту через сокеты передается готовый DOM. Браузеру вообще почти ничего не надо скачивать в начале, но зато вместо этого постоянно по кускам скачивать обновленный DOM.
Хммм, да поправит меня yelbota, но что-то мне это очень сильно напоминает… :)
yelbota
12.08.2019 11:05Да, обсуждали сходство в комментах к моему посту. Непонятен только вес рантайма для сервер-сайд режима. Если он весит мегабайты как Vaadin, то это не имеет особого смысла. Королев специально сделан так чтобы весить минимально.
yurickas
12.08.2019 06:23Месяц отдал на эту интересную штуку. Понравилось, но сыровато. Заявленного рантайма нету, дебаг пробовал запустить и ничего не вышло. Можно делать очень интересные вещи, до которых ангуляр и его братья не дотянутся. Например автоматическое построение формы на основе атрибутов вьюмодели.
Про 21 мб автор немного пошутил. Если правильно поставить зависимости, чтобы клиент не тянул серверные сборки, то получается в разы меньше и работает быстрее. Года через 2-3 сие чудо уже будут требовать в стеке разработчика, так что изучать можно сейчасmayorovp
12.08.2019 09:06А в чём проблема сделать автоматическое построение формы по атрибутам вью-модели на Ангуляре?
alexs0ff
12.08.2019 09:41Более того, в 8 версию разработчики добавили описание такой возможности в официальную документацию
kekekeks
12.08.2019 09:23Silverlight-ом оно станет, когда будет что-то типа этого: http://testapp.keks-n.net/ (потыкайтесь для интереса инспектором). А пока это обычный веб-фреймворк.
LootKeeper
12.08.2019 14:33Вопрос, а почему оно лагает то так?
kekekeks
12.08.2019 18:12Пушто
1) сделано левой ногой за два дня с целью проверки концепта и сбора граблей
2) Mono в режиме интерпретатора в целом весьма тормознутое
По результатам: инфраструктура для работы в WASM в дотнетах пока очень сырая, отладка отсутствует в принципе, стоит подождать ещё год перед очередной попыткой портирования.
win32nipuh
12.08.2019 10:02-1Blazor + NET Core 3 в развитии, надо изучать.
Но, причитав частушки типа «Больше тудушек богу тудушек!» становится странно, автор лучше бы ошибки исправил в тексте, если в состоянии, чем такое писать.
Например,
Все регистрируется в Startap.cs [запятая] как в обычном asp.net core приложении. Тут ничего нового. А вот внедрение зависимостей для нашей VM таки происходит через публичные свойства [запятая] а не через кноструктор. Свойство просто нужно декорировать отрибутом [<-ОтрЕбутом]
Matisumi
12.08.2019 11:14-1Вот только у меня такое чувство, что нет особого смысла учить эту костыльную штуку, когда есть Vue, Angular и прочее? Есть ощущение, что через пару лет ее выпилят как и Сильверлайт в свое время.
mayorovp
12.08.2019 11:24Сервелат выл выпилен не сам по себе, а вместе с NPAPI. На тех же браузерах, которые этот NPAPI всё ещё поддерживают — Silverlight работает до сих пор.
Но я пока что не могу представить ситуацию, в которой браузеры точно так же дружно отказываются от WebAssembly.
HomoLuden
15.08.2019 00:11А по моим наблюдениям сервелат начали прибивать по политическим соображениям, когда никаких технических препятствий не было. Он мне как технология напомнил песню Бритни: I'm not a girl. Not yet a woman.
В сравнении с WPF он был неудобен и менее функционален. Плюс неудобная модель дистрибьюции и ещё больше проблем сперфомансом, чем у WPF.
Kanut
12.08.2019 11:29+1Если эта «костыльная штука» будет работать хотя бы приблизительно так хорошо как «Vue, Angular и прочее», то учить её очень даже стоит. Так как она позволяет использовать один и тот же язык и одни и те же библиотеки для фронта и бэка. И это очень многого стоит. Как разработчик ты экономишь кучу времени, которое в том же Angular уходит на дублирование кода. Как фирма ты экономишь кучу денег и времени, так как твоим разработчикам нужно учить на один язык программирования меньше.
То есть на мой взгляд если Microsoft не учудит каких-либо глупостей, то эта штука «взлетит». И я лично с удовольствием на неё перейду годика через два-три.Matisumi
12.08.2019 11:51> Как разработчик ты экономишь кучу времени
А как пользователь мне понравится тащить в 20 раз больше мегабайт?Kanut
12.08.2019 11:56А с чего вы решили что вы обязательно будете «тащить в 20 раз больше мегабайт»? Если я всё правильно понимаю что в случае «серверной версии» там не сильно много лишнего и тащить то надо будет.
И как-будто всякие Javascript/Typescript/Angular/… библиотеки, которые тащатся сейчас, совсем ничего не весят :)shaukote
12.08.2019 14:51И как-будто всякие Javascript/Typescript/Angular/… библиотеки, которые тащатся сейчас, совсем ничего не весят :)
Берём (для примера) самый мейнстримовый (и при этом весьма раздутый, если по-честному) стек: React (+ ReactDOM) + Redux + Reselect + Redux-Saga.
Суммарно получаем ~139 kB minified или ~46 kB minified + gzipped.
Даже если накинуть ещё столько же на прикладную логику (хотя это будет уже явно не уровень todo-приложения), Blazor'у ещё расти и расти в этом плане.Kanut
12.08.2019 14:58А если добавить туда какую-нибудь библиотечку для формочек/контролей и прочего? Сколько тогда будет? И с React я лично не работал, но если брать Angular(a особенно если Angular Elements какие-нибудь) да ещё и с каким-нибудь Kendo, ещё парочку других библиотек добавить, да ещё картиночек, да ещё… то уже пойдут мегабайты. И это не то чтобы абсолютно исключение для веб-страничек…
shaukote
12.08.2019 15:27Ну, самое, опять же, мейнстримовое и разухабистое решение — Redux-Form, ещё 139 kB minified или 26.9 kB minified + gzipped. Безумно много, но суммарно (со всем остальным) даже до половины мегабайта не дотянули.
При этом довольно популярная альтернатива (от тех же авторов, осознавших, какое хтоническое чудовище они породили в виде Redux-Form) — Final-Form, весит всего 14.8 kB minified или 5 kB minified + gzipped.
Понятное дело, что для сильных, смелых и умелых бесконечность никогда не предел, можно на чём угодно сделать приложение любых размеров. Но мы, я надеюсь, говорим о том, насколько хорошо можно сделать, а не о том, насколько плохо? :)
Пока что в сравнении размеров базовой комплектации Blazor очень сильно уступает современным JS-решениям. К тому же, решение с интерпретацией IL на клиенте (см. выше) лично у меня вызывает вопросы касательно производительности выполнения прикладного кода.
Надеюсь, всё это временные проблемы Blazor'а, которые его разработчикам получится преодолеть.mayorovp
12.08.2019 15:41С производительностью там как раз всё в порядке: это ж не чистая интерпретация, а JIT-компиляция статически типизированного языка.
Проблемы тут возможны с долгим стартом и с размером.
shaukote
12.08.2019 15:51То есть у нас JIT-компиляция IL внутри CLR, который работает в WebAssembly VM c JIT? :)
mayorovp
12.08.2019 15:56… и в чём проблема?
shaukote
12.08.2019 16:16В том, что современный JS компилируется в байт-код на старте приложения и дальше JIT-компилируется нативным JS-движком.
А в случае C# добавляется дополнительная прослойка в виде CLR, что "вызывает вопросы касательно производительности".
Tangeman
12.08.2019 17:38А в случае C# добавляется дополнительная прослойка в виде CLR, что «вызывает вопросы касательно производительности».
Не факт. Там есть AOT (вероятно, ещё не полностью допиленный), т.е. в браузер уже уйдёт оптимизированный wasm-код (без IL), который, в свою очередь, ещё может быть JIT-компилирован самим браузером (как и любой другой).
mayorovp
12.08.2019 18:02Вот только JS, будучи языком с динамической типизацией, испытывает проблемы с оптимизацией полиморфных функций. От чего, к слову, страдают в первую очередь фреймворки — ведь именно у них зачастую внутри содержится код, которому приходится пропускать через себя значения совершенно разных типов.
А что MSIL что WASM являются полностью статически типизированными, что позволяет результирующему машинному коду не делать никаких проверок типа в рантайме.
HomoLuden
15.08.2019 00:28В вебфреймворках размер бандла распухает подключением Materials Design и прочих нахлобучек, которых блазоре нет также. Тут ничья. А вот .net runtime не стрипается вообще. Это фундаментальный косяк, что с приложением в память грузятся целиком сборки. Так же и в блазоре щас. Через сеть тянется весь рантайм. Изменится сишарп твоей логики и рантайм заново будет сосаться или браузер сможет в глобальный кэш положить рантайм (GAC в браузере)?
VanquisherWinbringer Автор
12.08.2019 14:59Как я уже выше говорил приложения на Rust весят почти столько же как и под JS с фреймворками так что тут дело в рантайме. Как только запилят встроенный в WASM GC и оптимизируют сборку чтобы в ней был только нужный код то будет и Blazor весит меньше мегабайта. Таки да технология молодая от слова совсем. Имхо — задавит JS фреймворки со временем потому что большинству молодых программистов просто лень учить JS. Вы не представляете себе силу лени. Лет через 5 — 6 когда примерно произойдет смена поколений.
shaukote
12.08.2019 15:31Имхо — задавит JS фреймворки со временем потому что большинству молодых программистов просто лень учить JS. Вы не представляете себе силу лени.
На мой взгляд, с учётом того, сколько нынче JS-разрабочиков, людей, активно изучающих JS (и пойдущих в JS-разработчики через год-другой), ну и уже написанного JS-кода, "сила лени" это скорее аргумент в противоположную сторону. :)
Хотя не уверен, какой смайл здесь более уместен. :(
Kanut
12.08.2019 16:26Я работаю с JS уже долго и лично я им очень недоволен. TS уже лучше, но всё ещё унаследовал некоторые «болезни» JS. Если лично мне дадут возможность писать фронтэнд на языке вроде C#, то я буду танцевать от радости :)
П.С. И да на JS/TS тоже можно работать «правильно» и писать «чистый» код. Но проблема в том что далеко не всегда работаешь только со своим кодом и тут начинаются заморочки.shaukote
12.08.2019 16:35Я же и не спорю с такой точкой зрения. :)
Но тем не менее индустрия весьма инертна, есть много людей, которые вложили свои силы и время в изучение JS (и очень многим из них весьма комфортно с этим языком), много компаний, которые вложили большие деньги в JS (от Google, вложивший огромное количество сил в свой V8, до продуктовых компаний, выстроивших продукт вокруг JS-стека).Kanut
12.08.2019 16:44+1Компании понять можно. Но обычно если выясняется что на новом стэке можно экономить время и деньги, то они не долго тянут с переходом :)
А насчёт людей, которые вложили время в изучение JS… Ясное дело что люди, которые знают JS и не знают C#, в большинстве своём останутся на JS. А вот люди, которые знакомы с JS и C#(а таких на мой взгляд достаточно много), на мой взгляд потихоньку будут избавлятся от JS :)shaukote
12.08.2019 17:26люди, которые знакомы с JS и C#(а таких на мой взгляд достаточно много)
Всё очень зависит от личного опыта, я вот за всю свою жизнь ни одного живого C#-разработчика не видел (кроме себя разве что, но я не настоящий сварщик). ?_(?)_/?
Что конечно же совсем не означает, что их нет или мало.
обычно если выясняется что на новом стэке можно экономить время и деньги, то они не долго тянут с переходом
Ох, это очень сложный вопрос.
Тут и количество/запросы разработчиков на рынке нужно учитывать, и вопрос стоимости миграции существующих решений.
Да и JS сегодня позволяет достаточно (окей, более-менее) эффективно писать код под практически все платформы.
Я вот не готов сходу делать такие смелые оценки, что будет для сферического в вакууме бизнеса выгоднее.
off-topicПо моим воспоминаниями, лет пять назад все дружно похоронили Ruby и RoR. Я вот вообще не вижу причин сегодня писать на Ruby.
Однако ж не очень похоже что этот стек умер. :)HomoLuden
15.08.2019 00:48Пример:
Visual Studio против VS Code.
Функционал для рядового девелопмента у Code не намного скуднее. Даже иногда наоборот.В Code можно отлаживать Xamarin Android на реальном телефоне, а с VS хз как… Я не нашел способа подрубить дебаггер.
Code написана на js и летает
VS написана на убогом старом VS Shell (c++) а поверх натянут WPF. Топмозит, жрет памяти вагон, и размер установки с нужными тулзами под 30ГБ на системном диске.
HomoLuden
15.08.2019 00:41Я знаю и то и другое на высоком уровне. Ни в жисть не буду с js слазить. Хватило мне с головой winforms и webforms. Если на шарпе, только wpf с XAML.
HomoLuden
15.08.2019 00:39Нормальный инж должен под конкретную задачу осваивать подходящие инструменты. Мы ж не Кижи строим, чтоб все без гвоздей и только топором. Иначе получается карго-культ типа: "я только в сишарп" Как в раге: "кто запоёт, тот лох"
HomoLuden
15.08.2019 00:35В ui динамический язык, duck typing в сочетании сдеклараьивным подходом дают больше профита чем статическая типизация.
Как ui разраб на WPF с 2007 года я вас уверяю, что со статической типизацией во фронте под десктоп тоже не сладко. Довольно много сил отнимают церемонии с полиморфизмом, интерфейсами и согласованиями зависимостей. А уж как вы будете писать на шарпе ui я даже представить боюсь.
Было у меня щастье от индусов разных национальностей в поддержку получать WinForms файл главного окна на 25 килострок. Ужосна хххх. Дай то б-г чтоб вам такое не довелось попробовать.
shaukote
12.08.2019 12:14Прозвучит для кого-то ужасно, но есть области, где это совершенно неважно.
Типичный пример — разработка внутрикорпоративных решений. Браузер там используется просто как платформа для запуска приложений. Пользователи открывают "сайт" один раз в день (если не раз в неделю) и, как правило, не закрывают.
Другой пример — оболочки некоторых (за все не скажу) сенсорных терминалов, которые можно увидеть во многих ТЦ и магазинах, работают как веб-приложения. Опять же, такие приложения запускаются один раз (на старте терминала), их размер и скорость инициализации мало кого интересует.
Хорошо это или плохо, но Web очень быстро (и уже давно) растёт и расширяется, так что он давно не ограничивается тем, что мы видим в процессе "веб-сёрфинга". :)
some_x
12.08.2019 13:44Во первых в комментариях выше уже отметили что на самом деле не обязательно 20 мегабайт.
Во вторых это очень молодая технология, наверняка вопрос с размером будет решаться в будущих релизах.HomoLuden
15.08.2019 00:52Даже в .net core ещё вроде не научились стрипать сборки, чтоб облегчить дистриб и потребление памяти (все сборки грузятся целиком). Поправьте меня если неправ.
Если научат CLR стрипать сборки, тогда может и облегчится. А пока они идут по пути сервелата. Пытаются оптимизировать код в сборках, чтоб они были меньше. Сбрасывают жир в ущерб функциональности.
HomoLuden
15.08.2019 00:19Один язык — это все фигня. Меня ни в жисть не заставишь на шарпе писать вебовский фронт. Хватит… Надрался я этого долбаного Razor в WebForms с треклятыми постбэками.
Для меня разор… Тьфу… Блазор суть удачное сочетание недостатков WebForms и Silverlight. И предательство M$ с SL/WPF я не забуду. Вместо исправления графического стэка для подъёма производительности они пилят новый сервелатФормз.
ПыСы: я на WPF с 2007 года (а может даже чуть раньше, не помню уж) и для меня VueJS значительно соблазнительные.
mayorovp
15.08.2019 06:48Э-э-э, это вы вообще о чём? Какое отношение имеет Razor к WebForms и постбэкам?
HomoLuden
15.08.2019 12:27Да вы правы. Я смешал две техники. И происходит это потому что у них есть общее. Смешивание декларативки с кодом c#. А разница между <% и @ уже несущественна. Смешивание разных парадигм в одном файле — гадство.
mayorovp
15.08.2019 12:43Там разница не между
<%
и@
, а между коряво реализованным компонентным подходом и хорошо реализованной шаблонизацией.
Что же до смешивания парадигм — не вижу никакой особой императивности в цикле foreach пока внутри у него нормальная декларативная разметка. Конечно, это несколько уменьшает возможности постобработки — но для серверной генерации html большего и не требуется.
Kanut
15.08.2019 08:57Вы мне столько написали, отвечу в одном месте.
На тему статической типизации во фронте… Я сам и WebForms писал в своё время и Wpf сейчас пишу и честно говоря вообще никакой проблемы в статической типизации во фронте не вижу. Я ей даже рад, поскольку с ней всeвозможные предшественники, коллеги, подчинённые и авторы сторонних библиотек могут мне нагадить гораздо меньше чем без неё. И писать UI на C# я буду совершенно спокойно, используя например MVVM паттерн. Опять же не вижу вообще никаких причин почему в Wpf это работает, a в Blazor вдруг перестанет.
Дальше перейдём к Razor. Проблема Razor не в том, что он использует C#, а в том что запилен исключительно под MVC паттерн. И C# код там выполняется исключительно в момент генерации вьюшки. Замените в Razor C# на JS или любой другой язык программирования и ничего не изменится. Так что скорее всего просто вам не надо было брать Razor потому что MVC не подходил под ваши задачи.
Ну и самое главное насчёт GAC в кэше браузера при «смене C#». Это вообще какая-то дичь. Я вас наверное удивлю, но Microsoft наконец-то научился кросс-рантайму. Для этого и был придуман .NET-Standard. У наc инфраструктура состоит из .Net Framework(C# 7) и .NetCore(C# 8) и мы спокойно пишем под них общие проекты/библиотеки. И если добавиться какой-нибудь .NetWeb или .NetBlazor рантайм, то если он будет совместим с .NET-Standard 2.0 или выше(а я не вижу почему это должно быть иначе), то я смогу использовать свои библиотеки и там.
И я вообще ожидаю от Blazor что браузер будет тащить мои библиотеки с моего хоста, а рантайм будет тащиться с хоста Microsoft и где-тo кэшится. Аналогично с тем как сейчас хостится JQuery.
Ну или, если уж совсем фантазировать, что браузеры рано или поздно добавят рантайм Blazor в свои сборки.mayorovp
15.08.2019 09:02Проблема Razor не в том, что он использует C#, а в том что запилен исключительно под MVC паттерн
На самом деле нет: кроме ASP.NET MVC существовала ещё и ASP.NET WebPages, где запрос напрямую попадал к cshtml-странице. Более того, такой режим использования Razor до сих пор поддерживается в ASP.NET Core.
Kanut
15.08.2019 09:08Ок, mea culpa, совсем вырезал это из памяти :) Но если человек на этом пытался веб делать, то тогда мне его боль вполне понятна :)
HomoLuden
15.08.2019 09:22-1Дальше перейдём к Razor. Проблема Razor не в том, что он использует C#, а в том что запилен исключительно под MVC паттерн. И C# код там выполняется исключительно в момент генерации вьюшки. Замените в Razor C# на JS или любой другой язык программирования и ничего не изменится.
С разором проблема главная в НЕудобстве использования. Смешение парадигм и языков. С ним в презентационном слое получается месиво из 4х языков: c#, js, css, html.
Kanut
15.08.2019 09:27+1Ну если следовать такой логике, то вашем Vue получается «мессиво» трёх языков, а в том же Angular опять же четырёх(ну если использовать его так, как вы используете Razor). Так что это для меня не особо аргумент.
И если Blazor будет «работать на трёх языках», а именно C#, css, html, то лично я это переживу :)
HomoLuden
15.08.2019 09:26На тему статической типизации во фронте… Я сам и WebForms писал в своё время и Wpf сейчас пишу и честно говоря вообще никакой проблемы в статической типизации во фронте не вижу. Я ей даже рад, поскольку с ней всeвозможные предшественники, коллеги, подчинённые и авторы сторонних библиотек могут мне нагадить гораздо меньше чем без неё.
Рукожопы (профессиональные) могут нагадить изрядно и с тем и с другим. Это вопрос культуры проганья, тимлидинга и ревью.
Kanut
15.08.2019 09:32+1Ага, ну и расскажите мне как при помощи «культуры проганья, тимлидинга и ревью» я например должен разгребать кривой легаси-проект, написанный кем-то, кого я даже на фирме не застал :)
HomoLuden
15.08.2019 00:20Язык будет один, но подходы все равно в корне отличаются. С блазором бэкендеры будут шурупы забивать молотком и наждачной бумагой полировать поверхность воды.
gr2y
13.08.2019 00:06Откуда 21мб взялся?
В презентации NDC от 10 июля Steve Sanderson говорит про 2,3Мб для рантайма.VanquisherWinbringer Автор
14.08.2019 00:37Ну можете скачать оба примера, запустить в браузере и посмотреть сколько каждый из них накачал всего файлов. У меня 21 и 1.7 мегабайта получилось. Может дело в том что я только в дебоше зпускал.
HomoLuden
15.08.2019 00:54А потом вслед за рантаймом ещё летят сборки второстепенные. Я тоже видел видео с вкладкой Network в хроме.
NewDevLab
14.08.2019 00:27я правильно понял, что к DOM только байдинг через JS?
VanquisherWinbringer Автор
14.08.2019 00:38Вымысли привязка значений? Не стал особо углубляться по этому поводу.
boolive
Интересно зачем микрософт это придумал
YuryZakharov
WebAssembly же.
AgentFire
я не особо в курсе, когда там уже можно будет полноценно писать на шарпе под браузер?
roboter