Ну что, продолжаем критиковать существующие подходы создания пользовательских интерфейсов, стоить теории - как привести все это дело в порядок, и ныться о том, как мы до такого докатились.
Данная статья является основной частью ранее опубликованную работы, посвященной синтаксису и способам определения шаблонов компонентов.
https://habr.com/ru/articles/864816/
Не будем сразу обращаться к содержанию предыдущей статьи, начнем с базы.
База. Понятия
На случай если кто-то не особо понимает, о чем речь, освежим некоторые понятия
Верстка (визуальная часть) = разметка (HTML) + стили (CSS)
Шаблон компонента = Верстка + Часть логики над ней (JS).
Почему часть - принцип разделения логики управления данными и представления.
По сути, все что остается в шаблоне это привязка данных, и спец. конструкции
(вставки, if, for).
Цель шаблона - сделать интерфейс динамическим, интерактивным и модульным.
Компонент - структура, описывающая элемент интерфейса целиком.
Вставки, они же вставки данных - любой блок кода в верстке.
Распространенные обозначения: {{ }} или { }
Используются для задания пропсов, атрибутов, а также в качестве слотов.
Слот - произвольно место в верстке для вставки контента (данные, компоненты, if, for)
Структура шаблона
Композиция компонентов и тегов
Стили
Реактивные атрибуты тегов
Реактивные вставки
Условный рендеринг (if)
Рендеринг списков (for)
Реактивные пропс (?)
Некоторые "киллер фичи" - какие-то на первый взгляд неочевидные вещи, например передача ссылки на тег в переменную вот таким образом ref={formRef}
Способы формирования шаблона
Фундаментально можно выделить два способа:
С использованием синтаксического сахара (лучше для ssr)
Нативно (лучше для рантайма)
UI-фреймворки, в большинстве своем формируют шаблон за счет синтаксического сахара,
а в дополнение предлагают некую "возможность" писать все без него, но как правило, она оставляет желать лучшего.
JSX
<Card className="title">
<p>Text</p>
</Card>;
Нативный синтаксис
React.createElement(Card, {
className: "title"
}, React.createElement("p", null, "Text"));
Ниже указаны некоторые особенности каждого из способов
Шаблоны на синтаксическом сахаре
Меньший контроль
Стандартизированный подход, абстракции => простота, читаемость, быстрая разработка
Подобные шаблоны более строгие и требовательные, дают меньше свободы => меньше шансов сделать ошибку
Лучшая интеграция с инструментами разработки - автокомплит, подсветка синтаксиса, и прочие спец. возможности IDE.
Набор встроенных функциональных фич - реактивность, привязка данных, слоты.
Зависимость от других инструментов - сборщиков, библиотек, плагинов, поскольку синтаксис требует транспиляции. Все это усложняет проект, процесс его разработки
За счет транспиляции и строгости - проще реализовать ssr или другой бэкенд-рендеринг
Магия - требует обучения, может привести к недопониманию происходящего, к путанице
Отсутствие нативности - решение не будет работать напрямую в браузере, или же потребует для этого дополнительных усилий, принудит пойти на неприятный компромисс
Вероятно меньшая производительность ввиду транспиляции, различных прослоек (?)
Высокая скорость разработки
Нативный шаблон
Прозрачность, полный контроль процесса
За счет меньшей строгости - больший шанс допустить ошибку, особенно новичку
Нативность/универсальность - стандартный синтаксис js, будет работать в любом окружении, поддерживаться любыми IDE.
Сложность и громоздкость - вероятно будет чуть больше кода и труднее читать (?)
Возможно более сложная реализация функциональных фич - реактивности, привязки данных, слотов (?)
Независимость от инструментов преобразования кода
Более трудная реализация ssr
Упрощенное обучение за счет нативности, прямой и очевидной реализации(?)
Вероятно более высокая производительность, за счет нативности (?)
Вероятно чуть меньшая скорость разработки (?)
Изначально хотел отразить это табличкой, но пункты не особо симметричны и не столь однозначны - большую часть из них так и хочется вынести за скобки.
По сути, это так - чисто наброски, чтобы примерно представлять на что следует обращать внимание.
Все решает исключительно конкретная реализация.
Ближе к делу
Чтобы продумать какой-то синтаксис, нужно четко понимать, чего мы от него хотим.
Для начала - давайте просто взглянем на жизненный путь кусочка jsx-кода (react)
1 - Написали так
<div className="title">Text</div>
2 - После транспиляции получилось такое
React.createElement("div", { className: "title" }, "Text");
3 - После запуска кода получаем объект (часть virtual dom)
{ type: "div", props: { className: "title", children: "Text" } };
4 - После отрисовки мы получаем что? правильно - результат на лицо
<div class="title">Text</div>
Вопросы?)
Разумеется, это упрощенный пример, без js-конструкций, но чисто в плане верстки все обстоит как-то так
Если кто-то не выкупает как формируются элементы DOM - вот один из способов.
const div = document.createElement("div");
div.className = "title";
const content = document.createTextNode("Text");
div.appendChild(content);
Другие способы: innerHTML и template + cloneNode, но нам пока они не интересны.
Итак, в этапах 2 и 3 можно разглядеть альтернативные способы задания шаблона
2 - посредством по сути создания тегов на месте - разметка будет формироваться сразу
Кстати - во vue и preact этот этап выглядит также
h("div", { className: "title" }, "Text"),
3 - посредством структуры, которую в дальнейшем придется обойти, чтобы превратить все в конечную верстку
<Способ 1 - через объект>
Про 3 мало что можно сказать. Синтаксис уже, по сути, перед вами. Улучшать тут нечего.
Необходимость дополнительного обхода циклом не кажется привлекательным решением.
К тому же при подобной структуре очень быстро нарастает вложенность дочерних элементов - шаблон слишком быстро растет вправо. Возможно, еще вернемся к этому, на первый взгляд идея кажется сомнительной.
а вот 2 - его можно попробовать доработать
Что вообще случилось в примере - мы взяли js, и решили с его помощью создать тег div, задать атрибут className: 'title', и добавить содержимое - 'Text'
Это похоже на то, к чему мы пришли в предыдущей статье
"Ну что, у вас есть: tagName, props и children. Осталось только объединить их."
Недолго думая, упрощаем до сути
div({ className: "title", children: "Text" })
Для удобства делаем параметры именованными, поэтому используем объект
Разумеется, подобный подход уже существует, и зовется фабрикой элементов, а это лишь одна из его реализаций.
Фабрика элементов — это паттерн, который упрощает создание DOM-элементов путем создания функций или оберток вокруг document.createElement.
Продумывание синтаксиса
Ну что, пробуем реализовать все фичи шаблона для нашего примера.
В процессе будем стараться как-то улучшать и упрощать синтаксис
Некоторый план возможностей шаблона, для реализации
Тег и атрибуты
Композиция тегов
Компонент и пропс
Специальные конструкции (реактивные атрибуты и пропс, вставки, if, else)
Тег и атрибуты мы уже задали. Пробуем реализовать композицию.
За основу возьмем вот такой пример
<div className="wrapper">
<div className="header">
<div className="left">Left</div>
<div className="right">Right</div>
</div>
</div>
С учетом того, что дочерних элементов может быть несколько, более подходящая структура для этого - массив
<Способ 2 - множественные функции. Вариант с children>
div({
className: "wrapper",
children: [
div({
className: "header",
children: [
div({ className: "left", children: [ "Left" ] }),
div({ className: "right", children: [ "Right" ] })
]
})
]
})
Да, на что-то приятное это не тянет, да и от способа через json мы недалеко ушли
Что тут не так. Вложенность точно не задалась)
каждый раз приходится писать свойство children
приходится расписывать свойства построчно, ввиду чего шаблон быстро растет вправо
А что, если отделить children от прочих пропс? Но куда?!
Давайте попробуем сперва сделать второй параметр для div, который и будет принимать children
<Способ 2 - множественные функции. Вариант с [ ]>
div({ className: "wrapper" }, [
div({ className: "header" }, [
div({ className: "left" }, ["Left"]),
div({ className: "right" }, ["Right"])
])
])
Вроде получше, но немного какой-то майнкрафт, не?
Конечно, массив можно сделать опциональным на случай одного child, или его отсутствия, но он в целом как будто избыточен - стоит хотя бы попробовать уйти от него, убираем.
<Способ 2 - множественные функции. Вариант без [ ]>
div(
{ className: "wrapper" },
div(
{ className: "header" },
div({ className: "left" }, "Left"),
div({ className: "right" }, "Right")
)
)
Вроде тоже неплохо. Но основная проблема здесь - форматирование - непонятно как лучше делать переносы, хуже прослеживаются уровни скобок, к тому же - без особых настроек IDE, мы вероятно будем получать что-то такое при автоформатировании.
div({ className: "wrapper" }, div({ className: "header" }, div({ className: "left" }, "Left"), div({ className: "right" }, "Right")))
или такое
div({
className: "wrapper"
},
div({
className: "header"
},
div({
className: "left"
}, "Left"),
div({
className: "right"
}, "Right")
)
)
Впрочем, это решаемо. Помимо этого - первый параметр обязательный.
а div у нас может не иметь атрибутов, но при этом иметь детей
Поэтому придется каждый раз вписывать нижнее подчеркивание _ или пустой объект { },
Об это нужно будет не забывать в процессе разработки, дабы не натворить делов.
Пробуем дальше: для children делаем второй вызов, получаем следующее
<Способ 2 - множественные функции. Вариант с замыканием>
div({ className: "wrapper" })(
div({ className: "header" })(
div({ className: "left" })("Left"),
div({ className: "right" })("Right")
)
)
Выглядит вроде норм, особенно если сверить с исходным html куском
Но что случилось? Да, появилось замыкание, что плохо отразиться на производительности, особенно на большом количестве элементов.
Хотя сам по себе способ максимально привлекательный за счет хорошего форматирования, и гибкости, получаемой за счет каррирования.
Что тут еще можно улучшить?
Второй вызов делаем необязательным - на случай если тег одиночный, или не имеет детей. Помимо этого, можно сделать свойство child опциональным в первом параметре. В таком случае если оно задается одним простым примитивом, что бывает часто, - не придется делать для него отдельный вызов, некрасивый перенос скобок на новую строку.
и вместо
div({ className: "wrapper" })(
"Text Text Text Text Text Text Text Text Text"
)
у нас будет
div({
className: "wrapper",
child: "Text Text Text Text Text Text Text Text Text"
})
Для вставки экземпляра компонента в шаблон ничего не меняется.
div({ className: "red" }) => Card({ className: "red" })
Других хороших вариантов композиции тегов, кроме выведенных выше не вижу.
Двигаемся дальше.
Теперь по поводу спец. конструкций (реактивные атрибуты и пропс, вставки, if, else).
В нашем случае долго это обсуждать не придется, поскольку все нативно.
Для того чтобы отрендерить список элементов достаточно обойти массив, добавляя элементы в fragment или другой тег. А после вставить его в верстку.
div({ className: "list" })(
list(items, (item) => (
div({ className: "item", child: item })
));
)
Сработает ли это - да, но интерфейс будет статичным.
Все волшебство реализуется за счет реактивности, а это тема для другой статьи.
В целом первоначально по нашему плану мы прошлись, давайте делать какие-то выводы.
Зачастую компоненты в коде описывается в виде класса или функции.
Тоже самое касается и тегов, если смотреть не на сахар, а на конечное представление.
Получается - если бы мы писали нативно и делали все сразу как нужно - синтаксис был бы примерно таким, как в примерах выше.
Есть компонент, есть его пропс, есть дочерние элементы.
Все, желаемая композиция получена.
Поскольку все нативно - прямо в шаблоне мы, в целом, можем писать практически любой код, без использования повсеместных { } для вставок, как это делается в jsx.
Также это дает некоторую гибкость - можно легко дробить шаблон на части, выносить его пропс в отдельную константу для удобства. И тд и тд.
Причем, используя подобный подход вам необязательно пихать логику в шаблон - вы можете описать ее где-то рядом - в остальной части компонента.
Более того вы можете формировать шаблон компонента кусочками, в императивном стиле, обрабатывать каждый из тегов по отдельности, построчно.
В некоторых случаях это может дать читаемость для конечного (корневого) представления компонента - по итогу оно будет сведено к композиции из семантических названий составляющих ее элементов.
Примерно вот так
Page(
Header(
Logo,
Menu
),
Content(
ListOfItems
),
Footer,
)
А все прочее, включая атрибуты, пропсы, будет указано где-то выше, в императивном стиле.
Ну и присвоено разумеется в какие-то переменные/константы, а потом уже указано в конечном представлении.
const onHeaderClick = () => {};
const cn = cx('header', isDarkTheme ? 'dark' : 'light');
const Header = div({ className: cn, onClick: onHeaderClcik });
Подход, по сути, универсален - можно писать императивно, а можно декларативно.
И вы, как разработчик можете контролировать это. И скажем в повседневке описывать все декларативно, а сложные кейсы прописывать отдельно, императивно. И уже после монтировать их в конечное представление.
А что насчет множества html-тегов, как их сделать доступными всюду?
Мы еще вернемся к этому, но пока, дабы вас успокоить, навскидку предложу след. варианты
globalThis
объединить в один объект, и так и использовать ui.div (фи)
каждый раз делать деструктуризацию { div } = ui; (фи)
добавлять строку импорта со всеми тегами к скрипту при загрузке
with
Касательно множества возникающих вопросов по данному способу, например: как на все это дело ляжет реактивность, какой она будет, как все это дело впишется в компонент, что со стилями, и как лучше прописывать оставшуюся часть логики - все это темы отдельных статей.
Если, конечно вам, вообще все это нужно)
Чтош, идем дальше. Разумеется, есть еще как минимум пара способов нативно задать шаблон, которые стоит хотя бы упомянуть.
<Способ 3 - Строковое представление. Шаблонные строки (template literals)>
const template = `
<div class="card">
<h1>${title}</h1>
<p>${description}</p>
</div>
`;
Пожалуй, наипростейший способ создания шаблона. Но сделать из этого что-то достойное, по сути - анрил, кроме вставок тут больше никаких возможностей то и нет. А преобразование в конечный набор узлов реализуется через тот же innerHTML
<Способ 3 - Строковое представление. Тегированные функции (tag functions)>
html`
<div class="app">
<${Header} name="ToDo's (${page})" />
<ul>
${todos.map(todo => html`
<li key=${todo}>${todo}</li>
`)}
</ul>
<button onClick=${() => this.addTodo()}>Add Todo</button>
<${Footer}>footer content here<//>
</div>
`;
Значительно более перспективный его собрат. Здесь за ширмой возможно вытворять всяческие манипуляции с прилетающими в функцию вставками и их значениями.
Подход используется во многих фреймворках, в данном случае это preact.
Как видите, идея синтаксиса тут примерно такая же, что и в решениях с использованием синтаксического сахара.
Оба способа позволяют реализовать шаблон нативно, и делают это достаточно производительно. Но данные подходы по-прежнему используют html в привычном виде.
Даже без учета этого - не знаю как вы, а я терпеть не могу шаблонизаторы. Как по мне это очередной вид извращений, который до сих пор реализуется во многих яп.
А про каскад шаблонных строк из кода выше я вообще молчу.
В общем просто оставляю примеры здесь, и пора закругляться.
Не хочу выносить анализ jsx в отдельную статью, давайте добьем на месте.
Изъяны синтаксиса jsx. Что же с ним не так?
Использование html в качестве основы шаблонов
использование угловых скобок
необходимость прописывать имя парного тега дважды, что избыточно
не самый приятный способ задания атрибутов, в html это делается в виде строк.
Вставки
для их объявления необходимо каждый раз указывать блок кода { }.
в целом это выглядит более-менее приемлемо, когда вставка где-то между тегов. Но при передаче пропс, или задании атрибутов, необходимость постоянного написания знака "ровно" и фигурных скобок (={ }) кажется избыточной, начинает бесить
также при совпадении key и value нельзя указать только key. Что кажется полным бредом, особенно на большом количестве ключей
комментарии также задаются с помощью обертки в блок, что неудобно
поддерживаются только js-выражения, произвольный код сюда нельзя писать, это может сбивать с толку
условный рендеринг прямо в шаблоне выглядит крайне непривлекательно, из-за него невероятно быстро растет вложенность. исключением мб лишь простейшее однострочное условие с помощью &&. Например, {yes && }
<div name={myName} age={myAge}>{content}</div>
//это
<Card a={a} b={b} c={c} d={d} e={e} />
//вместо этого
<Card a b c d e />
{/*Комментарий*/}
<div>
{yes ? (
<div className="yes">
Yes
</div>
) : (
<div className="No">
No
</div>
)}
</div>
Работа с ContextAPI Вложенность провайдеров, с которой думаю все знакомы.
Вроде решаемо, но не стоит говорить что этого нет.
<ThemeContext.Provider value={theme}>
<SettingsContext.Provider value={settings}>
<UserDataContext.Provider value={userData}>
<App />
</UserDataContext.Provider>
</SettingsContext.Provider>
</ThemeContext.Provider>
Это только то, что вспомнилось, а по факту - подобных бесячих моментов намного больше.
Итак, задача была в том, чтобы найти качественный подход задания шаблона компонента.
И по возможности сделать это нативным способом.
Как я говорил ранее - шаблон, реализованный через синтаксический сахар, может быть абсолютно любым - решаете только вы, ваша воображалка. Скажу как есть - я просто не знаю как можно было написать его иначе. После того как я открыл для себя подобный синтаксис, мне стало казаться что шаблон как будто бы вообще всегда задавался именно таким образом. Словно это норма, данность, стандарт. Короче - будь у меня задача реализовать шаблон через сахар - я бы сделал его точно таким.
Впрочем, судить вам.
Не знаю, что еще тут добавить)
Вроде все основное сказал по данной теме.
Надеюсь, вы уже понимаете, что это не последняя статья, это самое начало.
Мне крайне важна обратная связь, вопросы, обсуждения, поддержка и хейт с вашей стороны.
Пожалуйста, напишите что-нибудь от себя снизу, или в личку - будь то послевкусие, замечание, эмоция или мысль. Спасибо!
Оставлю небольшую голосовалку, мб кому-то будет интересно поучаствовать.
Пока преимущественно судим синтаксис и подход.
Лично мне понравились варианты с массивом и с замыканием.
Важно помнить, что все есть компромисс.
Комментарии (23)
alexnozer
14.12.2024 22:14Читал статью и словил дежавю, потому что тоже пробовал выразить шаблоны нативным синтаксисом JS и без теговых шаблонных литералов. Вдохновлялся такими вещами, как Flutter, Jetpack Compose, SwiftUI и прочими технологиями, где шаблоны выражены средствами самого языка.
Ход мыслей был примерно как в статье. Хотелось получить лаконичный синтаксис без лишних скобок, массивов и JSON. В итоге пришёл к тому, что всё есть функция. Получилось что-то такое:
article( attr('class', 'card'), data('type', 'featured'), aria('labelledby', 'heading_id'), prop('id', 7425639), comment('Article template'), img( attr('src', '...'), attr('alt', '...'), prop('className', 'img') ), h2( prop('id', 'heading_id'), a( attr('href', '/article/7425639'), 'Read more' ) ), p(`Article description`), p( text('Posted on: '), time('2024-12-17') ) )
Что превращается в это:
<article class="card" data-type="featured" aria-labelleby="heading_id" id="7425639"> <!-- Article template --> <img src="..." alt="..." class="img"> <h2 id="heading_id"> <a href="/article/7425639">Read more</a> </h2> <p>Article description</p> <p>Posted on: <time>2024-12-17</time></p> </article>
attr
иprop
устанавливают атрибуты и свойства узлам.aria
иdata
— хелперы для ARIA-атрибутов и data-атрибутов, можно задавать и черезattr
/prop
. Остальные аргументы функции — children. Если передан массив, он джойнится. Примитивные значения и объектные обёртки преобразуются к строкам и вставляются как текстовые ноды.Пробовал разные варианты
if
иfor
, в итоге тоже получились функции, но вот с этим уже сложнее было. Потом натягивал на всё это реактивность через Proxy. В общем эксперимент получился интересный и чем-то похож на то, что описано в статье.fcrng Автор
14.12.2024 22:14Благодарю за комментарий. Не знаю где, но я уже видел этот подход, вот правда не помню где) Мне нравится это как альтернативная реализация. Полагаю тут может быть хороший выигрыш в памяти, за счет меньшего кол-во промежуточных структур. Но с другой стороны, это как-то как будто бы избыточно - уж очень многословно. Особенно вызов функции на каждый атрибут.
В теории можно добавить это в статью как еще один способ, для полноты картины, если вы не против.
Также, если данная реализация есть где-то на гитбах, я бы взглянул. Сбросьте в личку если есть такая возможность.
supercat1337
14.12.2024 22:14Честно говоря, я не считаю ужасной проблемой использование закрывающих парных тегов или фигурных скобочек в строках. Самое главное, это читаемость кода, возможность его редактирования и масштабирования без боли.
Вообще, шаблоны на бэке изначально были задуманы как альтернатива императивному коду, где смешивались представления, модели и логика. Собственно, если ни у кого вопросы по отделению данных от представления не возникали, то поводом для дискуссии стала возможность использования логики в шаблонах.
Отсюда возникли 2 течения: одно течение позволяет использовать любую логику в шаблоне (сложные условия, хитрые циклы и т.п.), другое - logicless - ограничивало использование логики (например, шаблонизатор mustache). Последнее, грубо говоря, отделяло понятие логики шаблона от бизнес-логики. То есть простые if-ы допустимы, а вот более сложные условия уже запрещались на уровне синтаксиса самого шаблона.
Проблема первого течения заключалась в том, что когда логика в шаблонах использовалась широко, то можно было легко выстрелить себе в ногу. Что касается logicless-подхода, то он оказался крайне ограничен в жизненных кейсах, потому что у вьюхи часто требуется своя "модель" (то есть данные должны преобразовываться в модель отображения).
Лично я сделал вывод, что самый правильный подход написания шаблонов находится где-то посередине. Если у части шаблона есть своя логика, то лучше ее вынести отдельно. Причем независимо как вы формируете html: функциями или строкой. Компонент, как и класс в программировании, должен отвечать конкретной цели.
Еще можно добавить, что декларативный подход шаблонов отлично юзается и на бэке, и на клиенте. То есть используется принцип DRY. При этом реализация "секции логики" будет отличаться на различных платформах.
Когда же мы говорим о проектировании UI на клиенте, то в этом случае логики становится гораздо больше. Есть необходимость связывания модели данных с элементами, его атрибутами, обработка событий. Несмотря на разность во взглядах как сформировать динамическую верстку, большинство приходит к выводу о том, что все связано с конкретным элементом, то лучше держать это близко к друг другу. То есть не должно разбрасываться по разным модулям. И неважно на jsx вы пишите или формируете DOM через tag template, либо вообще императивно через аналоги h-функции.
В общем, пишите на том, что вам ближе по духу, однако, если чувствуете, что надо рефакторить, то лучше рефакторить сразу :). Проблема не в скобочках, а в архитектуре.
Я же придерживаюсь того, что логики в шаблонах должно быть как можно меньше. Вся работа с элементами страницы должна быть вынесена в отдельную секцию кода. Вьюха должна оставаться вьюхой.
fcrng Автор
14.12.2024 22:14Даже несмотря на немалый опыт во фронте, единственное в чем я сейчас уверен - нативность) Без нее мы как будто бы движемся куда-то не туда. И уж слишком извращаемся, и усложняем все и вся для получения конечного результата.
Нельзя не согласиться с тем, что правда во всех этих рассуждениях где-то посередине, и ваш подход по сути где-то рядом.Мы ведь тем здесь и заняты, что ищешь и прощупываем эту середину)
SergeiZababurin
А если надо написать срочно 3 сервиса за 10 дней (условно) Какой способ потребует меньше усилий (количество нажатий кнопок мыши и движений рукой и компактность, что бы один пользовательский кейс желательно описывался в одном файле) для того что бы это сделать ?
divinity2000
Сферический конь в вакууме такого рода обычно решается просто подтягиванием любого готового UI кита, который закрывает 70% работы) Но если сложно писать большие конструкции в том же jsx, то всегда есть Pug
SergeiZababurin
Я спросил какой из вариантов потребует меньше усилий для использования при больших объемах работы. Или в этой статье описан сферический конь в ваккуме, который на практике не использовался ? Все способы, которые я использовал для формирования компонентов я могу сравнивать приводя плюсы и минусы подходов и могу сказать какой из них самый простой.
Например этот вариант мне не очень понравился
/
const template = `<div class="card"> <h1>${title}</h1> <p>${description}</p></div>`;
потом таких огрызков становится так много, что в них становится трудно ориентироваться.
А потом ещё функцию добавить
divinity2000
Если выбирать вариант из предложенных -- никакой. При больших объемах работ просто нужно иметь достаточно большую коллекцию компонент/шаблонов/сниппетов чтобы быстро накидывать интерфейс.
Вообще, насколько я понял автору не очень нравится то что мы используем html для описания dom, поэтому вместо отлаженного синтаксического сахара которые предоставляют фреймворки (которые, кстати, частично упрощают жизнь) он начал придумывать свои велосипеды.
Так уж исторически сложилось что для отображение в вебе мы используем частный случай xml и управляем мы им из js, только потому что "ну вот так вышло". И мы придумали способы для упрощения формирования страниц. Это шаблонизаторы -- twig, pug, razor и т.д., которые позволяют выполнить так называемый code splitting, что уже ускоряет разработку.
В общем, мы ничем более не ускоримся если просто будем менять один синтаксический сахар на другой. А хранить верстку в переменных или выбирать компонент из списка через условия, просто ради красивой функции, то это бред
fcrng Автор
Да, вы все правильно поняли.
С этим соглашусь - синтаксис тут вообще не причем, и ни на что не повлияет в рамках его задачи
Единственное - мне кажется моветном, что любая инициатива, любой андеграундный софт. У нас, у кодеров по дефолту зовется велосипедами)
divinity2000
Не совсем и не всегда) Просто если говорить о js фреймворках то там уже есть редер функции которые, по сути, являются некой абстракцией над
document.createElement
и заводить еще один уровень поверх кажется, как минимум, странным)А если судить о других технологиях -- то там XML строится для формирования модели интерфейса и где-то можно в явном виде на него воздействовать, а где то -- нет.
Если вот прям совсем не хочется с html контактировать -- можно воспользоваться canvas, но там придется реализовать свой DOM, который также может быть в JSON, XML или даже Yaml
fcrng Автор
С DOM в том и проблема, что в него заложено крайне мало возможностей по работе с элементами. Либо сделано это как-то криво, недоработано, не покрывает всех функций. Канвас это вообще про другое, с ним придется знатно намучиться и накостылить, прежде чем получишь достойную реализацию, чтобы потом отказаться от него и вернуться в DOM)
Прост интересная логика) Реакт и прочие реализации - не костыль, а любое предложение от обычного разраба - да.
Предвзятость какая-то)
без негатива если что
divinity2000
Ну так они реализуют виртуальный DOM, т.е. по сути еще один уровень абстракции, реализованный в js для управления DOM.
Тут просто нужно понимать что веб зарождался совсем не для того, чтобы мы использовали так как используем сейчас. Изначально он был придуман как файлообменник для документов в формате html (упрощенный LaTeX(латех) в формате xml, если прям ну очень грубо) и то что в него приплели js тоже дело случая, например, на тот момент action script шагнул чутка дальше чем js. И вот мы живем просто с наследием которое вообще не должно было работать так как работает сейчас, и поэтому кажется что "все не продуманно".
И да, по моему имхо фреймворки тоже костыли) А вся разница в костылях заключается только в том что кому то нравиться одно, а кому то другое и тут нет никакой предвзятости
fcrng Автор
Да с наследием все понятно, я говорю по текущей ситуации. Разумеется нужно держать в голове всю эту предысторию, понимать как к мы к этому пришли, шарить за пред технологии и все такое. Но в текущей ситуации костыль - это скорее старые базовые технологии чем реакт и прочие подобные ему решения. Они ведь не просто так появились, а потому что встроенные браузерные решения уже давно самостоятельно не покрывают тот пласт задач, который мы от них требуем
divinity2000
Так вот проблема в том что это все только кажется. Фреймворки придумали чтобы сократить трудозатраты и только. Все остальное, включая сами фреймворки, реализуется за счет стандартных средств, которые со временем расширяются. Сейчас в ванильном js (браузерном) можно даже файлы с флешки читать, даже vr приложуху запилить.
Не в обиду будет сказано, но примерно года с 2018 (если не раньше) люди перестали учить js, кроме каких то основ, а учат только фреймворки.
Возможно по этому возникают такие ощущения, что якобы js -- костыль, а реакт -- крутая фича, и это печально. Да порог входа вырос, но обучаются люди не с того конца сейчас.
Если капнуть исходники реакта -- то мы увидим все те же методы для работы с DOM
Ну и так, просто посмотри сколько всего есть в js в "стоке" -- https://developer.mozilla.org/ru/docs/Web/API
fcrng Автор
Как же вы все путаете и усложняете.
Не говорил я ничего подобного)
Вы зачем-то описываете некие очевидные вещи
Речь по сути лишь про стандарты, стандарты разработки.
И суть в том, что база не поспевает за рынком. Можно клепать сколько угодно апи под разные задачи - это все здорово. Но чисто фронт, он особо не меняется - базово. Бесконечно меняются лишь инструменты на которых мы пишем.
Главный вопрос - почему? Почему появляются все эти технологии - потому что нет никакой стандартной из коробки. Вот и все.
Отсюда и вывод про костыли между тем же отвратительным реактом и базой
divinity2000
Очень холиварно получилось, но продолжим)
Какие технологии?)
Реакт не технология и даже не фреймворк. Почему он появился? Потому что захотели менее трудозатратную разработку фронта, с порогом входа на уровне jQuery
Jsx тоже не технология -- это шаблонизатор.
Так и что в итоге за кем не поспевает?)
То что мы сейчас делаем на фронте -- это просто наши больные фантазии, которые работают через костыли типа SPA и велосипеды типа фреймворков.
Почему все это происходит -- потому что мы живем с тем "наследием" которое имеем, да мы можем придумывать все новые велосипеды, но и они будут иметь под капотом все то же "наследие" (пример -- классы в es6). А просто так взять и создать какой-то новый подход мы тупо не можем т.к. переход на него будет идти вечность, как пример -- мы дружно не начали юзать wasm :)
fcrng Автор
Технология это общее слово, точно также как Инструмент. Зачем к этому цепляться. То есть это штуковина, которая позволяет нам что-то создать/получить/улучшить с ее помощью.
В остальном - вы снова вкидываете столь очевидные вещи)
Как бы там не было раньше, это уже неважно.
Решает рынок. Рынку нужны что - быстрые способы готовить фронт, все.
Можно ли делать фронт быстро без фреймворков или либ-оберток? - нет.
Идеальны ли фреймворки - нет.
Но очевидно что без них фронта уже не будет. Но не в смысле их конкретных реализаций. А в смысле подхода. Не трудно понять что какая-либо минимальная обертка даже для натива - мастхев. Иначе разработка превратится в еще больший ад, чем с каким-то кривым фреймворком. Опять же, почему - потому что технологии для разработки ui, из коробки дают нам лишь примитивы. Над которыми нам ПРИХОДИТСЯ всячески извращаться чтобы получить желаемый результат.
Разрабы до сих пор ищут разрешение этих проблем, а вы говорите - это велосипеды, это не решаемо. Посмотрите на тот же хабр - тут тысячи докладов именно на тему фронта.
Никто не предлагает взять и вырезать то что уже существует.
Но можно попытаться адаптировать это под современные нужды. Под невероятно требовательные запросы рынка.
И именно база (html css js и др) не поспевает за рынком, а не наоборот
divinity2000
Ну блин, вы просто ищете ответы на риторические вопросы.
И я не говорю что это все "не решаемо", просто сейчас все эти адаптации решают одни и те же проблемы немного разными способами и на данный момент нет никакой панацеи.
А "база" и не должна успевать, имхо, без этого не будет развития.
Но с другой стороны я еще ни разу не видел чтобы "база" не могла решить проблемы бизнеса без каких то ухищрений. Опять же если касаться фреймворков с их модульностью и компонентами -- есть веб компоненты, теневой дом и umd, которые существуют давно, для реактивности -- mutation observer, например. И без всяких ухищрений можно писать на ванильке без оберток и не париться.
Предлагаю на этом остановиться. Я понимаю что вы имеете ввиду, но у меня просто свой взгляд на этот счет :)