Эта статья - можно сказать продолжение моего развития в программировании. Вот начало моего развития. Советую его прочитать.

Я написал интернет-магазин для магазинов мяса на Blazor Server(это не WebAssembly). Там фактически нет фронта, есть только бекенд. Всё события и изменения в DOM-дереве передаются на сервер через WebSocket и сервер генерирует новый html и шлет обратно. То есть onclick можно обработать прямо из C# и там же скачать, к примеру данные из базы. Это очень удобно, никаких проблем с HTTP запросами! Blazor абстрагирует меня от этого. Кстати, в React 17 появилась така штука как React Server Components, это чисто копия подхода Blazor. Теперь можно написать компонент и с него же обращаться, в базу, например.

Но, поначалу все было очень классно и удобно, что позволило мне быстрее написать интернет-магазин, но потом возникли проблемы.

Например: нужно сделать колесо фортуны, где можно выиграть продукт. Сами подарки можно изменять в программе. Я нашёл готовое колесо, которое рисуется в canvas. Когда оно докручивается, вызывается функция в JS, из неё мне как-то надо передать в C#, что колесо докрутилось и какой продукт выпал человеку. Это делается дико сложно.

Была проблема даже с инициализацией колеса, потому что код вызывался до того как данные скачались с базы и колесо было пустым. Пришлось брать содержимое файла JS локально с сервера и потом, когда данные загрузились, вызывать его из C# c помощью JsInterprop. Короче полный костыль, кстати когда я это делал, я обнаружил серьёзную уязвимость, с помощью которой смог попасть в панель хостинга.

Из-за таких сильных ограничений я решил переписать свой интернет-магазин на React + Node. Вот, я думал, будет классно, это же тренд, удобнее моего C#. Круто! Но, как оказалось, очень многого в JS-инфраструктуре нет или хуже, чем в. NET.

Я уже немного знал про Node. Умел не много. Максимум - это получить post запрос с формы и отобразить ответ, ну и pug минимально. То есть до нормального "hello world" не дотягивает. Много читал про React и его безграничные возможности.

Ну что же, начинаем! Запустил Visual Studio Code, создал проект. Так..СТОП. Где подсказки? Почему не работает Intellisense? Ах.. Это же JS. Спас Microsoft с их Typescript-ом, был доволен работающим intellisense.

Бекенд

React начально освоил легко. Стал делать сервер. Стандартно express. Хм.. Как же работать с базой(MS SQL Server). Не напрямую же? Так, ищу ORM. Что там у нас есть? Какие-то легенькие bookshelf, waterline, в которых не даже миграций. Что осталось? Sequelize с ужастным синтаксисом и без active record? О! TypeORM. Эта ORM была создана наподобие NHibernate и Entity Framework. Это же то, что мне нужно!

Сразу стало неудобно. Вот как я делаю запрос в Entity Framework Core:

User FoundUser = new Context().Users.Where(user => user.Name == "Вася")
  							.FirstOrDefault();

Вот так это делается в TypeORM:

const found_user = await connection
    .getRepository(User)
    .createQueryBuilder("user")
    .where("user.name = :name", { id: "Вася" })
    .getOne();

Как же это неудобно. В случае TypeORM, IDE не подскажет мне, что я неправильно написал название свойства, я узнаю это уже во время выполнения потому, что в случае JS - это просто строка для компилятора, а в случае C# - это лямбда выражение, которое принимает параметр user с типом User, и оно мне подскажет название свойства, а если я ошибусь, то я даже скомпилировать не успею, как IDE мне подскажет.

Особенно с увеличением сложности запроса это помогает еще больше.

Это все из-за того, что в C# есть гениальная штука: Деревья выражений. С помощью нее можно разобрать обычное лямбда-выражение и преобразовать его в SQL запрос! В JS такого, к сожалению, нет. Но это можно пережить, хотя с болью и страданиями.

Теперь настроим миграции. Я создал для примера класс User.

import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";

@Entity()
export class User {

    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    firstName: string;

    @Column()
    lastName: string;

    @Column()
    age: number;
}

Создадим миграцию. Не работает. Так, наверное не так пути к папкам сущностей и миграций прописаны? Попробовал все возможные варианты. Все та же ошибка.

Создам-ка я проект из template-а typeorm. Создаем миграцию. Теперь выдает другую ошибку. Еще спустя несколько дней я смог создать миграцию. Фуух, неделю пытался заставить работать миграции, теперь все позади. А вот и нет. Решил я добавить поле phoneNumber. Делаю миграцию иии... Что я вижу? Изменения не найдены??!! Как? Причем первая миграция создалась без проблем. То есть пути все правильно уже прописаны, это template проект TypeORM. Спустя еще один миллион попыток и созданных проектов, наконец-то! Нашло изменения. Теперь нужно запустить эту миграцию, чтобы она применила изменения к базе. АААА!!! Пишет, что миграции новых нет, хотя только что их сгенерировало! Миграции в TypeORM просто не работают!!! Я перепробовал все, что можно, такая проблема уже есть в issues, я думал, ну ладно, может эта тупая ORM не работает с MS SQL Server, попробовал PostgreSQL - та же фигня. Я уже подумал просто забросить эту ноду и написать сервер на C#. Но тут нашелся выход.

MikroORM - это чудо! Она работает, в отличии от других ORM! Но она не работает с MS SQL Server. Буду теперь на PostgreSQL.

UPD: Кое-что забыл дописать. Оказывается, в Typescript можно менять AST дерево кода и заменить лямбда-выражение на SQL запрос, прямо как в C#. Есть проект для возможности написания Linq-подобных запросов в TypeORM. Называется typeorm-linq-repository

Ещё вспомнил очень большую проблему в ORM в JS(она есть у всех).

Посмотрите, как устроены миграции.

Есть некий класс с методами Up и Down.

Вот как миграции устроены в TypeORM:

import {MigrationInterface, QueryRunner} from "typeorm";

export class PostRefactoringTIMESTAMP implements MigrationInterface {

    async up(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`ALTER TABLE "post" RENAME COLUMN "title" TO "name"`);
    }

    async down(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`ALTER TABLE "post" RENAME COLUMN "name" TO "title"`); // reverts things made in "up" method
    }
}

Вот как выглядит миграция в случае Entity Framework Core:

using Microsoft.EntityFrameworkCore.Migrations;
 
namespace HelloApp.Migrations
{
    public partial class InitialCreate : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Users",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Name = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    Age = table.Column<int>(type: "int", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Users", x => x.Id);
                });
        }
 
        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Users");
        }
    }
}

Разница между ними в том, что в случае TypeORM, миграция - это просто строка с SQL запросом, а в случае Entity Framework Core - это вызовы разных методов. И в чем же разница? А в том, что Entity Framework Core не зависит от базы данных, текущая структура у него есть, а в случае TypeORM, он полностью зависит от базы. Он не может узнать структуру из SQL строк. Он сравнивает текущее состояние в базе данных с тем, что есть в сущьностях, а Entity Framework Core может воспроизвести структуру базы по миграциям и сравнить её с новыми изменениями. То есть в TypeORM я просто не могу создавать миграции без базы! Это неудобно. В Entity Framework Core я могу создавать любые миграции вне зависимости от базы и потом только их применить. В JS я не нашёл ни одной ORM с миграциями, которые бы были независимы от базы данных.

Фронтенд

Теперь перейдем к React. Скажу так, нет ни одного шаблонизатора(я туда отношу и JSX), который хоть немного сравнится по удобству с Razor(шаблонизатором для Blazor).

Давайте теперь сделаем простой todo.

Для Blazor код компонента такой:

<ul class="todo">
	@* Цикл по списку элементов *@
	@foreach(TodoItem TodoItem in TodoItemsList)
	{
		<li class="todo__item">
			@* Сам контент *@
			@TodoItem.Content
			@* Эта кнопка вызывает функцию DeleteTodoItem и передает ей TodoItem агрумент *@
			<button @onclick="(() => DeleteTodoItem(TodoItem))">Удалить</button>
		</li>
	}
</ul>
<div>
	@* Двусторонний биндинг к свойству класса, свойство меняется, если пользователь вводит текст,
	и текст в input меняется, если поменять свойство из метода *@
	<input @bind="ValueToAdd" />
	<button @onclick="AddTodoItem">Добавить</button>
</div>

@code {
	public List<TodoItem> TodoItemsList = new(); // список элементов
	public class TodoItem
	{
		public string Content;
	}
	public void DeleteTodoItem(TodoItem TodoItem)
	{
		TodoItemsList.Remove(TodoItem);
	}
	public string ValueToAdd;
	public void AddTodoItem()
	{
		TodoItemsList.Add(new TodoItem
		{
			Content = ValueToAdd
		});
		ValueToAdd = "";
	}
}

Я могу писать настоящий C# в Razor! Циклы - пожалуйста, условное ветвление - пожалуйста, и даже двусторонний биндинг.

Возьмем пример с React.

import React, {useState} from "react";

function Todo({ todo, index, removeTodo }) {
  return (
    <div className="todo"/>
      {todo.text}
      <div>
        <button onClick={() => removeTodo(index)}>Удалить</button>
      </div>
    </div>
  );
}

function TodoForm({ addTodo }) {
  const [value, setValue] = useState("");

  const addItem = () => {
    addTodo(value);
    setValue("");
  };

  return (
  	<input
        type="text"
        className="input"
        value={value}
        onChange={e => setValue(e.target.value)}
      />
  	<button onClick={addItem} />
  );
}

function App() {
  const [todos, setTodos] = useState([]);

  const addTodo = text => {
    const newTodos = [...todos, { text }];
    setTodos(newTodos);
  };

  const removeTodo = index => {
    const newTodos = [...todos];
    newTodos.splice(index, 1);
    setTodos(newTodos);
  };

  return (
    <div className="app">
      <div className="todo-list">
        {todos.map((todo, index) => (
          <Todo
            key={index}
            index={index}
            todo={todo}
            removeTodo={removeTodo}
          />
        ))}
        <TodoForm addTodo={addTodo} />
      </div>
    </div>
  );
}

export default App;

Сразу заметно, что код React в 1.5 раза больше. Также, он значительно сложнее для понимания. А в Blazor можно еще вынести блок @code в отдельный файл и отделить логику от представления(конечно не полностью, но это лучше, чем все вместе)

Код React очень усложняет, то что в JSX нельзя сразу внедрить код JS, нужно обернуть это в фигурные скобки и там внутри вернуть JSX, это неудобно. В Razor это сделано гораздо лучше.

В JSX нет двустороннего биндинга, нужно писать className, а не просто class, что очень раздражает. Еще есть Angular и Vue, но там шаблонизаторы тоже не такие мощные, краткие и понятные как Razor.

Что меня бесило больше всего, так это работа с данными.

В Blazor можно легко сделать DI контейнер (в Angular тоже), в React же все построено вокруг паттернов CQRS, Event Sourcing, на бекенде эти паттерны используют только в сложных проектах, поскольку для простого проекта это принесет только вред, в React же этот подход пиарят сразу.

Есть еще Modx, но если я не ошибаюсь, это тот же Redux, только с сахаром в виде ООП да и сейчас Redux - де-факто стандарт. У меня возникает, как React, Redux, и прочие библиотеки стали так сильно популярны и почему мне рекламируют их, если они неудобные, непрактичные, или ну ооочень многословные?

Окончание

Сейчас, спустя 2 месяца, 1 месяц из которых я пытался найти нормальную ORM, мой новый интернет-магазин умеет только создать временного пользователя в базе(для хранения корзины в базе, а не в LocalStorage), с cookies и создание session-key я делал сам, и все! А интернет-магазин на Blazor я написал за те же два месяца! И там была моя самодельная CMS(хотя ей в итоге никто не пользовался), автоматическое распределение заказов по магазинам в зависимости от улицы, и также я успел написать дектопную программа для оператора на WPF, где можно смотреть пользователей, экспортировать в Excel, перенаправлять заказы на другую точку...

Blazor показался мне намного легче в изучении, чем React.

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

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

Комментарии (89)


  1. marshinov
    09.09.2021 22:15
    +10

    Добро пожаловать в мир JavaScript. Вы просто испорчены C#. C# - один из лучших прикладных языков сейчас. Посмотрите Kotlin Multiplatform. Bleeding Age, но может вам зайдёт, если есть желание писать на хорошем языке и иметь возможность дергать JS-код


    1. nin-jin
      10.09.2021 00:10
      +8

      Котлин уже научился не тянуть с собой 2 мегабайта рантайма?


      1. some_x
        10.09.2021 04:00
        +4

        Blazor тянет не меньше, но клиентов автора это устраивало, насколько я понял


        1. ApeCoder
          10.09.2021 08:47
          +3

          Я написал интернет-магазин для магазинов мяса на Blazor Server. Там фактически нет фронта, есть только бекенд. Всё события и изменения в DOM-дереве передаются на сервер через WebSocket и сервер генерирует новый html и шлет обратно. 

          Это не blazor webassebly - другая технология


          1. donPedro
            10.09.2021 15:34
            +2

            А там уже исправили ситуацию, что один сервер может поддерживать всего несколько десятков пользователей одновременно, из за того что для каждого пользователя надо его контекст на сервере держать?


            1. ApeCoder
              10.09.2021 17:28
              +1

              Это вопрос к @arthurlomakin - он же на нем что-то делает. Я только теоретически интересуюсь


            1. arthurlomakin Автор
              10.09.2021 20:19
              +1

              At this point, we have actually hit a limitation. With the Azure SignalR Service. As of this post, the max number of SingleR connections a service can have is 100,000 concurrent connections.

              Thus there is a limit of 100,000 concurrent users to a Blazor Server Side application.

              Не интересовался, но оказывается много. Но это на мощном сервере.


              Scale vertically means adding more compute and/or memory to the machine.

              In our example, Microsoft has stated that a 1 core, 3.5 GB memory machine can scale to 5000 concurrent users.

              Note this says for concurrent users, not total users. So if you have a system with 10,000 users, but you have only 1000 on at any given time, you are fine with the machine listed above. By the way, you should know how many concurrent users you have using your system over any given day.

              Upgrade your Azure machine to the P2V3, 4 Core, 16 GB memory, and you can support up to 20,000 concurrent users.

              For a single Azure app service, your application can support up to 20,000 concurrent users. That seems fairly decent.


        1. arthurlomakin Автор
          10.09.2021 12:11
          +2

          ~250 KB. У меня Blazor Server, а не Blazor WebAssembly, там намного больше, как вы и говорите.


      1. marshinov
        10.09.2021 10:46
        +2

        По-моему еще нет. Просто как вариант "и волки сыты, и овцы целы" для автора. В Kotlin Multiplatform ИМХО проще до JS дотянуться, чем из Blazor и сам язык на C# похож, а местами даже интереснее сделан.


    1. arthurlomakin Автор
      10.09.2021 12:34

      Если я испорчен C#, это плохо или хорошо?


      1. qrKot
        10.09.2021 13:20
        +3

        даже не уверен, стоит ли продолжать писать новый интернет-магазин или может поправить код, сделать нормальную архитектуру старого

        Ну вот "испорчен C#" - это значит, что "сделать нормальную архитектуру старого" у вас, скорее всего, не получится на данном этапе.


      1. marshinov
        10.09.2021 13:21
        +1

        Не воспринимайте слишком всерьез;)


  1. Alexufo
    09.09.2021 22:16
    +6

    Надо было мол брать, а не реакт

    https://t.me/mol_memes

    Blazer это же васм. Так можно людей и из геймдева сайты научить писать. Просто в e-commerce все плохо, либо самопис либо ... Битрикс))))

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

    Вы пришли из Шарпа и плюетесь на то что все не такое как Шарп) Уэлкам, избалованным корпоративным софтом разработчикам. !


    1. gressmc
      10.09.2021 09:52

      Либо самопис либо Битрикс - либо АТГ. А это пострашнее будет


    1. arthurlomakin Автор
      10.09.2021 11:21

      Есть Blazor Server, а есть Blazor Webassembly. Я писал на Blazor Server.


  1. vladbarcelo
    09.09.2021 22:35
    +27

    Заходим в документацию TypeORM, смотрим раздел "Working with Repository":

    import {getRepository} from "typeorm";
    import {User} from "./entity/User";
    
    const userRepository = getRepository(User); // you can also get it via getConnection().getRepository() or getManager().getRepository()
    const user = await userRepository.findOne(1);

    А если ещё и инициализировать все репозитории где-то в одном месте, и прокидывать их в сервисы по мере необходимости, то ваш код превратится в одну строчку:

    const user = await userRepository.findOne({ where: { name: 'Вася' } });

    Сходили бы, доку почитали бы ради разнообразия, что ли.


    1. Alexufo
      09.09.2021 23:51
      -8



      nin-jin Дим, у тебя похоже наверняка решение?


      1. nin-jin
        10.09.2021 00:14

        Серверные модули на $mol пока ещё не достаточно мощные, так что я бы рекомендовал сделать API на C#, и клиента на $mol. API в любом случае ещё много для чего пригодится.


    1. symbix
      10.09.2021 03:19
      +3

      Претензии, конечно, так себе, но в итоге выбор в пользу MikroORM был сделан абсолютно верно.


      Если автор ORM прямо в readme заявляет, что «Your models in your app are your database tables» — очевидно, он не понимает, что вообще такое ORM, и делает что-то странное. Дальше можно даже не смотреть.


      1. Alex_ME
        10.09.2021 10:25
        +1

        А что делает ORM? В Entity Framework таблицы базы описывалось C# классами и запросы тоже с ними работали.


        1. symbix
          10.09.2021 15:08
          +1

          Object/Relational Mapping


        1. symbix
          10.09.2021 18:16
          +1

          Я особо не в теме про C#, но навскидку не вижу причин, по которым EF был бы пригоден только для persistence models. Можно, конечно, и так делать, но ORM не должна ограничивать разработчика самым примитивным подходом.


          Вот нагуглил, вроде все окей с EF:


          https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/net-core-microservice-domain-model


      1. arthurlomakin Автор
        10.09.2021 11:53

        Согласен, что не все претензии весомые. Но добавилась весомая проблема независимости миграций в ORM. Дописал в статье.


        1. qrKot
          10.09.2021 13:24
          +3

          ORM - это такая штука, которая а) не всегда нужна, б) иногда вылазит боком.

          Ну и, в целом, стремление переписать фронт на React.JS понять еще можно. Зачем при этом на беке на ноду переползать - вообще не понятно.

          В общем, Blazor вас испортил. Мухи отдельно, котлеты отдельно Фронт связан с беком на уровне API, тчк.


          1. arthurlomakin Автор
            17.09.2021 21:42

            В общем, Blazor вас испортил. Мухи отдельно, котлеты отдельно Фронт связан с беком на уровне API, тчк.

            Но в React зачем-то добавили React Server Components. Чисто копия Blazor.


            Зачем при этом на беке на ноду переползать — вообще не понятно.

            На ноду хочу из-за того, что хочу выучить что-то новое.


    1. motoroller95
      10.09.2021 10:12

      del


    1. BashkaMen
      10.09.2021 11:08
      +1

      но проблемы js все равно не ушли, а ты точно уверен что name там есть?)) а может name это массив байтов?


    1. arthurlomakin Автор
      10.09.2021 11:12

      Я это смотрел. Все равно, если я напишу вместо name nam, то будет все ОК. Хотя я хотел добиться, чтобы IDE мне сообщило об ошибке. Я правда уже нашёл способ. Дописал в статье. Но все равно миграций нормальных нет нигде в JS.


      1. nin-jin
        10.09.2021 11:30

        А что такое "нормальные миграции"?


        1. arthurlomakin Автор
          10.09.2021 11:37

          Дописал в статье. Независимость от базы при создании новой миграции.


          1. nin-jin
            10.09.2021 11:47

            А что скажете про такие миграции? Они норм или нет?


            1. arthurlomakin Автор
              10.09.2021 11:50

              Думаю норм. Только это же для IndexedDB


          1. qrKot
            10.09.2021 13:25
            +3

            Независимость от базы при создании новой миграции

            ... как правило, фикция.


        1. Alexufo
          10.09.2021 04:42
          +1

          так я про тоже, у gwt нельзя было открыть две вкладки сайта, у них видилите стейт! Не знаю как сейчас дела, но может я тогда видел продукт криворуких..(1000% криворуких, арабы) оверхеда я насмотрелася.


  1. anonymous
    00.00.0000 00:00


    1. Alexufo
      10.09.2021 00:05
      +6

      чего это непонятна? GWT хреначит с 2006 года, я думал он умер, а он в 2020 обновился! Это электрон наоборот, там не умеем в десктоп будем писать на js, а тут не умеем в js будем писать на шарпе.


  1. anonymous
    00.00.0000 00:00


    1. js605451
      10.09.2021 00:24
      +5

      Классика же, 10 лет назад вместо Vue просто jQuery был.


    1. Bromka
      10.09.2021 08:43

      А таки почему JS, а не TS? Тем более что и второй, и особенно третий Вью его хорошо поддерживают.


  1. anonymous
    00.00.0000 00:00


  1. derikn_mike
    09.09.2021 23:07
    +3

    web api + vue = идеально , другого не нужно


    1. Alexufo
      09.09.2021 23:34

      что такое web api?


      1. oji
        10.09.2021 01:24

  1. AlexDevFx
    10.09.2021 00:01
    +8

    1. Когда начинаешь писать на новом для себя фреймворке, языке всегда времени тратится больше на разработку, так как приходится многое изучать. Вы приплюсуйте время на изучение C# и Blazor.

    2. Язык/фреймворк - инструменты. Под каждую задачу/проект - свой набор, не стоит тут зацикливаться на какой-то одной экосистеме. Иногда набросать небольшой сервер на nodejs быстрее, чем C# городить (хотя в последнее время они значительно сократили бойлерплейт и в принципе приблизились к той же nodejs). Плюс иногда есть хорошие либы, вокруг которых легче строить приложение. Например, бота для телеграмма мне удобнее писать на telegraf+node, а систему авторизации на .NET Identity.
      Для бэкэнда на ноде есть удобный фреймворк nestjs, в нем и IoC, ORM, Eventbus. Он довольно близок по идеалогии к .NET.
      Да и почему бы фронт не писать на JS , а бэк на C#. Есть разные связки и они хорошо подходят под разные сферы.

    3. Зачастую над проектом трудятся несколько разработчиков: фронэндер, бэкэндер. Как правило фронтэндер пишет на js/typescript. Разбирать страницы Razor на смеси C#+JS куда сложнее.


    1. nin-jin
      10.09.2021 00:18
      +2

      На React, а особенно на React+Redux разработка действительно идёт крайне медленно. Это свойство самой технологии - низкоуровневость решения, большие объёмы бойлерплейта и обилие подводных камней, которые становятся видны лишь со временем.


      1. marshinov
        10.09.2021 08:32
        +5

        Редакс вообще часто не нужен


    1. arthurlomakin Автор
      10.09.2021 11:23
      +1

      Опыта до Blazor у меня было тоже +- 2 месяца, я писал приложение на WPF. И это все.


      1. qrKot
        10.09.2021 13:28
        +1

        Опыта до Blazor у меня было тоже +- 2 месяца, я писал приложение на WPF. И это все.

        И поэтому вы жалуетесь, что "React не Blazor" а "JS не C#". Ну, такое себе. (прошу заметить, сторонником что JS, что React'а не являюсь, скорее наоборот. Это просто два не особо любимых мной инструмента)


    1. Free_ze
      10.09.2021 12:18

      Зачастую над проектом трудятся несколько разработчиков: фронэндер, бэкэндер.
      Тем не менее, это далеко не всегда так. Фронт в том же энтерпрайзе зачастую не настолько заморочен, чтобы был смысл в отдельных специалистах.

      Разбирать страницы Razor на смеси C#+JS куда сложнее.
      «Смесь» должна быть скрыта под врапперами с интеропом, «прикладной» код должен быть только на C#.


  1. Alexufo
    10.09.2021 00:03
    +1

    Заголовок нужен был такой «свой первый инет магаз я переписал в 15 лет»


  1. justboris
    10.09.2021 00:18
    +19

    Давно не было статьи "у меня большой опыт C#/Java, решил разобраться с фронтендом, а у тут все устроено не так, как я привык, и мне это не нравится". Спасибо, ваше мнение важно для нас! /s

    На самом деле, есть в статье интересные моменты:

    нужно сделать колесо фортуны, на JS готовое решение есть, а для Blazor нет

    Так может потому и нет, потому что на Blazor сложные UI штуки не реализовываются? Что сама технология задумана для более-менее прямолинейных CRUD, и как-только начинаются шаги в сторону, технология уже не справляется.

    Сразу заметно, что код React в 1.5 раза больше.

    А ничего что в React у вас 3 компонента, а Blazor все соединено в один? Сами написали больше кода, и жалуетесь теперь.

    React же все построено вокруг паттернов CQRS, Event Sourcing, на бекенде эти паттерны используют только в сложных проектах, поскольку для простого проекта это принесет только вред

    Откуда такая уверенность, что работающее хорошо в бекенде априори будет работать хорошо везде? Большинство бекендов как правило stateless, поэтому там можно обойтись прямолинейными паттернами. Фронтенд же (и любой UI в принципе) хранит в себе состояние, и без его грамотной огранизации даже то самое колесо фортуны реализовать не получится.

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


    1. arthurlomakin Автор
      10.09.2021 11:35

      Большой опыт C# - это +- 2 месяца до написания интернет-магазина. Я тогда узнал хоть, что такое БД, умел только SELECT, UPDATE, DELETE. Не знал даже, что нужно таблицы связывать по ключу. И писал приложение на WPF. А JS я тоже знал, но бек не писал и фронт писал без фрейворков и библиотек.


  1. nin-jin
    10.09.2021 00:24
    +7

    Есть еще Modx, но если я не ошибаюсь, это тот же Redux, только с сахаром в виде ООП да и сейчас Redux - де-факто стандарт.

    MobX - совсем не Redux, даже не близко. Рекомендую всё же к нему присмотреться, если собираетесь оставаться на React.

    Redux же не стандарт совсем. Его какое-то время много хайпили, пока проекты не выросли. После этого даже до самых глупых дошло, что это какая-то тормозная ерунда, в которой концы с концами не сведёшь, а рефакторить адски больно. Теперь же потихоньку Redux-легаси переписывают кто на MobX, кто на хуки.


    1. strannik_k
      10.09.2021 21:59
      +1

      Я 3 месяца назад искал работу. К сожалению, вакансий c react + redux было гораздо больше чем с react + все остальное. Смотришь, хорошая вакансия, но redux все портит и нет особого желания всерьёз рассматривать позицию в проекте с ним. Да и на курсах штампуют новых поклонников Абрамова и redux-а. Эх, испортил Абрамов реакт основательно и надолго.


  1. Cobalt
    10.09.2021 01:06
    +9

    <sarcasm>Судя по кол-ву опечаток, вообще удивительно что удалось написать хоть что-то рабочее</sarcasm>.

    А если серьезно, то сам сталкивался с подобным эффектом когда менял стек. Переходил с PHP на Java и пытался применить старый опыт в новой среде - получал кучу ошибок и потенциальных дыр. Переходил с Java на JS - аналогично: привычные подходы и паттерны перестают работать. В 2019м полез во Flutter и тоже не мог долго вкурить как тут делать привычные мне штуки. Но как только в мозгах перестраиваешь парадигму мышления и начинаешь понимать что другой стек = другие паттерны - все начинает идти как по маслу.


    1. kibizoidus
      10.09.2021 01:36
      +7

      Тут труЪ программист без IntelliSense прямо в статье превращается в тыкву, а вы ему - про различный опыт...


      1. JustDont
        10.09.2021 11:09
        +2

        Самое интересное, что Intellisense на голом JS в VS Code тоже есть весьма неплохой, хотя и дико уступающий тому, что есть в TS. Скорее всего оно там не возникает из воздуха и там есть какие-то еще требования к тому, чтоб оно работало (открывать файлы, возможно) — но оно есть.


  1. nin-jin
    10.09.2021 01:18
    -5

    Скажу так, нет ни одного шаблонизатора(я туда отношу и JSX), который хоть немного сравнится по удобству с Razor(шаблонизатором для Blazor).

    Я тут рассказывал недавно, почему шаблонизация - это устаревшая концепция. Для примера, могу продемонстрировать, как ваше приложение пишется на $mol.

    Cмотрите, это не шаблон, это статически типизированное декларативное описание композиции компонент:

    $my_app $mol_list
    	tasks?val /$my_task
    	rows /
    		<= Task_list $mol_list rows <= task_list /$mol_view
    		<= Add $mol_row sub /$mol_view
    			<= Add_descr $mol_textarea value?val <=> add_descr?val \
    			<= Add_submit $mol_button_major
    				title @ \Add
    				click?event <=> add_submit?event null
    	Task!id $mol_row sub /$mol_view
    		<= Task_descr!id $mol_text text <= task_descr!id \
    		<= Task_remove!id $mol_button_minor
    			title @ \Remove
    			click?event <=> task_remove!id?event null

    Тут мы видим из чего состоит приложение и как компоненты связаны друг с другом разносторонними связями.

    А вся логика описывается уже отдельно кодом:

    namespace $.$$ {
    	export class $my_app extends $.$my_app {
    		task_list() {
    			return this.tasks().map( ( _, i )=> this.Task( i ) )
    		}
    		task_descr( id: number ) {
    			return this.tasks()[ id ].descr
    		}
    		task_remove( id: number ) {
    			const tasks = this.tasks().slice()
    			tasks.splice( id, 1 )
    			this.tasks( tasks )
    		}
    		add_submit() {
    			const task = { descr: this.add_descr() }
    			this.tasks([ ... this.tasks(), task ])
    			this.add_descr( '' )
    		}
    	}
    }

    Тут мы видим откуда берутся данные и что делают кнопки.

    Как видите, кода получилось примерно столько же, как и на Blazor, но:

    • Тут уже есть поддержка локализации. Английские тексты при сборке автоматически выносятся в отдельный JSON, а в коде они заменяются на человекопонятные глобально уникальные ключи.

    • У каждого элемента есть все необходимые человекопонятные классы для стилизации. Это что-то типа автоматического БЭМ, только круче. Более того, можно писать статически типизированные стили.

    • Тут есть куча точек расширения. Каждое имя после стрелочки - такая точка. Фактически это просто методы в классе, которые можно в дальнейшем переопределять.

    • Описания задач поддерживают маркдаун, а форма - подсветку синтаксиса.

    • Рендеринг приложения автоматически виртуализируется. Даже если вы в описание каждой из тысяч задач засунете килотонны текста - больше 1000 DOM-элементов у вас на странице не будет.

    • И весит это всё меньше, чем один только голый Реакт. Грузится, соответственно, тоже гораздо быстрее.


    1. DevAdv
      10.09.2021 02:40
      +11

      $Не?/ну<=>это<=ад \
      $.$$$какой@то? null


      1. nin-jin
        10.09.2021 02:46
        +2

        return <абсолютно .{.. props} c={<><вами>согласен</вами>{' '}<в>этом</в></>} {/*или*/} className={'irony ' + нет()}/>


        1. vabka
          10.09.2021 05:40

          Справедливости ради, если отформатировать нормально, то читать уже не так сложно.
          В принципе любой, кто значет html и js сможет понять, что тут написано.
          Что же означает это нагромождение из !$$$@@/\ <= понять очень трудно.
          Во что это превращается в html?

          return <абсолютно 
                         {/*Не знал, что тут можно комментарии писать, хех*/} 
                         className={'irony ' + нет()}
                         c={<><вами>согласен</вами> <в>этом</в></>} 
                         {... props}
                     />
          


          Ну и немного моих любимчиков
          let root model dispatch =

          let pageHtml =
          function
          | Page.About -> Info.View.root
          | Counter -> Counter.View.root model.counter (CounterMsg >> dispatch)
          | Home -> Home.View.root model.home (HomeMsg >> dispatch)

          div
          []
          [ div
          [ ClassName "navbar-bg" ]
          [ div
          [ ClassName "container" ]
          [ Navbar.View.root ] ]
          div
          [ ClassName "section" ]
          [ div
          [ ClassName "container" ]
          [ div
          [ ClassName "columns" ]
          [ div
          [ ClassName "column is-3" ]
          [ menu model.currentPage ]
          div
          [ ClassName "column" ]
          [ pageHtml model.currentPage ] ] ] ] ]


          1. vabka
            10.09.2021 05:42
            +1

            Блин, отступы поехали. Теперь буду знать, что надо делать всегда предпросмотр
            Вот норм вариант:

            let root model dispatch =
            
              let pageHtml =
                function
                | Page.About -> Info.View.root
                | Counter -> Counter.View.root model.counter (CounterMsg >> dispatch)
                | Home -> Home.View.root model.home (HomeMsg >> dispatch)
            
              div
                []
                [ div
                    [ ClassName "navbar-bg" ]
                    [ div
                        [ ClassName "container" ]
                        [ Navbar.View.root ] ]
                  div
                    [ ClassName "section" ]
                    [ div
                        [ ClassName "container" ]
                        [ div
                            [ ClassName "columns" ]
                            [ div
                                [ ClassName "column is-3" ]
                                [ menu model.currentPage ]
                              div
                                [ ClassName "column" ]
                                [ pageHtml model.currentPage ] ] ] ] ]


          1. nin-jin
            10.09.2021 09:35

            Если уж совсем топить за справедливость, то код на view.tree вообще невозможно отформатировать ненормально. И он не превращается в html, он превращается в дерево компонент. А компоненты уже могут создавать dom элементы, рисовать на canvas, а могут вообще не иметь никакой визуализации.


      1. Alexufo
        10.09.2021 02:59

        английский vs японский


    1. Bromka
      10.09.2021 08:49
      +6

      Ого, новая версия brainfuck. Теперь с $


  1. xadd
    10.09.2021 01:28

    В TypeScript нужно впилить расширение для компилируемых шаблонов, как это сделали для JSX. На React пишу с 2014, но в 2021 видеть его почти стандартом уже странновато.


    1. ApeCoder
      10.09.2021 08:55
      +1

      Типа вот такого?


  1. kibizoidus
    10.09.2021 01:42
    +4

    @* Эта кнопка вызывает функцию DeleteTodoItem и передает ей TodoItem агрумент *@ <button @onclick="(() => DeleteTodoItem(TodoItem))">Удалить</button>

    Шел 2021 год... Программистам все еще продолжали платить за строчки кода с комментариями...


    1. arthurlomakin Автор
      10.09.2021 12:33

      Я думал, что не все поймут, потому что не знают Razor и C#, а в своем коде не пишу, только мешает.


  1. hello_my_name_is_dany
    10.09.2021 03:29
    +2

    Перед тем, как писать критику, стоило бы изучить те инструменты, которые вы выбрали. Про репозиторий в TypeORM вам писали, можно проще и лучше. Про миграции, всё есть в документации: указываем откуда, как и куда генерировать их через конфиг или энвы. Если не нравится через JS их генерировать и применять, есть ts-node, жизнь чутка проще становится. Прежде чем жаловаться, неплохо было бы изучить, как эти инструменты работают, чтобы потом не кричать, почему ничего не работает или не получается.

    Еще есть Angular и Vue, но там шаблонизаторы тоже не такие мощные, краткие и понятные как Razor

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

    В Blazor можно легко сделать DI контейнер (в Angular тоже), в React же все построено вокруг паттернов CQRS, Event Sourcing

    Если мы берём React, тут немного просто другие парадигмы используются. Вам в компонентно-ориентированном программировании зачем DI? Дочерние компоненты добавляются лёгким импортом и небольшим JSX. Если вы про запросы к API, то в React есть Hooks, делаете свой хук с привязкой к API.
    CQRS и Event Sourcing нужны для контроля над состоянием, вокруг которого и построен React.

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

    А почему доля того же blazor намного меньше, чем react+redux? Может всё же они немного для других задач? Может потому что они хорошо решают СВОИ задачи?

    Я люблю C#, ненавижу зоопарк в JS, но если вы не поняли или вам просто не понравилось, это не означает, что это плохие и ужасные технологии.
    Учитесь и не спешите с выводами. В любом случае, вы молодец, что поддерживаете хоть как-то кругозор.


    1. arthurlomakin Автор
      10.09.2021 12:19

      Я как раз использовал ts-node.


      Про репозиторий в TypeORM вам писали, можно проще и лучше.

      Я это тоже использовал. Но оно же все равно не подскажет, если я опечатался, а это и было целью


      1. hello_my_name_is_dany
        10.09.2021 16:34

        Что вы подразумеваете под опечаткой? Если брать классы Entity, то миграция генерируется на основе их и того, какая схема сейчас в бд. Самому руками редко приходиться в миграции что-то писать, многое можно декораторами описать у сущности.


        1. arthurlomakin Автор
          10.09.2021 20:21

          Опечатка — это если я в запросе написал не {where: {name: ''}}, а {where: {nam: ''}}.


    1. strannik_k
      10.09.2021 22:41
      +1

      А почему доля того же blazor намного меньше, чем react+redux? Может всё же они немного для других задач? Может потому что они хорошо решают СВОИ задачи?

      Учитесь и не спешите с выводами.

      Ну, на мой взгляд, вывод автор сделал правильный)

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

      Возвращаясь к react+redux. Помню, как в тем времена одновременно появилось много новых технологий. ES6, webpack, react + менеджеры состояний к нему и прочее. Народ не успевал во всем этом разобраться и большинство выбирали технологии по количеству лайков в github. В это время Абрамов показал зрелищный доклад на конференции, на которой были разработчики из facebook, которые и наняли его. Из-за этого события он обрел популярность и начали ошибочно полагать, что он очень сильный разработчик и сделал крутую вещь.


      1. Alexandroppolus
        11.09.2021 09:53

        Ещё такой момент: redux стал реализацией flux - модного тогда подхода к стейт-манагерству. Потому воспринялся как истинный путь.

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


        1. strannik_k
          12.09.2021 19:37

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


  1. Rsa97
    10.09.2021 09:00

    Всё события и изменения в DOM-дереве передаются на сервер через WebSocket и сервер генерирует новый html и шлет обратно.
    И насколько хорошо работает такая технология на мобильном интернете с низкой скоростью, непредсказуемыми задержками и периодическими обрывами?


    1. arthurlomakin Автор
      10.09.2021 10:02

      В том то и дело, что плохо


  1. tamazyanarsen
    10.09.2021 09:18
    +2

    вот после таких статей (про орфографические ошибки я вообще молчу) начинающие разработчики думают, что проблема в языке программирования (ЯП), а не в человеке, который неправильно использует этот ЯП

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


  1. Dropsen
    10.09.2021 10:02

    А почему бы не попробовать Сделать React + NodeJs + Laravel?


    1. hello_my_name_is_dany
      10.09.2021 16:38

      Может автору неинтересно изучать PHP? Захотел на ноде, захотел сделать full-stack монолит - сделал. Некрасиво, конечно, но проблема явно не в этом. А в том, что ему не понравились инструменты на JS, хотя в них он так и не смог нормально разобраться


  1. motoroller95
    10.09.2021 10:13
    +9

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


  1. Eugene_Bubnovich
    10.09.2021 11:36

    Комментарии норм разбежались)



  1. VGoudkov
    10.09.2021 12:15

    Скромно напомню про Vaadin. Входные языки Java или Kotlin, фронт отдельно писать не нужно совсем. Всем хорош, за исключением двух вещей: а) даже в 2021 даже опытные разработчики склонны писать бизнес-логику в onClick, и б) хрен на него найдёшь разработчиков в 2021 / RU. Вот печально думаем, куда переезжать - React или Vue :(


    1. arthurlomakin Автор
      10.09.2021 12:21

      даже в 2021 даже опытные разработчики склонны писать бизнес-логику в onClick

      Я так тоже делал, пока не понял, что так делать нельзя, сейчас изучаю архитектуры и паттерны, уже критично.


  1. DaturInnoxia
    10.09.2021 14:26

    Когда в одном файле и код и вёрстка ИМХО - это адовый адище.

    Redux сам автор уже давно объявил устаревшим после того как ввели хуки.

    Дэн Абрамов: "Люди используют его, чтобы реализовать кэш, потому что в React нет first class-концепции кэша, но каждый реализует это по-своему. Решать проблемы сложно, поэтому они обычно нерешенные. Получается мешанина из boilerplate-кода."

    И это ещё одна проблема Реакта - постоянно идёт смена библиотек - ты как на карусели - в каждой команде выдумывают своих макаронных монстров, в отличие от того же Angular - где уже много лет мало что меняется, кроме каких-то именно улучшений, а не пересмотра концепции.

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


  1. space2pacman
    11.09.2021 21:38

    "Всё события и изменения в DOM-дереве передаются на сервер через WebSocket и сервер генерирует новый html и шлет обратно. То есть onclick можно обработать прямо из C# и там же скачать, к примеру данные из базы. Это очень удобно"

    То есть то, что должно происходить на клиенте передается на сервер? А смысл? Чтобы сделать материал для этой статьи?

    "Это очень удобно". Это шутка?

    "То есть onclick можно обработать прямо из C#". Спасибо не надо.


    1. arthurlomakin Автор
      12.09.2021 10:44
      +1

      То есть то, что должно происходить на клиенте передается на сервер? А смысл?

      В React 17 добавили Server React Server Components, принцип тот же. Зачем-то же они добавили эту функцию? Может скажете, зачем, если это ерунда и никому не нужно?


      1. space2pacman
        12.09.2021 16:41

        Затем чтобы отдавать готовый контент для старых, слабых браузеров и что-то связанное с SEO.