Почему я не использую веб-компоненты

Автор оригинала: Rich Harris
  • Перевод

Я пишу это в основном для себя в будущем, чтобы у меня было куда сослаться, когда кто-нибудь спросит меня, почему я скептичен в отношении веб-компонентов и почему Svelte не компилируется в веб-компоненты по умолчанию. (Тем не менее, он может компилироваться в веб-компоненты, а так же интегрироваться с ними, что подтверждается превосходной оценкой на Custom Elements Everywhere).


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


1. Прогрессивное улучшение


Это может быть старомодным убеждением, но я считаю, что веб-сайты должны работать без JavaScript насколько это возможно. Веб-компоненты без JS не работают. Это нормально для вещей, которые по своей природе интерактивные, такие как кастомные элементы форм (<cool-datepicker>), но это ненормально для навигации сайта, например. Или представьте себе компонент <twitter-share>, который инкапсулирует в себе логику построения URL для отправки в Twitter. Я мог бы реализовать его на Svelte, что отрендерит на сервере мне вот такой HTML:


<a target="_blank" noreferrer href="..." class="svelte-1jnfxx">
  Tweet this
</a>

Другими словами, обычный <a> во всем его доступном великолепии.


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


В случае веб-компонента HTML выглядел бы как-то так:


<twitter-share text="..." url="..." via="..."/>

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


Кроме того, class="svelte-1jnfxx" предоставляет нам инкапсуляцию стилей без Shadow DOM. Что приводит нас к следующему пункту.


2. CSS в, эээ… JS


Если вы хотите использовать Shadow DOM для инкапсуляции стилей, то вам понадобится вставить свой CSS в тэг <style>. Единственный практичный способ это сделать, если вы хотите избежать моргания загружащегося контента (FOUC), это встроить CSS как строку в JavaScript, который определяет всю остальную логику вашего веб-компонента.


Это противоречит совету об улучшении производительности, который гласит: "поменьше JavaScript, пожалуйста". Сообщество CSS-in-JS, в частности, много критиковалось за неиспользование css-файлов для CSS, и вот, с веб-компонентами мы снова здесь.


В будущем, мы сможем использовать CSS Modules а также Constructable Stylesheets, чтобы справиться с этой проблемой. Еще у нас будет возможность стилизовать внутренности Shadow DOM через ::theme и ::part. Но и здесь не обошлось без проблем.


3. Усталость платформы



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

На момент написания, на https://crbug.com, баг-трекере Хрома, 61,000 открытых багов, которые показывают огромную сложность написания современного браузера.


Каждый раз когда мы добавляем в платформу новую фичу, мы увеличиваем сложность – создаем потенциал для новых багов и делаем все менее вероятным то, что у Хрома появится новый конкурент. Это также создает сложности для разработчиков, которых призывают учить эти новые фичи (некоторые из которых, например HTML Imports или изначальня версия стандарта Custom Elements, никак не прижились за пределами Google и теперь в процессе удаления).


4. Полифилы


То что вам нужно использовать полифилы для поддержки старых браузеров, не способствует развитию ситуации. И это совсем не помогает, что статьи на тему Constructable Stylesheets, написанные в Google (привет, Джейсон!), никак не упоминают, что эта фича доступна только в Chrome. (Все три автора спецификации работают в Google. Webkit, кажется, имеет сомнения по поводу некоторых аспектов этого стандарта).


5. Композиция


Бывает полезно контролировать, когда содержимое слота должно отрендериться. Представьте, что у вас есть компонент <html-include> для загрузки какого-то дополнительного контента, когда он виден:


<p>Toggle the section for more info:</p>
<toggled-section>
  <html-include src="./more-info.html"/>
</toggled-section>

Внезапно! Даже если мы еще не открыли toggled-section, но браузер уже запросил more-info.html, вместе со всеми изображениями и другими ресурсами, что там есть.


Это происходит потому что содержимое слотов рендерится в веб-компонентах заранее. В реальности же оказывается, что в большинстве случаев вы хотите рендерить содержимое слотов лениво. Svelte v2 принял упреждающую модель реднеринга чтобы соотвествовать веб-стандартам, но это оказалось основным источником неудобств – мы не могли создать что-то похожее на React Router, например. В Svelte v3 мы отошли от поведения веб-компонентов и ни разу не оглядывались назад.


К сожалению, это была одна из фундаментальных характеристик DOM. Что приводит нас к...


6. Путаница между свойствами и атрибутами


Свойства и атрибуты это же, в принципе, одно и тоже, правда?


const button = document.createElement('button');

button.hasAttribute('disabled'); // false
button.disabled = true;
button.hasAttribute('disabled'); // true

button.removeAttribute('disabled');
button.disabled; // false

Ну почти:


typeof button.disabled; // 'boolean'
typeof button.getAttribute('disabled'); // 'object'

button.disabled = true;
typeof button.getAttribute('disabled'); // 'string'

Бывают имена, которые не совпадают:


div = document.createElement('div');

div.setAttribute('class', 'one');
div.className; // 'one'

div.className = 'two';
div.getAttribute('class'); // 'two'

… а есть и такие, которые вообще не согласованы:


input = document.createElement('input');

input.getAttribute('value'); // null
input.value = 'one';
input.getAttribute('value'); // null

input.setAttribute('value', 'two');
input.value; // 'one'

Но мы бы смогли справиться с этими причудами, взаимодействия строкового формата (HTML) и DOM. Есть конечное число этих особенностей, они задокументированы, так что мы хотя бы можем о них узнать, при наличии времени и терпения.


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


class MyThing extends HTMLElement {
  static get observedAttributes() {
    return ['foo', 'bar', 'baz'];
  }

  get foo() {
    return this.getAttribute('foo');
  }

  set foo(value) {
    this.setAttribute('foo', value);
  }

  get bar() {
    return this.getAttribute('bar');
  }

  set bar(value) {
    this.setAttribute('bar', value);
  }

  get baz() {
    return this.hasAttribute('baz');
  }

  set baz(value) {
    if (value) {
      this.setAttribute('baz', '');
    } else {
      this.removeAttribute('baz');
    }
  }

  attributeChangedCallback(name, oldValue, newValue) {
    if (name === 'foo') {
      // ...
    }

    if (name === 'bar') {
      // ...
    }

    if (name === 'baz') {
      // ...
    }
  }
}

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


7. Протекающий дизайн


Этот пункт немного расплывчатый, но мне кажется странным, что attributeChangedCallback это просто метод класса. Вы можете сделать буквально следующее:


const element = document.querySelector('my-thing');
element.attributeChangedCallback('w', 't', 'f');

Атрибуты не поменялись, но код ведет себя так, как будто это произошло. Конечно, в JavaScript всегда было много способов навредить, но когда я вижу торчащую таким образом деталь реализации, мне кажется что с дизайном что-то явно не так.


8. Плохой DOM


Ок, мы уже установили, что DOM – плохой. Но все еще тяжело преувеличить, насколько это неудобный способ делать интерактивные приложения.


Несколько месяцев назад я написал статью "Пишите меньше кода", призванную проиллюстрировать как Svelte позволяет писать компоненты более эффективно, чем фреймворки вроде React и Vue. Там не было сравнения с ванильным DOM, а должно бы. Вкратце, у нас есть простой компонент <Adder a={1} b={2}/>:


<script>
  export let a;
  export let b;
</script>

<input type="number" bind:value={a}>
<input type="number" bind:value={b}>

<p>{a} + {b} = {a + b}</p>

Вот и все дела. А теперь напишем то же самое через веб-компонент:


class Adder extends HTMLElement {
  constructor() {
    super();

    this.attachShadow({ mode: 'open' });

    this.shadowRoot.innerHTML = `
      <input type="number">
      <input type="number">
      <p></p>
    `;

    this.inputs = this.shadowRoot.querySelectorAll('input');
    this.p = this.shadowRoot.querySelector('p');

    this.update();

    this.inputs[0].addEventListener('input', e => {
      this.a = +e.target.value;
    });

    this.inputs[1].addEventListener('input', e => {
      this.b = +e.target.value;
    });
  }

  static get observedAttributes() {
    return ['a', 'b'];
  }

  get a() {
    return +this.getAttribute('a');
  }

  set a(value) {
    this.setAttribute('a', value);
  }

  get b() {
    return +this.getAttribute('b');
  }

  set b(value) {
    this.setAttribute('b', value);
  }

  attributeChangedCallback() {
    this.update();
  }

  update() {
    this.inputs[0].value = this.a;
    this.inputs[1].value = this.b;

    this.p.textContent = `${this.a} + ${this.b} = ${this.a + this.b}`;
  }
}

customElements.define('my-adder', Adder);

Да уж.


Заметьте, если мы синхронно изменим и a, и b, то у нас будут два отдельных обновления. Фреймворки в большинстве своем от этой проблемы не страдают.


9. Глобальные имена


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


10. Все эти проблемы уже решены


Самую большую печаль вызывает то, что у нас уже есть хорошие компонентные модели. Мы все еще учимся, но базовая задача – синхронизация view с некоторым состоянием через обновление DOM в компонентно-ориентированном стиле – уже решена несколько лет как. И мы все еще добавляем фичи в веб-платформу только для того чтобы догнать то, что мы уже имеем в библиотеках и фреймворках.


Поскольку наши ресурсы не бесконечны, время потраченное на одну задачу означает недостаток внимания другой задаче. Значительная энергия была потрачена на веб-компоненты, несмотря на общее безразличие разработчиков. Чего бы мы смогли достичь, потратив эту энергию на что-нибудь другое?

Поделиться публикацией

Похожие публикации

Комментарии 53

    +11

    11. То, о чём все забывают. Производительность.
    Каждый веб-компонент, будучи создаваем с помощью JS, затрачивает много больше процессорного времени, чем построение обычного, стандартного элемента DOM.

      –3

      Лично меня напрягают тормоза лейаута после ресайза. Впрочем на флеше было ненамного быстрее но там была песочница....

        +2
        тормоза лейаута после ресайза

        если у вас лейаут считается в js-e, то это будет тормозить на любом фреймворке.

      0

      В платформу надо добавлять лишь то, что невозможно реализовать на уровне библиотек. Fibers, например.

        0

        Как с помощью библиотек реализовать элемент с изолированным css и не торчащими наружу кишками (ShadowDOM)?

          0

          Как на самосвале добраться из пункта А в пункт Б, не используя самосвал? Наверное никак.


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

            –2
            Насчёт кишок я не понял о чём вы

            я про внутренний dom компонента, зачем он мне торчащий наружу.


            Неймспейсы и маглинг имён решают проблему конфликтов не хуже изоляции.

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


            Представь ситуацию: крупная компания, количество фронтенд-команд перевалило за десяток, каждая сама выбирает удобный ей фреймворк и базовый набор стилей. Можно, конечно, определить корпоративный стандарт, но это существенно усложнит поиск новых разработчиков. Кроме того проекты достаточно долгоживущие, фреймворки отмирают быстрее. При этом компания хочет свой корпоративный стиль, свои компоненты, отличающиеся от стандартных, часто не только цветом. Что делать? Разрабатывать и поддерживать библиотеки компонентов под каждый используемый фреймворк? Дороговато выходит.
            Или другая ситуация: ты работаешь в компании A и разрабытываешь библиотеку компонент на фреймворке X. А потом либо меняется компания A и в компании B используется фреймворк Y, либо фреймворк X отмирает и все хотят Y. Куча работы вылетает в трубу, а ведь класный набор компонентов получился, хотелось бы дальше применять.
            Веб-компоненты тут идеальное решение, ShadowDOM спрячет лишний внутренний dom, который теперь не будет мешать фреймворкам, глобальные стили не будут заставлять компонент расползаться, но, в то же время, есть хитрые css-селекторы позволяющие при необходимости что-то поменять внутри. Другими словами, получающиеся компоненты полностью автономны, так же как и уже встроенные в браузер input, select, video и тд. Из коробки веб-компоненты не очень удобны, но большинство существующих проблем решается легковесной обёрткой, опять же никак не мешающей существующим фреймворкам.

              0
              я про внутренний dom компонента, зачем он мне торчащий наружу.

              Где? Речь про вкладку Elements в девтулзах? Когда всё приложение построено на shadow-dow — вам всё равно придётся открывать кучу веток этих самых кишок. Только ещё и путаться без конца будете между этими деревьями.


              базовый набор стилей

              Не надо так.


              усложнит поиск новых разработчиков

              Не усложнит.


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

              Почему бы ей не захотеть не только единый стиль, но и единое поведение, и единую реализацию, и единый фреймворк?


              Разрабатывать и поддерживать библиотеки компонентов под каждый используемый фреймворк?

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


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

              Так применяйте, какие проблемы?


              есть хитрые css-селекторы позволяющие при необходимости что-то поменять внутри

              Это какие? Когда я последний раз смотрел — их все задепрекейтили.


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

              Ну конечно, и ленивый реактивный рендеринг к ним легко прикрутить?

                +1
                вам всё равно придётся открывать кучу веток этих самых кишок

                не прийдётся. Не обязательно и может даже не нужно обычные въюхи в ShadowDOM запихивать, он обязательно используется только для компонент вроде селекта, табов и подобного. Но кишки мешают больше не мне в devtools, а как раз при использовании таких компонентов в других фреймворках самим этим фреймворкам.


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

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


                Почему бы ей не захотеть не только единый стиль, но и единое поведение, и единую реализацию, и единый фреймворк?

                веб-компоненты с большой вероятностью проживут намного дольше, чем любой из существующих сегодня фреймворков, так зачем привязывать себя к какому-то из них, ждать когда он отомрёт и по новой переписывать каждую рюшечку? Если ui-библиотека сделана на веб-компонентах, то пусть разработчики использующие её пишут на чём им удобно.


                Ну конечно, и ленивый реактивный рендеринг к ним легко прикрутить?

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


                Не усложнит.

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


                Это какие? Когда я последний раз смотрел — их все задепрекейтили.

                ну видимо когда-то предложат что-то на замену или достаточно того, что осталось, я сейчас больше про общую идею, а не про текущую её реализацию, которая пока да, не идеальна. Сам я из веб-компонентов использую только CustomElements и HTMLTemplates. ShadowDOM полноценно не полифилится, а существующие недополифилы сильно жрут производительность. Поэтому я не особо в курсе что там с селекторами. Использую привычный БЭМ.


                о чём вы

                применяйте

                я себя лет на 20 старше чувствую)). Зачем вообще на хабре все выкают? В офисах и на конференциях все на ты, а здесь ощущение, как будто сплошные доктора наук собрались).

                  0
                  не нужно обычные въюхи в ShadowDOM запихивать

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


                  не выглядит как совмещение единорога с китом

                  Использование html для композиции компонент и костыли с аттрибутами/свойствами/слотами — это уже совмещение единорога с китом.


                  ждать когда он отомрёт

                  Ничего с ним не случится, если выбирать/создавать фреймворк исходя из потребностей, а не по принципу "что там сейчас в моде?". Если гнаться за модой, то сначала выбираются одни кривые технологии, а через пару лет они меняются на другие, не менее кривые.


                  Подобное нужно реализовывать на уровне конкретного компонента

                  Зачем что-то делать, если можно это не делать, а оставшееся время потратить на что-то более полезное?


                  найти разработчика на какой-то конкретный фреймворк

                  Не надо "искать на фреймворк". Толковый разработчик быстро освоит любой фреймворк. Бестолковый же и на своём любимом такого накреативит, что будете плакать кровавыми слезами.


                  Например, насколько легко находить разработчиков на mol?

                  Элементарно. Подойдёт любой знакомый с тайпскриптом. Пара недель и он уже контрибьютит во фреймворк. Прецеденты были.

                    +1

                    В общем, я свою точку зрения высказал, принимать что-то или нет — дело твоё. Дальше препираться смысла не вижу, по моему опыту это не приводит ни к чему кроме потери времени. Удачи!

          0

          Раз уж на то пошло, а какую уникальную функциональность добавляют Fibers, которую не заменит async/await?

            0

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

              0

              Не вижу здесь ничего такого, что невозможно реализовать в userland. Так что Fibers не вписываются под те же критерии, что вы применяете для ShadowDOM в треде выше.

                –1

                Ок, как извне остановить асинхронную функцию? Как в обработчике, например, события click сделать асинхронный вызов после чего решить отменять ли дефолтное поведение?

                  +1
                  как извне остановить асинхронную функцию

                  AbortController. Его можно не только с fetch использовать, но и для своего асинхронного кода


                  Как в обработчике, например, события click сделать асинхронный вызов после чего решить отменять ли дефолтное поведение

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

                    –2
                    AbortController.

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


                    Интерфейс будет заблокирован, ожидая ответа на событие.

                    В том-то и дело, что нет.

                      +1
                      только специально подготовленной.

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


                      Интерфейс будет заблокирован, ожидая ответа на событие.

                      В том-то и дело, что нет.

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

                        –2
                        код завершился

                        С файберами он не "завершается", а приостанавливается.


                        запрос ушел на сервер

                        Не ушёл, так как событие ещё не доплыло до корня документа.

                0
                function* myFiber() {
                        console.log('делаю работу')
                        yield* subFiber1('декомпозирую работу')
                        yield subPromise2('жду работу')
                        yield [ subPromise4('параллелю работу'), subPromise5('параллелю работу') ]
                        console.log('могу остановиться когда угодно')
                }
                
                async function fibersManager() {
                        const myFiberInstance = myFiber()
                        let result
                        let param
                        do {
                                result = myFiberInstance.next(param)
                                param = await (Array.isArray(result.value)
                                    ? Promise.all(result.value)
                                    : Promise.resolve(result.value)) // TODO: support subFibers
                                if (нужно_прерваться) return;
                        } while(!result.done) 
                }
                
                fibersManager()
                

                В JS файберы уже давно есть, только их генераторами назвали.
                  –2

                  То, что в JS — это stackless coroutines. А файберы — это stackfull coroutines. Попробуйте на генераторах реализовать это:


                  ondragstart = event => {
                      if( servers.some( server => server.loadData().isAvailable ) ) {
                          event.preventDefault()
                      }
                  }
                    –1

                    Дмитрий, как реализованы fiber. Через отдельный процесс?

                      0

                      Если речь про $mol_fiber, то это всего-лишь эмуляция настоящих файберов, реализованная на обычных функциях, исключениях и кешировании.

                      +2

                      Да-да, отличная идея — делать запрос на сервер для того, чтобы определить можно ли перетаскивать элемент… А браузеру в это время что пользователю показывать-то?

                        0

                        Песочные часики возле курсора.

                          0

                          Так сам элемент-то при этом должен двигаться или нет? Ответ на этот вопрос должен быть дан независимо от формы курсора.

                            0

                            Да в принципе без разницы. Имейте ввиду, что сервер может быть локальным. А запрос может быть не только к серверу, но и, например, в воркер. Вы бы не к конкретному примеру докапывались, а смотрели бы шире на проблему — половина апи синхронные, половина асинхронные. И то и дело появляется необходимость вызывать асинхронные из синхронных.

                              +1

                              Если "в принципе без разницы" — то делаем вот так и никакой проблемы нет:


                              ondragstart = event => {
                                  servers.some( server => server.loadData().isAvailable );
                              }

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

                                0

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

                                  +2

                                  Так вы же пишете, что без разницы что там таскается?


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

                        –1
                        function* ondragstart(event) {
                            if(servers.some(yield server => server.loadData().isAvailable))
                                event.preventDefault()
                        }
                        

                        Вы это имели ввиду?
                          +1

                          Вы это браузеру скормить попробуйте.

                            0
                            Промазал илдом, простите, с телефона набирал. Но я уверен, что вы поняли суть :).
                              0

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

                                0
                                Ну так оберните, раз уж вы выбрали модель корутин. Либо ваши источники событий пусть поддерживают корутины, либо сделайте совершенно несложную обёртку генератора в промис.
                                  0

                                  preventDefault, dataTransfer и другие обработчики выше по дому вас ждать не будут.

                                    0
                                    Да, как вам написали выше, это сделано не зря. Делать корутиновый DOM не стали по разумным причинам. Но вы из своей любви к корутинам вполне можете написать обёртку и для ДОМа, организовав асинхронный рендеринг. Я к тому, что в ЖС для этого есть ВСЁ. Но асинхронно зажигать люминофор в пикселях никому не нужно, после всех хитрых изменений модели, когда вы уже будете готовы к самовыражению, ДОМ примет вас, синхронно отрисовав всю вашу сложнейшим асинхронным образом построенную модельку :). Поэтому DOM синхронный, так удобнее программировать. (Во всех мне известных графических операционных системах GUI-поток — единственный, никто не пытается переключать пиксели из двух потоков, с этим справится и один, а остальные потоки пусть только рассказывают ему, что изменилось в данных.)
                                      0

                                      Про костыль с waitUntil слышали?

                                        0
                                        Не слышал, почитал, спасибо. Не очень понял, что вы пытаетесь этим сказать :). Вам уже и обёртку почти написали, получается, оборачивать генераторы проще простого :).
                                          0

                                          Думаю и не поймёте, пока не попрограммируете на языке с поддержкой файберов.

                                            0
                                            Да понимаю, корутины — действительно, очень мощная и универсальная идея, я и сам кооперативной многозадачностью «болею», пытаясь нивелировать издержки менеджера вытесняющей многозадачности :). Я наколеночный язык даж пилю на досуге, в котором корутины пишутся без синтаксического мусора (мне для микроконтроллера нужно, но пока синтаксис отрабатываю на учебном транспиляторе в ЖС :)).

                                            Но нужно понимать, что любая универсальная идея всегда пытается стать богом, захватить мозг и заставить вас думать, что ВСЁ является проекцией этой идеи :). Корутины — мощный инструмент, но ему вовсе не нужно быть абсолютно глобальным, есть принципиально синхронные вещи, над которыми корутины и надстраиваются.
                –1
                Без JS? В 2019 году? Вам не жалко своих пользователей, у которых не будет валидации форм, навигации без перезагрузки страницы и других давно привычных вещей?
                  +6
                  не будет валидации форм

                  Будет, есть же встроенная валидация через html-атрибуты


                  навигации без перезагрузки страницы

                  Если страница мало весит и быстро загружается с сервера, то в чем проблема? Зачем чинить то что не сломано?

                    0
                    Будет, есть же встроенная валидация через html-атрибуты
                    А в Safari оно уже работает?
                      0

                      Да. Сейчас сам проверил, валидация вызывается.

                        0
                        Даже если не работает, то ничего не сломается, изящная деградация во все поля. В отличии от отключения JS на сайте, написанном только под его использование.
                        +1
                        У моего банка клиент сделан на генерируемых сервером страницах, поэтому когда я в 5й раз открываю страницу со списком шаблонов платежей, я должен ждать несколько секунд пока сервер сгенерирует мне эту несчастную страницу, так что лучше уж JS и рендеринг страниц на стороне клиента с кешированием списков данных.
                          +4
                          Это проблема криво написанного бекенда. Уверяю, с таким подходом у них и рендеринг на стороне пользователя будет тормозить и жрать свой гигабайт оперативной памяти.
                            –3
                            Фронт и бэк пишут разные люди.
                            Да, с текущими реактами и «модными» технологиями в нем, фронт будет тоже тормозить.
                            Но! Я уже не буду 5 секунд ждать пока бэк сгенерирует несчастную страницу со списком.
                              +3

                              Ну да, теперь вы будете ждать 10 секунд пока эту страницу вам сгенерирует фронт...

                        +5
                        навигации без перезагрузки страницы

                        Порой голый HTML с толикой CSS и JS с нуля рендерит страницу быстрее, чем отработают все эти новомодные реакты.
                          +1

                          Что мешает сделать валидацию и навигацию без перезагрузки страницы опциональной плюшкой сбоку, которая при включенном JS будет работать, а при отключенном JS «изящно деградировать» в «классический» сайт?

                          –1
                          Я не знаю насколько здесь можно прикладывать картинки. Но по моему Backend за Svelte будет все равно сильно больше.
                          На всякий случай приложил код создания компонента в RiotJS 3ей версии, потому что в 4 они там начудили не по детски. сам Riot с компилятором тянет дополнительно 36кб.
                          компоненты разбиваются на файлы, можно скомпилировать на сервере и будет уже только .js подтягиваться. Я возможно офтоплю сильно и сравнение не слишком корректно, но просто я думаю что у RiotJS тоже компонентная архитектура, по крайней мере я ее себе так представляю.
                          <myform>
                                      <input type="number" ref="a" oninput={change}>
                                      <input type="number" ref="b" oninput={change}>
                          
                                      <p>{opts.result}</p>
                          
                                      var self = this;
                                      change = function() {
                                          var a = isNaN(parseInt(self.refs.a.value)) ? 0 : parseInt(self.refs.a.value);
                                          var b = isNaN(parseInt(self.refs.b.value)) ? 0 : parseInt(self.refs.b.value);
                          
                                          opts.result = a + b;
                                          self.update();
                                      }
                                  </myform>
                          

                          image

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

                          Самое читаемое