Эта статья - можно сказать продолжение моего развития в программировании. Вот начало моего развития. Советую его прочитать.
Я написал интернет-магазин для магазинов мяса на 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)
Alexufo
09.09.2021 22:16+6Надо было мол брать, а не реакт
Blazer это же васм. Так можно людей и из геймдева сайты научить писать. Просто в e-commerce все плохо, либо самопис либо ... Битрикс))))
Вообще, когда вы что то долго ищите и не можете найти там, где сфера разработки огромна, типа поиск орм за 1 месяц, это звоночек, что вы пытаетесь использовать свой прошлый опыт в другой области. Что то тут не так и это что то может потом вылезти.
Вы пришли из Шарпа и плюетесь на то что все не такое как Шарп) Уэлкам, избалованным корпоративным софтом разработчикам. !
arthurlomakin Автор
10.09.2021 11:21Есть Blazor Server, а есть Blazor Webassembly. Я писал на Blazor Server.
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: 'Вася' } });
Сходили бы, доку почитали бы ради разнообразия, что ли.
symbix
10.09.2021 03:19+3Претензии, конечно, так себе, но в итоге выбор в пользу MikroORM был сделан абсолютно верно.
Если автор ORM прямо в readme заявляет, что «Your models in your app are your database tables» — очевидно, он не понимает, что вообще такое ORM, и делает что-то странное. Дальше можно даже не смотреть.
Alex_ME
10.09.2021 10:25+1А что делает ORM? В Entity Framework таблицы базы описывалось C# классами и запросы тоже с ними работали.
symbix
10.09.2021 18:16+1Я особо не в теме про C#, но навскидку не вижу причин, по которым EF был бы пригоден только для persistence models. Можно, конечно, и так делать, но ORM не должна ограничивать разработчика самым примитивным подходом.
Вот нагуглил, вроде все окей с EF:
arthurlomakin Автор
10.09.2021 11:53Согласен, что не все претензии весомые. Но добавилась весомая проблема независимости миграций в ORM. Дописал в статье.
qrKot
10.09.2021 13:24+3ORM - это такая штука, которая а) не всегда нужна, б) иногда вылазит боком.
Ну и, в целом, стремление переписать фронт на React.JS понять еще можно. Зачем при этом на беке на ноду переползать - вообще не понятно.
В общем, Blazor вас испортил.
Мухи отдельно, котлеты отдельноФронт связан с беком на уровне API, тчк.arthurlomakin Автор
17.09.2021 21:42В общем, Blazor вас испортил. Мухи отдельно, котлеты отдельно Фронт связан с беком на уровне API, тчк.
Но в React зачем-то добавили React Server Components. Чисто копия Blazor.
Зачем при этом на беке на ноду переползать — вообще не понятно.
На ноду хочу из-за того, что хочу выучить что-то новое.
BashkaMen
10.09.2021 11:08+1но проблемы js все равно не ушли, а ты точно уверен что name там есть?)) а может name это массив байтов?
arthurlomakin Автор
10.09.2021 11:12Я это смотрел. Все равно, если я напишу вместо name nam, то будет все ОК. Хотя я хотел добиться, чтобы IDE мне сообщило об ошибке. Я правда уже нашёл способ. Дописал в статье. Но все равно миграций нормальных нет нигде в JS.
nin-jin
10.09.2021 11:30А что такое "нормальные миграции"?
arthurlomakin Автор
10.09.2021 11:37Дописал в статье. Независимость от базы при создании новой миграции.
Alexufo
10.09.2021 04:42+1так я про тоже, у gwt нельзя было открыть две вкладки сайта, у них видилите стейт! Не знаю как сейчас дела, но может я тогда видел продукт криворуких..(1000% криворуких, арабы) оверхеда я насмотрелася.
derikn_mike
09.09.2021 23:07+3web api + vue = идеально , другого не нужно
AlexDevFx
10.09.2021 00:01+8Когда начинаешь писать на новом для себя фреймворке, языке всегда времени тратится больше на разработку, так как приходится многое изучать. Вы приплюсуйте время на изучение C# и Blazor.
Язык/фреймворк - инструменты. Под каждую задачу/проект - свой набор, не стоит тут зацикливаться на какой-то одной экосистеме. Иногда набросать небольшой сервер на nodejs быстрее, чем C# городить (хотя в последнее время они значительно сократили бойлерплейт и в принципе приблизились к той же nodejs). Плюс иногда есть хорошие либы, вокруг которых легче строить приложение. Например, бота для телеграмма мне удобнее писать на telegraf+node, а систему авторизации на .NET Identity.
Для бэкэнда на ноде есть удобный фреймворк nestjs, в нем и IoC, ORM, Eventbus. Он довольно близок по идеалогии к .NET.
Да и почему бы фронт не писать на JS , а бэк на C#. Есть разные связки и они хорошо подходят под разные сферы.Зачастую над проектом трудятся несколько разработчиков: фронэндер, бэкэндер. Как правило фронтэндер пишет на js/typescript. Разбирать страницы Razor на смеси C#+JS куда сложнее.
nin-jin
10.09.2021 00:18+2На React, а особенно на React+Redux разработка действительно идёт крайне медленно. Это свойство самой технологии - низкоуровневость решения, большие объёмы бойлерплейта и обилие подводных камней, которые становятся видны лишь со временем.
arthurlomakin Автор
10.09.2021 11:23+1Опыта до Blazor у меня было тоже +- 2 месяца, я писал приложение на WPF. И это все.
qrKot
10.09.2021 13:28+1Опыта до Blazor у меня было тоже +- 2 месяца, я писал приложение на WPF. И это все.
И поэтому вы жалуетесь, что "React не Blazor" а "JS не C#". Ну, такое себе. (прошу заметить, сторонником что JS, что React'а не являюсь, скорее наоборот. Это просто два не особо любимых мной инструмента)
Free_ze
10.09.2021 12:18Зачастую над проектом трудятся несколько разработчиков: фронэндер, бэкэндер.
Тем не менее, это далеко не всегда так. Фронт в том же энтерпрайзе зачастую не настолько заморочен, чтобы был смысл в отдельных специалистах.Разбирать страницы Razor на смеси C#+JS куда сложнее.
«Смесь» должна быть скрыта под врапперами с интеропом, «прикладной» код должен быть только на C#.
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 в принципе) хранит в себе состояние, и без его грамотной огранизации даже то самое колесо фортуны реализовать не получится.
Вывод такой – если уж взялись изучать новую экосистему, то нужно сначала понять её базовые парадигмы и паттерны, и почему они используются. А не натягивать свой прошлый опыт, потому что так привычнее.
arthurlomakin Автор
10.09.2021 11:35Большой опыт C# - это +- 2 месяца до написания интернет-магазина. Я тогда узнал хоть, что такое БД, умел только SELECT, UPDATE, DELETE. Не знал даже, что нужно таблицы связывать по ключу. И писал приложение на WPF. А JS я тоже знал, но бек не писал и фронт писал без фрейворков и библиотек.
nin-jin
10.09.2021 00:24+7Есть еще Modx, но если я не ошибаюсь, это тот же Redux, только с сахаром в виде ООП да и сейчас Redux - де-факто стандарт.
MobX - совсем не Redux, даже не близко. Рекомендую всё же к нему присмотреться, если собираетесь оставаться на React.
Redux же не стандарт совсем. Его какое-то время много хайпили, пока проекты не выросли. После этого даже до самых глупых дошло, что это какая-то тормозная ерунда, в которой концы с концами не сведёшь, а рефакторить адски больно. Теперь же потихоньку Redux-легаси переписывают кто на MobX, кто на хуки.
strannik_k
10.09.2021 21:59+1Я 3 месяца назад искал работу. К сожалению, вакансий c react + redux было гораздо больше чем с react + все остальное. Смотришь, хорошая вакансия, но redux все портит и нет особого желания всерьёз рассматривать позицию в проекте с ним. Да и на курсах штампуют новых поклонников Абрамова и redux-а. Эх, испортил Абрамов реакт основательно и надолго.
Cobalt
10.09.2021 01:06+9<sarcasm>Судя по кол-ву опечаток, вообще удивительно что удалось написать хоть что-то рабочее</sarcasm>.
А если серьезно, то сам сталкивался с подобным эффектом когда менял стек. Переходил с PHP на Java и пытался применить старый опыт в новой среде - получал кучу ошибок и потенциальных дыр. Переходил с Java на JS - аналогично: привычные подходы и паттерны перестают работать. В 2019м полез во Flutter и тоже не мог долго вкурить как тут делать привычные мне штуки. Но как только в мозгах перестраиваешь парадигму мышления и начинаешь понимать что другой стек = другие паттерны - все начинает идти как по маслу.
kibizoidus
10.09.2021 01:36+7Тут труЪ программист без IntelliSense прямо в статье превращается в тыкву, а вы ему - про различный опыт...
JustDont
10.09.2021 11:09+2Самое интересное, что Intellisense на голом JS в VS Code тоже есть весьма неплохой, хотя и дико уступающий тому, что есть в TS. Скорее всего оно там не возникает из воздуха и там есть какие-то еще требования к тому, чтоб оно работало (открывать файлы, возможно) — но оно есть.
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-элементов у вас на странице не будет.
И весит это всё меньше, чем один только голый Реакт. Грузится, соответственно, тоже гораздо быстрее.
DevAdv
10.09.2021 02:40+11$Не?/ну<=>это<=ад \
$.$$$какой@то? nullnin-jin
10.09.2021 02:46+2return <абсолютно .{.. props} c={<><вами>согласен</вами>{' '}<в>этом</в></>} {/*или*/} className={'irony ' + нет()}/>
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 ] ] ] ] ]
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 ] ] ] ] ]
nin-jin
10.09.2021 09:35Если уж совсем топить за справедливость, то код на view.tree вообще невозможно отформатировать ненормально. И он не превращается в html, он превращается в дерево компонент. А компоненты уже могут создавать dom элементы, рисовать на canvas, а могут вообще не иметь никакой визуализации.
kibizoidus
10.09.2021 01:42+4@* Эта кнопка вызывает функцию DeleteTodoItem и передает ей TodoItem агрумент *@ <button @onclick="(() => DeleteTodoItem(TodoItem))">Удалить</button>
Шел 2021 год... Программистам все еще продолжали платить за строчки кода с комментариями...
arthurlomakin Автор
10.09.2021 12:33Я думал, что не все поймут, потому что не знают Razor и C#, а в своем коде не пишу, только мешает.
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, но если вы не поняли или вам просто не понравилось, это не означает, что это плохие и ужасные технологии.
Учитесь и не спешите с выводами. В любом случае, вы молодец, что поддерживаете хоть как-то кругозор.arthurlomakin Автор
10.09.2021 12:19Я как раз использовал ts-node.
Про репозиторий в TypeORM вам писали, можно проще и лучше.
Я это тоже использовал. Но оно же все равно не подскажет, если я опечатался, а это и было целью
hello_my_name_is_dany
10.09.2021 16:34Что вы подразумеваете под опечаткой? Если брать классы Entity, то миграция генерируется на основе их и того, какая схема сейчас в бд. Самому руками редко приходиться в миграции что-то писать, многое можно декораторами описать у сущности.
arthurlomakin Автор
10.09.2021 20:21Опечатка — это если я в запросе написал не {where: {name: ''}}, а {where: {nam: ''}}.
strannik_k
10.09.2021 22:41+1А почему доля того же blazor намного меньше, чем react+redux? Может всё же они немного для других задач? Может потому что они хорошо решают СВОИ задачи?
Учитесь и не спешите с выводами.
Ну, на мой взгляд, вывод автор сделал правильный)
Точно не потому что хорошо решают) JS тоже самый популярный язык, но это не делает его лучшим. Он популярен из-за того, что веб-разработка очень распространена, а браузеры не поддерживают другие языки.
Возвращаясь к react+redux. Помню, как в тем времена одновременно появилось много новых технологий. ES6, webpack, react + менеджеры состояний к нему и прочее. Народ не успевал во всем этом разобраться и большинство выбирали технологии по количеству лайков в github. В это время Абрамов показал зрелищный доклад на конференции, на которой были разработчики из facebook, которые и наняли его. Из-за этого события он обрел популярность и начали ошибочно полагать, что он очень сильный разработчик и сделал крутую вещь.
Alexandroppolus
11.09.2021 09:53Ещё такой момент: redux стал реализацией flux - модного тогда подхода к стейт-манагерству. Потому воспринялся как истинный путь.
Абрамов на самом деле как разработчик весьма неплох, сейчас пилит сложные крутые штуки, его жизнь далека от формоклёпства. В редуксе он давно разочаровался. Фейсбук не использует редукс.
strannik_k
12.09.2021 19:37Я не говорю что он плох. Но он известней любого, гораздо более опытного фронтенд разработчика. Тогда он явно не был разработчиком, к которому стоило прислушиваться всему react сообществу, особенно в плане архитектуры. Я не работал с ним и не слежу за ним, поэтому не знаю в чем и насколько он хорош, но думаю, когда его нанимали, он был на уровне обычного миддла. Ну и поработав несколько лет в Фейсбук, он конечно стал гораздо более хорошим разработчиком, чем раньше.
Rsa97
10.09.2021 09:00Всё события и изменения в DOM-дереве передаются на сервер через WebSocket и сервер генерирует новый html и шлет обратно.
И насколько хорошо работает такая технология на мобильном интернете с низкой скоростью, непредсказуемыми задержками и периодическими обрывами?
tamazyanarsen
10.09.2021 09:18+2вот после таких статей (про орфографические ошибки я вообще молчу) начинающие разработчики думают, что проблема в языке программирования (ЯП), а не в человеке, который неправильно использует этот ЯП
как можно делать такие выводы, учитывая, что у Вас нет никакого нормального опыта на js
Dropsen
10.09.2021 10:02А почему бы не попробовать Сделать React + NodeJs + Laravel?
hello_my_name_is_dany
10.09.2021 16:38Может автору неинтересно изучать PHP? Захотел на ноде, захотел сделать full-stack монолит - сделал. Некрасиво, конечно, но проблема явно не в этом. А в том, что ему не понравились инструменты на JS, хотя в них он так и не смог нормально разобраться
motoroller95
10.09.2021 10:13+9Вообще статья выглядит как "Не разобрался, да и хуй с ним, пойду на шарпе писать и всем расскажу что ЖыэС говно"
VGoudkov
10.09.2021 12:15Скромно напомню про Vaadin. Входные языки Java или Kotlin, фронт отдельно писать не нужно совсем. Всем хорош, за исключением двух вещей: а) даже в 2021 даже опытные разработчики склонны писать бизнес-логику в onClick, и б) хрен на него найдёшь разработчиков в 2021 / RU. Вот печально думаем, куда переезжать - React или Vue :(
arthurlomakin Автор
10.09.2021 12:21даже в 2021 даже опытные разработчики склонны писать бизнес-логику в onClick
Я так тоже делал, пока не понял, что так делать нельзя, сейчас изучаю архитектуры и паттерны, уже критично.
DaturInnoxia
10.09.2021 14:26Когда в одном файле и код и вёрстка ИМХО - это адовый адище.
Redux сам автор уже давно объявил устаревшим после того как ввели хуки.
Дэн Абрамов: "Люди используют его, чтобы реализовать кэш, потому что в React нет first class-концепции кэша, но каждый реализует это по-своему. Решать проблемы сложно, поэтому они обычно нерешенные. Получается мешанина из boilerplate-кода."
И это ещё одна проблема Реакта - постоянно идёт смена библиотек - ты как на карусели - в каждой команде выдумывают своих макаронных монстров, в отличие от того же Angular - где уже много лет мало что меняется, кроме каких-то именно улучшений, а не пересмотра концепции.
Мир помешался на Реакте потому что кажется, что это небольшая и классная библиотечка, но она через пару мгновений превращается в чудище из библиотек, без которых "нельзя обойтись".
space2pacman
11.09.2021 21:38"Всё события и изменения в DOM-дереве передаются на сервер через WebSocket и сервер генерирует новый html и шлет обратно. То есть onclick можно обработать прямо из C# и там же скачать, к примеру данные из базы. Это очень удобно"
То есть то, что должно происходить на клиенте передается на сервер? А смысл? Чтобы сделать материал для этой статьи?
"Это очень удобно". Это шутка?
"То есть onclick можно обработать прямо из C#". Спасибо не надо.
arthurlomakin Автор
12.09.2021 10:44+1То есть то, что должно происходить на клиенте передается на сервер? А смысл?
В React 17 добавили Server React Server Components, принцип тот же. Зачем-то же они добавили эту функцию? Может скажете, зачем, если это ерунда и никому не нужно?
space2pacman
12.09.2021 16:41Затем чтобы отдавать готовый контент для старых, слабых браузеров и что-то связанное с SEO.
marshinov
Добро пожаловать в мир JavaScript. Вы просто испорчены C#. C# - один из лучших прикладных языков сейчас. Посмотрите Kotlin Multiplatform. Bleeding Age, но может вам зайдёт, если есть желание писать на хорошем языке и иметь возможность дергать JS-код
nin-jin
Котлин уже научился не тянуть с собой 2 мегабайта рантайма?
some_x
Blazor тянет не меньше, но клиентов автора это устраивало, насколько я понял
ApeCoder
Это не blazor webassebly - другая технология
donPedro
А там уже исправили ситуацию, что один сервер может поддерживать всего несколько десятков пользователей одновременно, из за того что для каждого пользователя надо его контекст на сервере держать?
ApeCoder
Это вопрос к @arthurlomakin - он же на нем что-то делает. Я только теоретически интересуюсь
arthurlomakin Автор
Не интересовался, но оказывается много. Но это на мощном сервере.
arthurlomakin Автор
~250 KB. У меня Blazor Server, а не Blazor WebAssembly, там намного больше, как вы и говорите.
marshinov
По-моему еще нет. Просто как вариант "и волки сыты, и овцы целы" для автора. В Kotlin Multiplatform ИМХО проще до JS дотянуться, чем из Blazor и сам язык на C# похож, а местами даже интереснее сделан.
arthurlomakin Автор
Если я испорчен C#, это плохо или хорошо?
qrKot
Ну вот "испорчен C#" - это значит, что "сделать нормальную архитектуру старого" у вас, скорее всего, не получится на данном этапе.
marshinov
Не воспринимайте слишком всерьез;)