Привет, Хабр!

Таки да, скоро выходит 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

Модели размещения


  1. На стороне клиента. Стандартное SPA которое можно раздавать различными способами. В моем примере я использовал шаблон в котором файлы приложения отдает браузеру сервер на asp.net core. Минус этого подхода в тех самых 21 мегабайтах которые нужно скачать браузеру.
  2. На стороне сервера. Все происходит на сервере, а клиенту через сокеты передается готовый 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; }
    }
}

  1. Так как это компонент нам тут не нужны "@page" и "@layout" потому что он не будет участвовать в маршрутизации а лайаут он будет использовать от родительского компонента
  2. С символа @ начинается C# код. Собственно так же как и в Razor
  3. @onclick=@(()=>ClickRow(item.Id))
    Привязывает событие нажатия на строку к методу ClickRow нашей ViewModel
  4. Указываем какие параметры будут передаваться из родительского компонента или страницы в наш с помощью атрибута [Parameter]
  5. Вызываем функцию обратного вызова которую получили из родительского компонента. Так родительский компонент узнает что в дочернем произошло какое-то событие. Функции можно передавать только завернутыми в EventCallback<> параметризованный EventArgs. Возможный список EventArgs можно посмотреть тут — docs.microsoft.com/ru-ru/aspnet/core/blazor/components?view=aspnetcore-3.0#event-handling
  6. Так как список возможных типов 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");
        }
    }

  1. Параметр маршрута "/todo/delete/{id:guid}" сюда передаться Guid если мы перейдем например по адресу localhost/todo/delete/ae434aae44...
  2. Инжектим сервисы из DI контейнера в нашу VM.
  3. Просто свойство нашей VM. Ее значение мы устанавливаем сами, какое хотим.
  4. Это метод вызывается автоматически при инициализации страницы. Тут мы устанавливаем нужные значения для свойств нашей VM
  5. Метод нашей VM. Мы можем привязать его например к событию нажатия какой нибудь кнопки нашей View
  6. Переход на другую страницу которая находиться по адресу "/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>
    }
    

    1. Указываем что эта страна будет доступна по адресу {корневой адрес нашего сайта} +"/todo/delete/"+{какой то Guid}. Например localhost/todo/delete/ae434aae44...
    2. Указываем что наша страница будет рендериться внутри MainLayout.razor
    3. Указываем что наша страница будет использовать свойства и методы класса DeleteTodoViewModel
    4. Узказываем что при нажатии на эту кнопку будет вызываться метод 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();
            }
        }
    

    1. Поддерживаются стандартные атрибуты валидации из System.ComponentModel.DataAnnotations. Конкретно тут мы указываем что это поле обязательное и тот текст который будет отображаться если пользователь не укажет значение в том input который будет связан с этим полем.
    2. Метод для обработки события с параметром. Этот метод будет обрабатывать событие из дочернего компонента
    3. Приводем аргумент к типу который мы создали в дочернем компоненте

    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>
    

    1. Вызываем дочерний компонент и передаем ему в качестве параметров свойства и методы нашей VM.
    2. Встроенный компонент формы с валидацией данных. Указываем в нем что в качеств е модели он будет использовать нашу VM и при отправке валидных данных он будет вызывать ее метод Create()
    3. Валидация будет выполняться с помощью атрибутов модели вроде [Requared] и т.п.
    4. Здесь буду отображаться общие ошибки валицадии
    5. Создаст input с валидацией. Список возможных тегов — InputText, InputTextArea, InputSelect, InputNumber, InputCheckbox, InputDate
    6. Здесь будут отображаться ошибки валидации для свойства public string NewTodo{get;set;}
    7. При нажатии на эту кнопку будет вызываться событие 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)


  1. boolive
    12.08.2019 02:22

    Интересно зачем микрософт это придумал


    1. YuryZakharov
      12.08.2019 09:28

      WebAssembly же.


      1. AgentFire
        12.08.2019 13:07

        я не особо в курсе, когда там уже можно будет полноценно писать на шарпе под браузер?


        1. roboter
          12.08.2019 15:02

          полноценно
          понятие растяжимое, а так уже!


  1. shaukote
    12.08.2019 03:03

    Если на Vue.js SPA вести 1.7 мегабайт, то точно такое же на Blazor 21 мегабайт.

    Кхм, а есть какое-то примерное понимание, откуда там такие размеры-то? Очень хочется пошутить про то, что он тянет с собой весь CLR, но это уж явно не имеет отношения к реальности.


    1. mayorovp
      12.08.2019 09:03

      Тем не менее, это именно так. А вы видите какой-то другой вариант запуска управляемого кода?


      1. shaukote
        12.08.2019 11:55

        А, точно, C# же managed… Я было думал, что оно полностью компилируется в WebAssembly, без использования CLR в run-time.


        Впрочем, если я правильно понимаю, с появлением GC integration в WebAssembly, можно ожидать отказ от CLR и уменьшения размеров сборки. Впрочем, не очень понятно, когда это ещё будет.


    1. some_x
      12.08.2019 13:40

      На сколько я знаю так и есть. На данном этапе решили не заморачиваться с компиляцией IL в WASM, вместо этого скомпилировали сам .NET в WASM, который интерпретирует обычный IL.


      1. shaukote
        12.08.2019 14:28

        Я правильно понимаю, что прикладной C#-код в итоге не компилируется в WebAssembly, а прислылается на клиент в IL и уже там исполняется внутри CLR?


        1. some_x
          12.08.2019 14:59

          Да


    1. VanquisherWinbringer Автор
      12.08.2019 14:30

      Ну на Rust и Yew (Фреймворк под веб ассембли на Rust) приложение весит почти столько же сколько и на Vue так что таки да, дело в рантайме который Blazor тащит с собой.


      1. shaukote
        12.08.2019 15:46

        А вот это, кстати, любопытно уже с другой стороны. :)
        Если Rust-приложения не тянут собой лишний рантайм и GC, то почему они не весят меньше?


        Вроде как резонно предплоложить (да, я знаю, что хожу по тонкому льду), что логика там плюс-минус та же, что и во Vue-приложениях (или любых других JS-приложениях), но при этом как минимум байт-код должен быть более компактен, чем синтаксис JS.


        1. mayorovp
          12.08.2019 15:50

          Они именно что тянут с собой лишний рантайм.


        1. Whuthering
          12.08.2019 16:36

          они тащат с собой стандартную библиотеку, например)


  1. shaukote
    12.08.2019 03:06

    Все происходит на сервере, а клиенту через сокеты передается готовый DOM. Браузеру вообще почти ничего не надо скачивать в начале, но зато вместо этого постоянно по кускам скачивать обновленный DOM.

    Хммм, да поправит меня yelbota, но что-то мне это очень сильно напоминает… :)


    1. yelbota
      12.08.2019 11:05

      Да, обсуждали сходство в комментах к моему посту. Непонятен только вес рантайма для сервер-сайд режима. Если он весит мегабайты как Vaadin, то это не имеет особого смысла. Королев специально сделан так чтобы весить минимально.


  1. yurickas
    12.08.2019 06:23

    Месяц отдал на эту интересную штуку. Понравилось, но сыровато. Заявленного рантайма нету, дебаг пробовал запустить и ничего не вышло. Можно делать очень интересные вещи, до которых ангуляр и его братья не дотянутся. Например автоматическое построение формы на основе атрибутов вьюмодели.
    Про 21 мб автор немного пошутил. Если правильно поставить зависимости, чтобы клиент не тянул серверные сборки, то получается в разы меньше и работает быстрее. Года через 2-3 сие чудо уже будут требовать в стеке разработчика, так что изучать можно сейчас


    1. mayorovp
      12.08.2019 09:06

      А в чём проблема сделать автоматическое построение формы по атрибутам вью-модели на Ангуляре?


      1. alexs0ff
        12.08.2019 09:41

        Более того, в 8 версию разработчики добавили описание такой возможности в официальную документацию


  1. kekekeks
    12.08.2019 09:23

    Silverlight-ом оно станет, когда будет что-то типа этого: http://testapp.keks-n.net/ (потыкайтесь для интереса инспектором). А пока это обычный веб-фреймворк.


    1. LootKeeper
      12.08.2019 14:33

      Вопрос, а почему оно лагает то так?


      1. kekekeks
        12.08.2019 18:12

        Пушто
        1) сделано левой ногой за два дня с целью проверки концепта и сбора граблей
        2) Mono в режиме интерпретатора в целом весьма тормознутое


        По результатам: инфраструктура для работы в WASM в дотнетах пока очень сырая, отладка отсутствует в принципе, стоит подождать ещё год перед очередной попыткой портирования.


  1. win32nipuh
    12.08.2019 10:02
    -1

    Blazor + NET Core 3 в развитии, надо изучать.
    Но, причитав частушки типа «Больше тудушек богу тудушек!» становится странно, автор лучше бы ошибки исправил в тексте, если в состоянии, чем такое писать.
    Например,
    Все регистрируется в Startap.cs [запятая] как в обычном asp.net core приложении. Тут ничего нового. А вот внедрение зависимостей для нашей VM таки происходит через публичные свойства [запятая] а не через кноструктор. Свойство просто нужно декорировать отрибутом [<-ОтрЕбутом]


  1. Matisumi
    12.08.2019 11:14
    -1

    Вот только у меня такое чувство, что нет особого смысла учить эту костыльную штуку, когда есть Vue, Angular и прочее? Есть ощущение, что через пару лет ее выпилят как и Сильверлайт в свое время.


    1. mayorovp
      12.08.2019 11:24

      Сервелат выл выпилен не сам по себе, а вместе с NPAPI. На тех же браузерах, которые этот NPAPI всё ещё поддерживают — Silverlight работает до сих пор.


      Но я пока что не могу представить ситуацию, в которой браузеры точно так же дружно отказываются от WebAssembly.


      1. HomoLuden
        15.08.2019 00:11

        А по моим наблюдениям сервелат начали прибивать по политическим соображениям, когда никаких технических препятствий не было. Он мне как технология напомнил песню Бритни: I'm not a girl. Not yet a woman.


        В сравнении с WPF он был неудобен и менее функционален. Плюс неудобная модель дистрибьюции и ещё больше проблем сперфомансом, чем у WPF.


    1. Kanut
      12.08.2019 11:29
      +1

      Если эта «костыльная штука» будет работать хотя бы приблизительно так хорошо как «Vue, Angular и прочее», то учить её очень даже стоит. Так как она позволяет использовать один и тот же язык и одни и те же библиотеки для фронта и бэка. И это очень многого стоит. Как разработчик ты экономишь кучу времени, которое в том же Angular уходит на дублирование кода. Как фирма ты экономишь кучу денег и времени, так как твоим разработчикам нужно учить на один язык программирования меньше.

      То есть на мой взгляд если Microsoft не учудит каких-либо глупостей, то эта штука «взлетит». И я лично с удовольствием на неё перейду годика через два-три.


      1. Matisumi
        12.08.2019 11:51

        > Как разработчик ты экономишь кучу времени

        А как пользователь мне понравится тащить в 20 раз больше мегабайт?


        1. Kanut
          12.08.2019 11:56

          А с чего вы решили что вы обязательно будете «тащить в 20 раз больше мегабайт»? Если я всё правильно понимаю что в случае «серверной версии» там не сильно много лишнего и тащить то надо будет.
          И как-будто всякие Javascript/Typescript/Angular/… библиотеки, которые тащатся сейчас, совсем ничего не весят :)


          1. shaukote
            12.08.2019 14:51

            И как-будто всякие Javascript/Typescript/Angular/… библиотеки, которые тащатся сейчас, совсем ничего не весят :)

            Берём (для примера) самый мейнстримовый (и при этом весьма раздутый, если по-честному) стек: React (+ ReactDOM) + Redux + Reselect + Redux-Saga.
            Суммарно получаем ~139 kB minified или ~46 kB minified + gzipped.
            Даже если накинуть ещё столько же на прикладную логику (хотя это будет уже явно не уровень todo-приложения), Blazor'у ещё расти и расти в этом плане.


            1. Kanut
              12.08.2019 14:58

              А если добавить туда какую-нибудь библиотечку для формочек/контролей и прочего? Сколько тогда будет? И с React я лично не работал, но если брать Angular(a особенно если Angular Elements какие-нибудь) да ещё и с каким-нибудь Kendo, ещё парочку других библиотек добавить, да ещё картиночек, да ещё… то уже пойдут мегабайты. И это не то чтобы абсолютно исключение для веб-страничек…


              1. 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'а, которые его разработчикам получится преодолеть.


                1. mayorovp
                  12.08.2019 15:41

                  С производительностью там как раз всё в порядке: это ж не чистая интерпретация, а JIT-компиляция статически типизированного языка.


                  Проблемы тут возможны с долгим стартом и с размером.


                  1. shaukote
                    12.08.2019 15:51

                    То есть у нас JIT-компиляция IL внутри CLR, который работает в WebAssembly VM c JIT? :)


                    1. mayorovp
                      12.08.2019 15:56

                      … и в чём проблема?


                      1. shaukote
                        12.08.2019 16:16

                        В том, что современный JS компилируется в байт-код на старте приложения и дальше JIT-компилируется нативным JS-движком.


                        А в случае C# добавляется дополнительная прослойка в виде CLR, что "вызывает вопросы касательно производительности".


                        1. Tangeman
                          12.08.2019 17:38

                          А в случае C# добавляется дополнительная прослойка в виде CLR, что «вызывает вопросы касательно производительности».

                          Не факт. Там есть AOT (вероятно, ещё не полностью допиленный), т.е. в браузер уже уйдёт оптимизированный wasm-код (без IL), который, в свою очередь, ещё может быть JIT-компилирован самим браузером (как и любой другой).


                        1. mayorovp
                          12.08.2019 18:02

                          Вот только JS, будучи языком с динамической типизацией, испытывает проблемы с оптимизацией полиморфных функций. От чего, к слову, страдают в первую очередь фреймворки — ведь именно у них зачастую внутри содержится код, которому приходится пропускать через себя значения совершенно разных типов.


                          А что MSIL что WASM являются полностью статически типизированными, что позволяет результирующему машинному коду не делать никаких проверок типа в рантайме.


              1. HomoLuden
                15.08.2019 00:28

                В вебфреймворках размер бандла распухает подключением Materials Design и прочих нахлобучек, которых блазоре нет также. Тут ничья. А вот .net runtime не стрипается вообще. Это фундаментальный косяк, что с приложением в память грузятся целиком сборки. Так же и в блазоре щас. Через сеть тянется весь рантайм. Изменится сишарп твоей логики и рантайм заново будет сосаться или браузер сможет в глобальный кэш положить рантайм (GAC в браузере)?


            1. VanquisherWinbringer Автор
              12.08.2019 14:59

              Как я уже выше говорил приложения на Rust весят почти столько же как и под JS с фреймворками так что тут дело в рантайме. Как только запилят встроенный в WASM GC и оптимизируют сборку чтобы в ней был только нужный код то будет и Blazor весит меньше мегабайта. Таки да технология молодая от слова совсем. Имхо — задавит JS фреймворки со временем потому что большинству молодых программистов просто лень учить JS. Вы не представляете себе силу лени. Лет через 5 — 6 когда примерно произойдет смена поколений.


              1. shaukote
                12.08.2019 15:31

                Имхо — задавит JS фреймворки со временем потому что большинству молодых программистов просто лень учить JS. Вы не представляете себе силу лени.

                На мой взгляд, с учётом того, сколько нынче JS-разрабочиков, людей, активно изучающих JS (и пойдущих в JS-разработчики через год-другой), ну и уже написанного JS-кода, "сила лени" это скорее аргумент в противоположную сторону. :)


                Хотя не уверен, какой смайл здесь более уместен. :(


                1. Kanut
                  12.08.2019 16:26

                  Я работаю с JS уже долго и лично я им очень недоволен. TS уже лучше, но всё ещё унаследовал некоторые «болезни» JS. Если лично мне дадут возможность писать фронтэнд на языке вроде C#, то я буду танцевать от радости :)

                  П.С. И да на JS/TS тоже можно работать «правильно» и писать «чистый» код. Но проблема в том что далеко не всегда работаешь только со своим кодом и тут начинаются заморочки.


                  1. shaukote
                    12.08.2019 16:35

                    Я же и не спорю с такой точкой зрения. :)
                    Но тем не менее индустрия весьма инертна, есть много людей, которые вложили свои силы и время в изучение JS (и очень многим из них весьма комфортно с этим языком), много компаний, которые вложили большие деньги в JS (от Google, вложивший огромное количество сил в свой V8, до продуктовых компаний, выстроивших продукт вокруг JS-стека).


                    1. Kanut
                      12.08.2019 16:44
                      +1

                      Компании понять можно. Но обычно если выясняется что на новом стэке можно экономить время и деньги, то они не долго тянут с переходом :)
                      А насчёт людей, которые вложили время в изучение JS… Ясное дело что люди, которые знают JS и не знают C#, в большинстве своём останутся на JS. А вот люди, которые знакомы с JS и C#(а таких на мой взгляд достаточно много), на мой взгляд потихоньку будут избавлятся от JS :)


                      1. shaukote
                        12.08.2019 17:26

                        люди, которые знакомы с JS и C#(а таких на мой взгляд достаточно много)

                        Всё очень зависит от личного опыта, я вот за всю свою жизнь ни одного живого C#-разработчика не видел (кроме себя разве что, но я не настоящий сварщик). ?_(?)_/?
                        Что конечно же совсем не означает, что их нет или мало.


                        обычно если выясняется что на новом стэке можно экономить время и деньги, то они не долго тянут с переходом

                        Ох, это очень сложный вопрос.
                        Тут и количество/запросы разработчиков на рынке нужно учитывать, и вопрос стоимости миграции существующих решений.
                        Да и JS сегодня позволяет достаточно (окей, более-менее) эффективно писать код под практически все платформы.
                        Я вот не готов сходу делать такие смелые оценки, что будет для сферического в вакууме бизнеса выгоднее.


                        off-topic

                        По моим воспоминаниями, лет пять назад все дружно похоронили Ruby и RoR. Я вот вообще не вижу причин сегодня писать на Ruby.
                        Однако ж не очень похоже что этот стек умер. :)


                        1. HomoLuden
                          15.08.2019 00:48

                          Пример:
                          Visual Studio против VS Code.
                          Функционал для рядового девелопмента у Code не намного скуднее. Даже иногда наоборот.В Code можно отлаживать Xamarin Android на реальном телефоне, а с VS хз как… Я не нашел способа подрубить дебаггер.


                          Code написана на js и летает
                          VS написана на убогом старом VS Shell (c++) а поверх натянут WPF. Топмозит, жрет памяти вагон, и размер установки с нужными тулзами под 30ГБ на системном диске.


                      1. HomoLuden
                        15.08.2019 00:41

                        Я знаю и то и другое на высоком уровне. Ни в жисть не буду с js слазить. Хватило мне с головой winforms и webforms. Если на шарпе, только wpf с XAML.


                    1. HomoLuden
                      15.08.2019 00:39

                      Нормальный инж должен под конкретную задачу осваивать подходящие инструменты. Мы ж не Кижи строим, чтоб все без гвоздей и только топором. Иначе получается карго-культ типа: "я только в сишарп" Как в раге: "кто запоёт, тот лох"


                  1. HomoLuden
                    15.08.2019 00:35

                    В ui динамический язык, duck typing в сочетании сдеклараьивным подходом дают больше профита чем статическая типизация.
                    Как ui разраб на WPF с 2007 года я вас уверяю, что со статической типизацией во фронте под десктоп тоже не сладко. Довольно много сил отнимают церемонии с полиморфизмом, интерфейсами и согласованиями зависимостей. А уж как вы будете писать на шарпе ui я даже представить боюсь.


                    Было у меня щастье от индусов разных национальностей в поддержку получать WinForms файл главного окна на 25 килострок. Ужосна хххх. Дай то б-г чтоб вам такое не довелось попробовать.


        1. shaukote
          12.08.2019 12:14

          Прозвучит для кого-то ужасно, но есть области, где это совершенно неважно.


          Типичный пример — разработка внутрикорпоративных решений. Браузер там используется просто как платформа для запуска приложений. Пользователи открывают "сайт" один раз в день (если не раз в неделю) и, как правило, не закрывают.


          Другой пример — оболочки некоторых (за все не скажу) сенсорных терминалов, которые можно увидеть во многих ТЦ и магазинах, работают как веб-приложения. Опять же, такие приложения запускаются один раз (на старте терминала), их размер и скорость инициализации мало кого интересует.


          Хорошо это или плохо, но Web очень быстро (и уже давно) растёт и расширяется, так что он давно не ограничивается тем, что мы видим в процессе "веб-сёрфинга". :)


        1. some_x
          12.08.2019 13:44

          Во первых в комментариях выше уже отметили что на самом деле не обязательно 20 мегабайт.
          Во вторых это очень молодая технология, наверняка вопрос с размером будет решаться в будущих релизах.


          1. HomoLuden
            15.08.2019 00:52

            Даже в .net core ещё вроде не научились стрипать сборки, чтоб облегчить дистриб и потребление памяти (все сборки грузятся целиком). Поправьте меня если неправ.
            Если научат CLR стрипать сборки, тогда может и облегчится. А пока они идут по пути сервелата. Пытаются оптимизировать код в сборках, чтоб они были меньше. Сбрасывают жир в ущерб функциональности.


      1. HomoLuden
        15.08.2019 00:19

        Один язык — это все фигня. Меня ни в жисть не заставишь на шарпе писать вебовский фронт. Хватит… Надрался я этого долбаного Razor в WebForms с треклятыми постбэками.
        Для меня разор… Тьфу… Блазор суть удачное сочетание недостатков WebForms и Silverlight. И предательство M$ с SL/WPF я не забуду. Вместо исправления графического стэка для подъёма производительности они пилят новый сервелатФормз.


        ПыСы: я на WPF с 2007 года (а может даже чуть раньше, не помню уж) и для меня VueJS значительно соблазнительные.


        1. mayorovp
          15.08.2019 06:48

          Э-э-э, это вы вообще о чём? Какое отношение имеет Razor к WebForms и постбэкам?


          1. HomoLuden
            15.08.2019 12:27

            Да вы правы. Я смешал две техники. И происходит это потому что у них есть общее. Смешивание декларативки с кодом c#. А разница между <% и @ уже несущественна. Смешивание разных парадигм в одном файле — гадство.


            1. mayorovp
              15.08.2019 12:43

              Там разница не между <% и @, а между коряво реализованным компонентным подходом и хорошо реализованной шаблонизацией.


              Что же до смешивания парадигм — не вижу никакой особой императивности в цикле foreach пока внутри у него нормальная декларативная разметка. Конечно, это несколько уменьшает возможности постобработки — но для серверной генерации html большего и не требуется.


        1. 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 в свои сборки.


          1. mayorovp
            15.08.2019 09:02

            Проблема Razor не в том, что он использует C#, а в том что запилен исключительно под MVC паттерн

            На самом деле нет: кроме ASP.NET MVC существовала ещё и ASP.NET WebPages, где запрос напрямую попадал к cshtml-странице. Более того, такой режим использования Razor до сих пор поддерживается в ASP.NET Core.


            1. Kanut
              15.08.2019 09:08

              Ок, mea culpa, совсем вырезал это из памяти :) Но если человек на этом пытался веб делать, то тогда мне его боль вполне понятна :)


          1. HomoLuden
            15.08.2019 09:22
            -1

            Дальше перейдём к Razor. Проблема Razor не в том, что он использует C#, а в том что запилен исключительно под MVC паттерн. И C# код там выполняется исключительно в момент генерации вьюшки. Замените в Razor C# на JS или любой другой язык программирования и ничего не изменится.

            С разором проблема главная в НЕудобстве использования. Смешение парадигм и языков. С ним в презентационном слое получается месиво из 4х языков: c#, js, css, html.


            1. Kanut
              15.08.2019 09:27
              +1

              Ну если следовать такой логике, то вашем Vue получается «мессиво» трёх языков, а в том же Angular опять же четырёх(ну если использовать его так, как вы используете Razor). Так что это для меня не особо аргумент.
              И если Blazor будет «работать на трёх языках», а именно C#, css, html, то лично я это переживу :)


          1. HomoLuden
            15.08.2019 09:26

            На тему статической типизации во фронте… Я сам и WebForms писал в своё время и Wpf сейчас пишу и честно говоря вообще никакой проблемы в статической типизации во фронте не вижу. Я ей даже рад, поскольку с ней всeвозможные предшественники, коллеги, подчинённые и авторы сторонних библиотек могут мне нагадить гораздо меньше чем без неё.

            Рукожопы (профессиональные) могут нагадить изрядно и с тем и с другим. Это вопрос культуры проганья, тимлидинга и ревью.


            1. Kanut
              15.08.2019 09:32
              +1

              Ага, ну и расскажите мне как при помощи «культуры проганья, тимлидинга и ревью» я например должен разгребать кривой легаси-проект, написанный кем-то, кого я даже на фирме не застал :)


      1. HomoLuden
        15.08.2019 00:20

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


  1. gr2y
    13.08.2019 00:06

    Откуда 21мб взялся?
    В презентации NDC от 10 июля Steve Sanderson говорит про 2,3Мб для рантайма.


    1. VanquisherWinbringer Автор
      14.08.2019 00:37

      Ну можете скачать оба примера, запустить в браузере и посмотреть сколько каждый из них накачал всего файлов. У меня 21 и 1.7 мегабайта получилось. Может дело в том что я только в дебоше зпускал.


    1. HomoLuden
      15.08.2019 00:54

      А потом вслед за рантаймом ещё летят сборки второстепенные. Я тоже видел видео с вкладкой Network в хроме.


  1. NewDevLab
    14.08.2019 00:27

    я правильно понял, что к DOM только байдинг через JS?


    1. VanquisherWinbringer Автор
      14.08.2019 00:38

      Вымысли привязка значений? Не стал особо углубляться по этому поводу.


    1. HomoLuden
      15.08.2019 00:55

      Наверняка через матёрый прослой и бубнотанцы как было в сервелате.