Pull to refresh

Comments 83

Может логичнее было бы сравнивать с TypeScript все же?
да, и сразу можно было бы выкинуть babel с flow и оставить только вебпак
Проблемы были бы примерно теже. TypeScript практически ничего не вносит нового, кроме адекватного наследования по сравнению с flow (могу ошибаться, не так много на нем писал). При этом у него всё таже проблема: нет гарантий того, что скомпилиный код будет работать так, как предполагалось. Когда я узнал, что степень строгости компиляции нужно указывать в конфигах и по-умолчанию она совсем слабая, я совсем растроился
Плюсы ts по сравнению с babel + flow
-ts компилятор быстрее babel и генерит чуть более оптимальный код (например нет лишних оберток для классов не имеющих родителя).
-ts отлично поддерживается в ide (vscode — из коробки, sublime — офф плагин от ms github.com/Microsoft/TypeScript-Sublime-Plugin, WebStorm — из коробки)
-npm @types
-c 3.0.0 расширены возможности типизации rest параметров
-keyof ( или в flow тоже есть аналог? )
-когда ставишь ts нет ощущения, что тебе в node_modules упало половина всего npm
-информацию о типах при желании можно вытянуть в runtime через декораторы (например на этой основе можно генерить валидаторы, gui, мета информацию для orm github.com/typeorm/typeorm)
В flow тоже так можно, но это несет доп издержки в рантайме codemix.github.io/flow-runtime/#
-У ts целостная экосистема, в отличии от babel где все перемотано изолентой.
Я так и не понял зачем нужны все эти пресеты в виде отдельных пакетов. Если уже используется транспилятор смысл использовать не все потенциальные фичи новых стандартов? В крайнем случае можно было бы сделать с конфиги одну настройку experimental включающую поддержку фич, попадание которых в стандарт находится под вопросом. Но в babel наворотили непонятно что. В итоге мануалы старше полугода уже становятся неактуальны и непонятно какой пресет нужно ставить для той или иной фичи.
-tsconfig поддерживает множественные псевдонимы путей ( пример
"@middlewares/*": [
                "./src/middlewares/*",
                "./src/core/middlewares/*"
            ],

), которые не ломают автокомплит в ide.
Да мне стоило по-другому сформулировать мысль. «практически ничего не вносит принципиально нового»
Позволю себе вас поправить: «Да мне следовало по-другому сформулировать мысль. Я не люблю тайпскрипт принципиально». Это было бы уместнее.
На Github у Kotlin 24 тыс звезд, у React 110 тыс. Очевидно, что React ненужная никому и устаревшая технология, вы совершенно правы :)

П.С. Сравнение теплого с мягким это дело субъективное…
Ни в коем случае не выступал против React. Я даже не выступал против JavaScript. Скорее о том, что если интересна строгая типизация на фронте, то есть хорошей язык для этого.
Никогда не сравнивал звезды, но не стал бы пытаться сравнивать язык и фреймворк.
Серьезно?
Пока я готовил эту статью, я узнал кучу новых вещей о современном Javascript: flow, babel, async/await, шаблоны jsx. Интересно, насколько быстро эти знания устареют? И всё это не нужно, если использовать Kotlin. При этом знать о React нужно совсем немного, потому что большая часть проблем легко решается при помощи языка.


Заголовок намекает, а заключение утверждает, что Javasript + React забудется и устареет, а Kotlin + React это прогрессивно. Однако сравнение востребованности говорит об обратном. Те кто пишут на Яве не спешат переходить на Котлин, а Реакт довольно популярен.
Но опять же, каждому свои фломастеры.
Если уж так рассуждать, то flow и TypeScript «забудутся и устареют». Но всё совсем не так. Каждая технология занимает свою нишу и время. JS будет жить очень и очень долго, просто потому что он живет в браузере. flow, TypeScript, React, redux, RxJs, Kotlin — это всё толи паразиты, толи симбионты. Они решают типовые проблемы, которые сам JS не спешит решать, а если решает, то медленно (ESтакой-то). В Java мире тоже самое: Kotlin решаем набор наболевших проблем. Кто-то готов ради этого учить новое, кто-то нет. И везде встречается костность людей. «Работает и хорошо», особенно, если это большая компания, где польза от новой технологии обычно перевешивается тонной легаси, необходимостью обучения всех и выроботки стандартов работы.
Не вижу смысла спорить, как я уже сказал фломастеры нужна всем разные. Вам кажется что Kotlin решает ваши проблемы? Отлично! Кто то скажет что React лучше держит нагрузку? Пускай пишет на React. Все упирается в навыки команды и требования бизнеса.
UFO just landed and posted this here
Во-первых, Koltin я поставил в тот же ряд. Во-вторых, я так и не понял зачем использовать redux и RxJs при работе с React. RxJs вполне перекрывается корутинами или async/await. Redux вносит глобальное состояние, что просто зло ИМХО.
Примерно такое же зло как СУБД на бэке. Redux при правильной готовке реализует одну глобальную точку входа для работы с композицией множества локальных состояний.
А почему глобальная точка входа должна быть одна? Зачем всю систему завязывать на один элемент? Как потом разбираться какой код можно выбросить? Как оченить степень последствий при изменениях?
Как раз чтобы проще было оценить степень последствий при изменениях. Если у нас единая точка входа, то мы можем контролировать все обращения к состоянию, подменять результаты, логировать и т. п. в одном месте.

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

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

UFO just landed and posted this here
Спасибо за объяснения от Вас и от VolCh
У меня подобные проблемы встречались пару раз, но не в слишком сложной форме. Объявление пары синглетонов полностью решало задачу. Но у меня простой интерфейс, возможно, при более сложных взаимосвязях это и имеет смысл.
UFO just landed and posted this here
Именно так. Но такие синглетоны формируются вокруг точки применения, а не вокруг факта того, что они хранят глобальное состояние (что подразумевает «единая точка входа»). И они не добавляют новый слой абстракции
UFO just landed and posted this here
Хороший пример, спасибо, я подумаю над этим.

И что такого плохого в лишнем слое абстракции?

То, что это новая библиотека, новые термины, новые ограничения, которые надо увязать со всем остальным. Чем меньше движущихся частей, тем легче поддерживать и писать новый код.
UFO just landed and posted this here
Ну как сказать легче. Ну вот я, прийдя на какой-то проект, вряд ли бы додумался искать какие-то синглтоны. Увидел что никакого знакомого хотя бы понаслышке стейт менеджера нет зависимостях и стал бы поднимать данные, нуждающиеся в шаринге на общий компонент, чаще всего это корневой App ну или какой-то Main. Синглтоны, имхо, куда болшее зло чем одна глобальная точка входа в хранилище.
Redux вносит глобальное состояние, что просто зло ИМХО.


Мне прямо интересно какую альтернативу вы предлагаете. А то Ваш пример из статьи с хранением состояния в ближайшем State компонента получит проблемы при необходимости разделения состояния между двумя компонентами. Особенно когда у вас иерархия компонентов перестанет совпадать с иерархией данных. Например показывать текущую страницу из основного списка на странице в заголовке, определяемым в Layout'e.

Сразу отвечу на некоторые Ваши вопросы.

А почему глобальная точка входа должна быть одна? Зачем всю систему завязывать на один элемент?

Да можете хоть десять сделать, просто вы при этом никакой проблемы не решите, зато добавите проблему с синхронизацией точек входа в ваши данные. Собственно из-за наличия возможного разделяемого состояния, это никак не поможет с «Как потом разбираться какой код можно выбросить? Как оценить степень последствий при изменениях?»

Как потом разбираться какой код можно выбросить? Как оценить степень последствий при изменениях?

В случае Redux+JS, проверить селекторы и использующие их компоненты. Удалить и проверить тесты.
В случае Redux+(TS/Flow/Kotlin) — просто удаляйте пока компилируется.
Например показывать текущую страницу из основного списка на странице в заголовке, определяемым в Layout'e.

В своем проекте я это решил через наследование, что больше соответствовало моему случаю (header отличается для разных страниц). Вот пример

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

Удалить и проверить тесты… просто удаляйте пока компилируется

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

шаблонные строки в JS тоже есть, hello ${foo}. И по-моему typescript лучше чем js+flow

чертов парсер. Шаблонные строки окаймляются бэктиками, `

Темплейты и optional chaining есть и в джсе (и даже можно подключить во флоу, в отличии от тайпскрипта)
«Во-вторых валидность пришедшего json проверяется во время десериализации, а не когда-нибудь при обращении» — что конкретно проверяется?
что все ожидаемые поля есть, они могут конвертиться в нужные типы и что они не null, если так объявлено поле. И так по цепочке во всех вложенных объектах.
Пусть есть класс с необязательными полями: a: string, b: number, c: bool
{ «a»: «aaa» } — валидный? Или пустые поля всегда передаются?
{ «a»: 33 } — ?
{ «a»: «aaa», «d»: 33} — ?
Попробовал: первые два валидны, третий в зависимости от флажка nonstrict сериализатора.
Валидность второго — вопрос, конечно, спорный
Ну примерно как я и думал. Основная ценность — общий котлин класс на сервере и клиенте, а проверки — весьма вторично.
Я бы так не сказал. Вот пример json ответа из моего проекта:
Заголовок спойлера
{
  "event": {
    "id": 52413,
    "name": "Событие",
    "owner": "Мой любимый клуб",
    "fromKu": "KU6",
    "toKu": null
  },
  "timetables": [
    {
      "id": 52414,
      "begin": 1524768960000,
      "end": 1524765600000
    },
    {
      "id": 52415,
      "begin": 1524168840000,
      "end": 1524169080000
    }
  ],
  "tariffs": [
    {
      "id": 52418,
      "name": "один день",
      "quantity": 1,
      "timetablesIds": [
        52414
      ],
      "price": "1000.00"
    },
    {
      "id": 52419,
      "name": "два дня",
      "quantity": 2,
      "timetablesIds": [
        52414,
        52415
      ],
      "price": "2000.00"
    }
  ],
  "demandsCount": 0,
  "pretendersCount": 8,
  "debit": {
    "cashless": "0",
    "cash": "5000.00",
    "full": "5000.00"
  },
  "id": 52413
}


Внутренние объекты как есть передаются на отрисовку и где-то очень далеко после уже используются. И если проверки не было бы, то мне пришлось проверять весь код до того места, потому что я воплне мог накосячить в js части
Ну только если голый js. Всё это прекрасно разбирается через JSON.parse() и моделируется TypeScript классом. Если язык на сервере и клиенте одинаков и сборка идёт из shared исходников, то проблемам в передаче взяться неоткуда.
За исключением того, что версии серверва и клиента будут какое-то время разными
Ну и чем тут Котлин поможет? Если изменились названия и типы полей по любому версионность на api вводить, если просто добавились новые поля, то и проблемы нет.
Представьте себе, что на вас регистрируют багу: на странице где-нибудь отрисовалось undefined. Вы смотрите на код — всё ок, вы пытаететь воспроизвести — не воспроизводится. И только через пару часов Вы выясняете, что это коллега Вася в это время выкатывал новый релиз без обратной совместимости и что надо просто Васе голову скрутить, а бага была только в процесе релиза.

Если бы была проверка на валидность json, то Вы бы не потратили несколько часов в такой ситуации.

Это не отрицает необходимость обратной совместимости api
UFO just landed and posted this here
Но насчет основной ценности — полностью согласен. В моем случае это вообще было основной причиной выбора этого стека

Как вам показался котлиновский сериализатор на вкус? Я пытался его использовать, но он плохо поддерживается IDEA'ей (говорится что нужно ставить отдельный плагин для идеи, но он не помог), кроме того почему-то для моих случаев получались какие-то костыли

Я без плагинов жил, нормально. Там есть проблемы, безусловно. Например, нет адекватного способа сериализовать KClass. Когда пишешь общую библиотеку, надо везде таскать KClass, чтобы потом им пользоваться (правда на такой глубине использования грех жаловаться). Ещё там какая-то странная логика при кастомных геттерах, лучше туда не лезть.
А так, пишешь data class простенький, помечаешь аннотаций и всё работает на ура.
а схему генерить, кстати, умеет? я так и неразобрался
На первом скриншоте пример того, как не надо писать код. Что за ужасный стиль вместо простого и понятного

function Question() {

писать кучу скобочек и стрелочек:

const Question = () => {

Это типичная болезнь яваскриптщиков, когда появляется какая-то фича, они ищут, где бы ее использовать, даже если это не нужно, а некоторые всерьез верят, что «в 2018» слово function устарело.

Стрелочные функции придуманы для удобной записи маленьких анонимных функций в таких ситуациях:

var names = array.map(users, u => u.name);

Не надо их везде пытаться использовать.
Как-то все забывают, что основная задача стрелочных функций — все таки использование родительского контекста (отсутствие собственного this, arguments, super и new.target).
В примере на картинке this не используется.

Мне нравится больше const Question = () => {
Когда дольше используешь стрелки полностью привыкаешь к такому стилю.


Минусов такого подхода я не вижу.


Стрелочные функции придуманы ...

Это вообще не аргумент. Какая разница для чего оно изначально делалось? Пирамиды тоже не для туристов строили.

В подобном подходе вообще минусов нет, за исключением одного и единственного: в React dev tools будет выводиться что-то типа Anonymous Component (или просто Component). Такая же штука с Vue. Иногда очень полезно видеть дерево компонентов и у кого что внутри твориться (пропсы, стейт, контекст). Естественно на продакшене такие вещи всякими минификаторами убиваются (насколько помню, можно выключить).
Области применения обычных и стрелочных функций пересекаются. Какие использовать в пересечении — вкусовщина.
Получение машин на JS можно было короче (в котлине почему-то используете темплейт-стринг а в ЖС — нет):
async loadCars() {
    const uri = `/api/cars?brand=${this.state.brand || ''}&color=${this.state.color || ''}`;
    this.setState({
                      cars:   await (await fetch(uri)).json(),
                      loaded: true,
                  });
}
Спасибо за пример, везде заменил на этот вариант
А что по размеру бандла? Где получилось больше — у Kotlin или JS?
Сейчас померил:
JS — 3.5mb
Kotlin — 5.4mb
Это как? Финальный js на 5 мег? Да не может такого быть.
да, что-то тут не так. Весь мой проект о котором говорил в статье собрался в 2мб. Видимо как-то не так конфиги сварил, попробую разобраться
Разобрался. Забыл подключить UglifyJsPlugin.
JS — 375kb
Kotlin — 752kb
да, без gzip. Не стал поднимать nginx, сжал в tar.gz
JS — 83kb
Kotlin — 139kb
Теперь разыв не в ~2, а ~1.5 раза
Кстати, спасибо за мысль, надо будет включить сжатие на серваке
т.е хелло-ворлд проект весит 750кб? средний мобильник будет открывать такую страницу секунд 5
Если вынести за скобки сборку и подобное, то для непосредственного написания кода с одной стороны остается Kotlin+React, а с другой Javascript+React+babel+Flow+ES5|ES6|ES7

Какая странная формулировка. Babel — это инструмент сборки, а ES5|ES6|ES7 — это часть языка Javascript. Корректнее будет написать Javascript+Flow+React, а если взять Typescript, как советуют комментаторы выше, то будет просто Typescript+React, что никак не сложнее Kotlin+React

С Babel всё-равно придется иметь дело в любом случае, потому что он отвечает за то, чтобы ( ) превратился в React компонент. Наверно, все к этому привыкли и не замечают (хотя там даже для использования обычного JS надо {} писать).
он отвечает за то, чтобы ( ) превратился в React компонент

У вас в скобочках контент потерялся. Подозреваю что там было что-то типа <ComponentName />.


В моем понимании это часть формулы Javascript+React. Просто такой специальный синтаксис для React. Не обязательно использовать Babel, есть и другие инструменты, Typescript его понимает из коробки, или можно взять buble, например. Это нюансы сборки, на процесс кодинга они не влияют.

специальный синтаксис для React

Которого нет в Kotlin варианте. А вместе с ним нет никаких ограничений что код, а что не код.

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

А какие могут быть ограничения? В любом случае специальные конструкции придётся учить, даже если это сахар языка. Все-таки билдеры встречаются не в каждом Kotlin-проекте


сторогость компиляции TypeScript зависит от конфигов

Странно ставить гибкость конфигурации в недостаток. Наоборот, за счёт этого можно мигрировать существующие проекты постепенно. Переименовали файлы JS -> TS и постепенно наращиваем строгость типизации.


В случае Kotlin придётся переписывать проект целиком в один присест, а это может растянуться надолго.

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

И никто не заставляет переписывать весь проект целиком. Я не пробовал серьезных сочетаний, но, кажется, это ничем не отличается от TypeScript и JS в одном проекте.
С Babel всё-равно придется иметь дело в любом случае

Зачем? TypeScript умеет работать с JSX.

Я как-то решил попробовать пару примеров, которые есть на GitHub-е на тему Kotlin + React. Предлагаемые решения очень сильно интегрированы в инфраструктуру JS с использованием его жуткого билд-стека, поверх которого еще стоят gradle-плагины.
Из неприятных эффектов — полная рекомпиляция бандла при любом малейшем изменении, которое может длиться от 30 сек. и больше взависимости от кодовой базы. Разрабатывать с таким темпом практически невозможно, из-за чего до сих пор юзаю GWT, который умеет инкрементальную компиляцию.

я использую сочетание gradlew --continuous и webpack-dev-server, так что изменения подхватываются автоматически. Обновление при это, действительно, в районе 30 секунд. Возможно, это можно ускорить, не пробовал. Для меня это приемлемое время, особенно по сравнению с jsf, где на каждый чих надо перезапускать сервер. При этом в большинстве случаев мне не надо сразу смотреть результат, а когда надо, то результат уже обновлен. Кроме того, ребята из JetBrains посмотянно тюнят компилятор, когда-нибудь оно само ускорится

Для меня критично в 2018 году ждать 30 секунд на любое изменение. С увеличением кодовой базы и используемых сторонних модулей это время будет только расти. Тогда как на сам перезапуск сервера тратится 1.5 секунды (Embedded Jetty).


Проблема там не столько в самом компиляторе Котлина, сколько в бандлере. Чем больше библиотек — тем дольше приходится ждать. Простой хелло ворлд компилируется 10 секунд. Подключите React и время сразу увеличивается до 30.


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

У меня bundle отрабатывает за 6-7 секунд на подхват изменений. Наверняка можно сделать быстрее. Стартует, правда, долго изначально, но это не имеет значения вообще.
Список зависимостей
  "dependencies": {
    "react": "15.6.1",
    "react-dom": "15.6.1",
    "react-router-dom": "4.2.2",
    "react-sticky": "6.0.1",
    "primereact": "1.4.0",
    "chart.js": "2.7.2",
    "react-transition-group": "^2.2.1",
    "font-awesome": "^4.7.0",
    "classnames": "^2.2.5",
    "bigdecimal": "^0.6",
    "moment": "^2.20",
    "file-saver": "1.3.3"
  }


При этом перезапуск java сервера у меня с минуту идет (база, liqubase, spring...).
У вас 1.5 секунды с учетем перекомпиляции (хоть и инкрементной) запуска jvm и подобного?

Ну вот я пробовал официальные туториалы в т.ч. create react kotlin app и котлиновский tictactoe. Они все используют gradle kotlin frontend plugin, который в свою очередь пускает npm, webpack и babel. Когда я меняю строчку и делаю рефреш, все-равно компилится около 30 секунд — не важно, новый бандл или рефрешнутый. Пробовал поковыряться с настройками, но по ходу нет способа ускорить.


Компиляцию не учитываю. Запуск JVM и бутстрап приложения. Я всегда пользую Embedded Jetty, который сам по себе стартует меньше секунды. Архитектура, где нужно запускать сторонний сервер и туда что-то деплоить — это из эры динозавнов. Приложение обычно использует легкий фреймворк типа Javalin, пускает Flyway для базы, зачастую также использую Eclipselink (JPA), который пускается в 3-5 раз быстрее Hibernate. Иногда прикручиваю Google Guice для DI, но это лишних 0.5 — 0.7 секунд стартапа. Spring (Boot) принципиально не использую.

gradle kotlin frontend plugin не стал использовать. Шаг влево, шаг вправо — расстрел. Да и версии используемых компонент не контролируешь. Когда буду писать статью о настройке всей этой конструкции, попробую получить оптимальное время

Как по мне, так основная проблема, которую необходимо решить любому языку, транспилирующемуся в js, — это размер коммьюнити и возможность использовать существующие библиотеки. Тот же Typescript имеет огромное коммьюнити и кучу типов для уже известных js библиотек. Как у этого с котлин?
Самому нравится шарить DTO и валидацию между фронтом и беком. Для этого все пишу на

С этим у Kotlin плохо, но есть одно но: сконвретировать TypeScript в Kotlin очень просто, так что «куча типов для уже известных js библиотек» есть и у Kotlin с небольшой платой.
UFO just landed and posted this here
Да, там немножко надо подпиливать напильником, но при учете не слишком частой необходимости вообще подключения новой бибилиотеки, это вполне можно потерпеть.
UFO just landed and posted this here
Зачастую в «родных» TS декларациях без поллитры не разберёшься, методом тыка пытаешься понять что же ожидает или возвращает метод. А уж подпиливать где-то то, что нагенерено чем-то до конца не понимая что в оригинале происходит вообще как-то грустно…
Sign up to leave a comment.

Articles