DevGang
Авторизоваться

Улучшение Vanilla веб-компонентов

Вы уже знакомы с подключением внешних сторонних веб-компонентов в приложении Enhance? Недостатком этих внешних компонентов является то, что они не отображаются на стороне сервера, поэтому они страдают от ужасной вспышки нестандартного пользовательского элемента (FOUCE), и если что-то пойдет не так с JavaScript, они вообще не будут отображаться!

В этом посте мы покажем вам, как улучшить другой веб-компонент Vanilla, wc-icon-rule, чтобы он был полностью развернут на клиенте, избегая использования теневого DOM.

wc-icon-rule

wc-icon-rule создает для вас горизонтальное правило с изображением в центре, которое разбивает линию. Изображение предоставляется в качестве слота для веб-компонента. Этот компонент представляет собой чисто презентацию без какой-либо интерактивности на стороне клиента, что сделает преобразование в улучшенный компонент довольно простым.

Исходный код для wc-icon-rule

Преобразование в Enhance компонент

Хотя wc-icon-rule - это небольшой веб-компонент, все равно необходимо заняться преобразованием, как если бы это был более крупный и сложный веб-компонент, чтобы показать вам, как мы бы справились с этой задачей.

Шаг 1: Создайте новый компонент Enhance

Во-первых, нам понадобится новый компонент Enhance для представления wc-icon-rule. Для этого нам нужно создать новый файл с именем app/elements/wc-icon-rule.mjs. Содержимое файла, для начала, является:

export default function Element ({ html, state }) {
  return html``
}

Это основа каждого компонента Enhance.

Шаг 2: Добавьте текущее правило wc-icon-rule

Далее мы возьмем исходный код для wc-icon-rule и поместим его в тег script в нашей функции рендеринга html.

export default function Element ({ html, state }) {
  return html`
  <script type="module">
export class WCIconRule extends HTMLElement {
  constructor () {
    super()
    this.__shadowRoot = this.attachShadow({ mode: 'open' })
    const template = document.createElement('template')
    template.innerHTML = WCIconRule.template()
    this.__shadowRoot.appendChild(template.content.cloneNode(true))
  }

  connectedCallback () {
    this.setAttribute('role', 'presentation')
    for (const child of this.children) {
      child.setAttribute('role', 'none')
    }
  }

  static template () {
    return \`
      <style>
        :host {
          display: block;
          overflow: hidden;
          text-align: center;
        }
        :host:before,
        :host:after {
          content: "";
          display: inline-block;
          vertical-align: middle;
          position: relative;
          width: 50%;
          border-top-style: var(--hr-style, solid);
          border-top-width: var(--hr-width, 1px);
          border-color: var(--hr-color, #000);
        }
        :host:before {
          right: var(--space-around, 1em);
          margin-left: -50%;
        }
        :host:after {
          left: var(--space-around, 1em);
          margin-right: -50%;
        }

        ::slotted(*) {
          display: inline-block;
          width: var(--width, 32px);
          height: var(--height, 32px);
          vertical-align: middle;
        }
      </style>
      <slot></slot>
    \`
  }
}

customElements.define('wc-icon-rule', WCIconRule)
</script>`
}

Сейчас мы рендерим компонент на сервере, но мы по-прежнему отправляем все это в виде тега script, поэтому мы не исправили проблему с FOUCE.

Шаг 3: Извлеките свои стили

Тег style для компонента отображается в функции template. Это совершенно нормально, но с расширенной возможностью добавлять стили к тегу head мы можем извлечь его из тега script компонента.

Итак, давайте переместим этот тег style над нашим тегом script.

export default function Element ({ html, state }) {
  return html`
  <style>
    :host {
      display: block;
      overflow: hidden;
      text-align: center;
    }
    :host:before,
    :host:after {
      content: "";
      display: inline-block;
      vertical-align: middle;
      position: relative;
      width: 50%;
      border-top-style: var(--hr-style, solid);
      border-top-width: var(--hr-width, 1px);
      border-color: var(--hr-color, #000);
    }
    :host:before {
      right: var(--space-around, 1em);
      margin-left: -50%;
    }
    :host:after {
      left: var(--space-around, 1em);
      margin-right: -50%;
    }

    ::slotted(*) {
      display: inline-block;
      width: var(--width, 32px);
      height: var(--height, 32px);
      vertical-align: middle;
    }
  </style>
  <script type="module">
export class WCIconRule extends HTMLElement {
  constructor () {
    super()
    this.__shadowRoot = this.attachShadow({ mode: 'open' })
    const template = document.createElement('template')
    template.innerHTML = WCIconRule.template()
    this.__shadowRoot.appendChild(template.content.cloneNode(true))
  }

  connectedCallback () {
    this.setAttribute('role', 'presentation')
    for (const child of this.children) {
      child.setAttribute('role', 'none')
    }
  }

  static template () {
    return \`
      <slot></slot>
    \`
  }
}

customElements.define('wc-icon-rule', WCIconRule)
</script>`
}

Если вы проверите свою страницу в инструментах разработки вашего браузера, вы заметите тег style в теге head вашей страницы. Правила CSS в теге style компонента wc-icon-rule были перенесены на страницы тег style. Пользователь заметит, что Enhance слегка переписывает ваши правила CSS, так что :host становится правилом wc-icon-rule, чтобы правильно настроить таргетинг на все правила wc-icon-rule на вашей странице.

Шаг 4: Удалите Shadow DOM

Как упоминалось ранее в этом посте, вам не нужен Shadow DOM для такого компонента, как этот. Давайте избавимся от нашей зависимости от теневого DOM.

Во-первых, полностью удалите функцию constructor. Нам это не нужно. Далее давайте переместим <slot></slot> из нашей функции template и включим его в тег script. Наконец, удалите остальную часть функции template, поскольку теперь она, по сути, не работает.

Ваш код должен выглядеть следующим образом:

export default function Element ({ html, state }) {
  return html`
  <style>
    :host {
      display: block;
      overflow: hidden;
      text-align: center;
    }
    :host:before,
    :host:after {
      content: "";
      display: inline-block;
      vertical-align: middle;
      position: relative;
      width: 50%;
      border-top-style: var(--hr-style, solid);
      border-top-width: var(--hr-width, 1px);
      border-color: var(--hr-color, #000);
    }
    :host:before {
      right: var(--space-around, 1em);
      margin-left: -50%;
    }
    :host:after {
      left: var(--space-around, 1em);
      margin-right: -50%;
    }

    ::slotted(*) {
      display: inline-block;
      width: var(--width, 32px);
      height: var(--height, 32px);
      vertical-align: middle;
    }
  </style>
  <script type="module">
export class WCIconRule extends HTMLElement {
  connectedCallback () {
    this.setAttribute('role', 'presentation')
    for (const child of this.children) {
      child.setAttribute('role', 'none')
    }
  }
}

customElements.define('wc-icon-rule', WCIconRule)
  </script>
  <slot></slot>`
}

Мы по-прежнему оставим connectedCallback на месте. Нам больше не нужен Shadow DOM, но мы все еще можем улучшить наши веб-компоненты, отображаемые на стороне сервера, с помощью JavaScript, чтобы добавить интерактивную функциональность.

Заключение

Смогли бы вы справиться со сжатием этих четырех шагов в один шаг, но хотелось бы четко объяснить, почему мы пишем компоненты так, как мы делаем с Enhance.

Хотя по сути нет ничего плохого в том, как написаны веб-компоненты Vanilla JS, изменив способ их доставки в браузер, вы можете избежать распространенных проблем с веб-компонентами, таких как FOUCE, и уменьшить общий объем JavaScript на вашей странице, что важно для производительности и доступности.

#JavaScript
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу