Создайте собственный веб-компонент для онлайн-чата с помощью Ably и AWS

Веб-компоненты - отличный способ создать многоразовые функции, которые можно использовать на разных веб-страницах и веб-приложениях. Представьте себе совместное использование компонентов между разными фреймворками, такими как React, Vue.js или Next.js! В этом посте мы углубимся в веб-компоненты и покажем вам, как создать веб-компонент чата с помощью Ably и использовать его в приложении, созданном с помощью AWS Amplify и AWS Lambda.

Веб-компоненты - это набор веб-технологий, стандартизированных в браузере, для облегчения написания многоразовых частей разметки и функциональности. Они представляют собой комбинацию Custom Elements, Shadow DOM и HTML-шаблонов. Когда они используются все вместе, они все вместе называются Веб-компоненты. Веб-компоненты - это альтернатива подходам к повторному использованию компонентов, специфичным для конкретной платформы, таким как компоненты React и шаблоны Vue.js. Веб-компоненты интересны тем, что они не привязаны к какой-либо среде выполнения и поддерживаются всеми современными браузерами. Конкурирующие подходы к компонентам для Интернета были установлены до того, как веб-компоненты получили широкую поддержку, что вынудило многие фреймворки разработать свои собственные подходы к повторному использованию и инкапсуляции. Веб-компоненты предлагают независимый от фреймворка способ создания многоразовой функциональности для браузера.

Говоря о веб-компонентах, большинство людей ссылаются на настраиваемые HTML-элементы, поскольку они наиболее близки к моделям компонентов в React или Vue. Пользовательские элементы могут:

  • Рендеринг HTML в себя, установив свои собственные свойства innerHTML.
  • Инкапсулируйте данные, сохраняя их как атрибуты в самих экземплярах.
  • Отслеживайте изменения данных, хранящихся в их атрибутах, и запускайте повторную визуализацию.
  • Позволяют вам писать код, который запускается, когда они добавляются и удаляются из DOM.

Первое, что мы сделаем в этом посте, - это создадим пример настраиваемого элемента, который отображает количество нажатий кнопки. Чтобы создать этот настраиваемый элемент, нам нужно создать новый файл JavaScript, на который мы можем ссылаться с нашей веб-страницы. Начните с создания нового файла с именем index.js. Мы определим класс для настраиваемого элемента и назовем его CountComponent. Это расширит HTMLElement (базовый тип браузера для всех его элементов):

Внутри этого класса нам нужно определить атрибуты, которые необходимо соблюдать для change tracking. Для этой демонстрации мы вернем массив из одной строки - имя атрибута count:

Затем нам нужно определить некоторые настраиваемые атрибуты и их поведение. Мы создаем getter для настраиваемого атрибута «count», и в функции значение атрибута count загружается с вызовом getAttribute, по умолчанию используется строка "0". После загрузки данных мы анализируем их из JSON - мы делаем это, потому что любые данные, которые мы храним, должны быть строкой.

Нам также необходимо создать установщик для нашего атрибута, и здесь мы сериализуем значение в JSON и устанавливаем его в нашем элементе:

Несмотря на то, что пользовательские элементы определены как классы, вы не можете поместить в их конструкторы никакой логики, кроме вызова super для запуска базовой логики для класса HTML Element. Добавьте любую другую логику или пропустите вызов super, и вы увидите ошибку в консоли, и элемент не будет работать.

Поскольку мы не можем выполнять какую-либо значимую работу в конструкторе, пользовательские элементы предоставляют обратные вызовы жизненного цикла - функции, которые вы можете реализовать на разных этапах жизненного цикла компонента. Мы будем использовать функцию connectedCallback. Реализация этого обратного вызова приведет к запуску кода при добавлении компонента в DOM - это похоже на функцию React componentDidMount. В connectedCallback мы устанавливаем значение по умолчанию для счетчика и вызываем функцию, которую мы вскоре рассмотрим, под названием setContent:

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

Пользовательские элементы также предоставляют нам attributeChangedCallback. Мы будем использовать эту функцию для выполнения кода при изменении observedAttribute:

setContent - это функция, которую компонент вызывает, когда компонент добавляется в DOM и когда происходит обновление. Обработчик кликов в приведенном выше коде может получить доступ к атрибутам элемента с помощью ключевого слова this, поэтому при каждом щелчке мы увеличиваем значение, в свою очередь, вызывая повторный рендеринг. Примечание. Эта демонстрация не особо ориентирована на производительность, поскольку она устанавливает весь innerHTML и подключает обработчик кликов к кнопке при каждом ее вызове. Это не то, как можно было бы создать приложение для производства! В реальном приложении вы бы не сбросили весь свой DOM. Вместо этого вы измените только те части пользовательского интерфейса, которые требуют обновления. Кроме того, API Shadow DOM можно использовать в сценариях, более чувствительных к производительности.

Наконец, в конце файла JavaScript мы вызываем customElements.define API браузера, предоставляя имя тега для элемента и ссылку на класс, который мы только что определили.

Теперь можно ссылаться на файл JavaScript Custom Element как на модуль и добавлять элемент, который мы только что создали, на страницу, используя имя тега, которое мы определили:

Компонент отрисовывается так:

Мы собираемся создать компонент текстового чата - компонент Ably Chat - путем создания двух настраиваемых элементов:

  • AblyBaseComponent.js - базовый компонент, который инкапсулирует Ably взаимодействия
  • AblyChatComponent.js - элемент, который наследуется от AblyBaseComponent и реализует логику чата поверх Ably.

Базовый компонент спроектирован так, чтобы строить и инкапсулировать базовые требования для управления ключами API Ably и подписки на каналы. Используя этот базовый компонент, мы могли бы создать любой пользовательский элемент, который полагается на обмен сообщениями в реальном времени.

Для подключения к Ably вам понадобится Ably account и API-ключ.

Сборка базового компонента

Базовый компонент начинается со стандартного шаблона Custom Element - AblyBaseComponent расширяет HTMLElement и вызывает его конструктор.

В функции connectedCallback мы вызываем this.connectToAblyChannel (который мы вскоре рассмотрим):

Функция disconnectedCallback выполняет цикл this.subscribedChannels (который мы создадим в функции connect) и отменяет подписку на все использованные каналы Ably:

В функции connectToAblyChannel происходит большая часть реальной работы. Мы начинаем с загрузки некоторой конфигурации - мы установим соглашение, согласно которому мы ожидаем, что пользователи будут предоставлять либо свой ключ API Ably, либо URL-адрес обратного вызова API, который отвечает запросом аутентификации токена Ably. Чтобы предоставить эти значения и поскольку мы создаем элементы HTML, мы ожидаем, что они будут установлены в качестве атрибутов элемента, когда он будет создан в разметке.

Чтобы прочитать атрибут данных при подключении элемента, мы ожидаем, что элемент будет выглядеть следующим образом:

Затем нам нужно разработать способ настройки клиента Ably. В обычном JavaScript вы можете использовать Document API для взаимодействия с элементами в DOM, но поскольку мы буквально внутри настраиваемого элемента, мы можем использовать вызовы DOM API, такие как getAttribute из this объект.

Этот код пытается загрузить data-api-key и data-get-token-url. Если ключ API найден, он имеет приоритет, в противном случае мы создаем объект конфигурации Ably JavaScript SDK, который содержит свойство authUrl. Теперь, когда у нас есть объект конфигурации (либо ключ API, либо URL-адрес), мы можем создать экземпляр Ably Realtime JavaScript SDK. Важно отметить, что этот настраиваемый элемент полагается на содержащую страницу, которая уже включила Ably JavaScript SDK с использованием элемента сценария до его выполнения. Следующий код появляется после конфигурации внутри функции connectToAblyChannel :

Когда у нас будет экземпляр SDK, хранящийся в переменной this.ably, мы также создадим пустой массив с именем subscribedChannels.

В заключение мы рассмотрим две дополнительные функции в AblyBaseComponent - функцию publish и функцию subscribe.

Стандартный Ably SDK предоставляет функции публикации и подписки, как только вы вызываете ably.channels.get(channelName), чтобы получить канал. В этом примере чата мы хотим, чтобы AblyChatComponent решал, какие каналы он будет публиковать и на которых подписываться, эффективно расширяя область действия API Ably SDK (набор вещей, которые может делать API).

Расширенные функции публикации и подписки вначале принимают дополнительный параметр - имя канала, а затем передают остальные аргументы в Ably SDK, чтобы он сделал за нас всю тяжелую работу. Мы делаем это, деструктурируя массив arguments и игнорируя первый элемент. Это позволяет нам захватить все параметры, кроме первого, в нашей недавно определенной переменной args:

Затем мы можем использовать .apply для вызовов публикации или подписки из Ably SDK, чтобы передать остальные переменные в SDK для публикации или подписки на сообщения.

Мы будем использовать первый параметр, channelName, чтобы получить правильный канал и отслеживать его, чтобы мы могли отказаться от подписки при отключении от DOM. Следующий код войдет в функцию publish под назначением аргументов. Функция подписки работает аналогично

Как видите, и publish, и subscribe являются практически идентичными функциями, за исключением того, что мы передаем им вызов channel.publish или channel.subscribe соответственно.

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

Этот базовый класс содержит весь наш код Ably и теперь готов для создания на его основе интересных компонентов, поэтому давайте создадим наш второй компонент, AblyChatComponent.

Создание компонента чата

Эти компоненты предназначены для импорта в виде модуля ES6, где тег скрипта, который вы используете в своем HTML, имеет type="module" в качестве атрибута. При использовании модулей ES6 мы можем использовать import в компонентах браузера, поэтому для начала мы импортируем наш AblyBaseComponent, чтобы расширить его.

Следующее, что нужно сделать, - это настроить шаблонный код для элемента. Определите атрибут, называемый «сообщениями», и сделайте его наблюдаемым. Затем настройте constructor, который, в свою очередь, вызывает конструктор AblyBaseComponent, используя вызов super();.

Мы можем использовать connectedCallback для настройки приложения чата. Мы вызываем super.connectedCallback, чтобы запустить всю логику конфигурации Ably из базового компонента. Следующий код находится под constructor, внутри закрывающей скобки класса AblyChat:

Мы вызываем функцию super.subscribe* *, которая была определена на базе, для подписки на сообщения в канале с именем chat. Остальные параметры, как мы указывали ранее, являются стандартными параметрами Ably JavaScript SDK - мы подписываемся только на сообщения по теме chat-message. Когда сообщение получено, мы вызываем функцию с именем this.onAblyMessageReceived (которую мы реализуем чуть позже), передавая полученное сообщение в качестве аргумента.

Чтобы гарантировать, что любые стили CSS, применяемые к странице, не влияют на компонент, и наоборот, внутри тела connectedCallback мы сгенерируем случайную строку и назначим ее свойству с именем id:

Затем мы вызываем функцию с именем renderTemplateAndRegisterClickHandlers, которую мы вскоре рассмотрим.

Наконец, мы помещаем фокус браузера на элемент с именем this.inputBox, который создается при рендеринге шаблона, чтобы люди, использующие пользовательский интерфейс чата, могли мгновенно начать вводить текст.

Затем мы используем attributeChangedCallback для обновления innerHTML пузырей чата в окне чата при получении сообщений. Когда атрибут изменяется, innerHTML из this.chatText устанавливается и прокручивается в поле зрения. Мы используем функцию formatMessages, которая берет историю сообщений и преобразует ее в HTML-элементы, пригодные для отображения:

Затем мы настраиваем функцию renderTemplateAndRegisterClickHandlers, названную в честь того, что она делает! Начало этой функции вызывает другую функцию с именем defaultMarkup, которая принимает единственный параметр - * id * элемента и возвращает innerHTML, который мы хотим отобразить на экране - пустой элемент окна чата.

После того, как элемент был отрисован в DOM, мы можем использовать querySelectorAll для поиска элементов chatText, inputBox, sendButton и messageEnd, чтобы мы могли использовать их в нашем коде:

Внутри мы также подключаем eventListeners для щелчков по sendButton и для каждого нажатия клавиши в поле ввода, чтобы мы могли обрабатывать ввод пользователя.

Мы упоминали функцию onAblyMessageReceived ранее, когда подписывались на сообщения Ably - эта функция берет текущую историю сообщений из this.messages, проверяет длину не более 199 сообщений и добавляет последнее сообщение в конец массива:

Затем этот новый массив присваивается this.messages, что запускает повторную визуализацию пользовательского интерфейса, поскольку this.messages является наблюдаемым свойством.

Функция sendChatMessage вызывается либо при нажатии Enter, либо при нажатии кнопки для отправки сообщения. Поскольку мы расширяем AblyBaseComponent, он вызывает функцию super.publish, передавая имя канала и полезную нагрузку сообщения Ably:

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

Функция handleKeyPress запускается при каждом нажатии клавиши. Если нажата клавиша Enter и есть сообщение в окне чата, оно отправляет сообщение чата:

Функция formatMessages отвечает за отображение истории сообщений Ably в элементы диапазона. Здесь есть немного логики, чтобы определить, было ли сообщение отправлено текущим пользователем приложения, путем проверки свойства message.connectionId на свойство ably.connection.id и добавления класса CSS me или other, который может применять стили к. Свойство data сообщения используется для передачи полученного текстового сообщения.

Вышеупомянутое завершает класс настраиваемого элемента. После завершения класса пользовательского элемента у нас есть две функции. Первая - это функция uuidv4(), которая генерирует уникальный id для компонента:

Второй - defaultMarkup, который принимает единственный параметр - идентификатор компонента - и использует его для установки свойства id в сгенерированном HTML.

После того, как мы установили это свойство id, мы можем встроить CSS, который привязан конкретно к этому идентификатору элемента, непосредственно в выходной код. Это означает, что если на одной странице отображается несколько экземпляров настраиваемого элемента, у них не будет конфликтующих идентификаторов или стилей.

Внизу следующего фрагмента вы можете увидеть разметку для компонента - div для хранения истории сообщений и form для сбора пользовательского ввода вместе с именами классов, используемыми в нашем селектор запросов вызывается ранее.

И, конечно же, как и в нашем примере элемента в начале, мы вызываем customElements.define, чтобы зарегистрировать тег HTML:

Пользовательский элемент теперь функционально завершен, и пока файлы AblyBaseComponent.js и AblyChatComponent.js включены в веб-приложение, их можно использовать, ссылаясь на наш AblyChatComponent.js как на модуль.

Чтобы использовать зарегистрированный пользовательский элемент, мы используем его, как любой старый тег HTML, и предоставляем ему правильные атрибуты:

Элемент отображается на странице таким образом, и когда он настроен с помощью ключа API или URL-адреса get-token, он просто работает!

Управление ключами API

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

Рекомендуемый способ использования ключей API в интерфейсе - использовать Ably Token Authentication. Аутентификация токенов - это механизм обмена, в котором вы используете свой настоящий ключ API для создания токенов ограниченного использования, которые могут быть переданы вашим клиентам для использования во внешнем интерфейсе.

Чтобы использовать аутентификацию с помощью токена таким образом, нам нужно где-то создать API для вызова из внешнего интерфейса, в котором хранится ваш настоящий ключ API Ably. Затем мы можем использовать функцию в Ably SDK для обмена вашего реального ключа API на токен, который возвращается в Ably JavaScript SDK. Ably JavaScript SDK управляет этим процессом обмена токенов за вас. Когда вы предоставляете URL-адрес, который указывает на API, который возвращает токен, SDK будет управлять токеном и обновлять его по мере необходимости, поэтому вам не нужно об этом беспокоиться. В этой демонстрации мы рассмотрим использование функций AWS Lambda и AWS API Gateway для достижения этой цели.

В следующем примере функция AWS Lambda предоставляет необходимые функции обмена токенами. Все, что нам нужно сделать, это потребовать Ably JavaScript SDK и создать экземпляр Ably.Realtime клиента, передавая ваш ключ API Ably из process.env.ABLY_API_KEY.

Мы используем client.auth.createTokenRequest для генерации временного токена и возврата его клиенту.

Ответственность за то, чтобы пользователи, запрашивающие временный токен, имели доступ к чату, лежит на владельце ключа API. Вы можете аутентифицировать запросы любым удобным для вас способом, и это ничем не отличается от аутентификации в любой другой лямбда-функции. В следующем разделе мы рассмотрим размещение этого на AWS Lambda.

Использование AWS Lambda для аутентификации

Для развертывания на AWS Lambda нам нужно создать новый каталог с именем / api / createTokenRequest с двумя файлами в нем - package.json и index.js Вот файл package.json.

А это файл index.js

Эти два файла вместе с их модулями node_modules необходимы среде выполнения AWS Lambda. Мы собираемся использовать npm для восстановления модулей узлов, а затем сжимаем содержимое каталога / createTokenRequest в zip-файл. В терминале выполните следующее:

После этого заархивируйте содержимое каталога createTokenRequest (этот процесс зависит от ОС). Мы будем использовать пользовательский интерфейс AWS, чтобы создать функцию Lambda и загрузить этот zip-файл в качестве исходного кода.

Сейчас мы рассмотрим этот процесс. Вам необходимо сначала войти в свою учетную запись AWS:

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

2. Нажмите кнопку «Создать функцию», чтобы создать новую лямбда-функцию.

3. Выберите «Автор с нуля» и дайте вашей функции имя, затем нажмите «Создать функцию».

4. После создания функции нажмите «Добавить триггер», чтобы сделать лямбда-функцию доступной по HTTP.

5. В раскрывающемся списке «Конфигурация триггера» выберите «API Gateway».

6. На открывшейся странице выберите нужную функцию из раскрывающегося списка и нажмите «Добавить».

7. Установите для стадии развертывания значение по умолчанию, а для параметра «Безопасность» - «Открыть», затем нажмите «Добавить».

8. После добавления триггера в пользовательском интерфейсе отображается URL-адрес на вкладке «Триггеры» в разделе «Конфигурация». (Это то, что вы добавите в качестве параметра data-get-token-url, когда используете свой компонент в HTML, но у нас все еще есть некоторые дополнительные настройки!)

9. Теперь вам нужно загрузить zip-файл, который мы создали ранее. Щелкните вкладку «Код», затем «Загрузить из» и выберите «файл .zip».

10. После загрузки zip-файла вам нужно будет настроить переменные среды с помощью ключа Ably API. В разделе «Конфигурация» выберите «Переменные среды», затем нажмите кнопку «Изменить».

11. Добавьте свой API-ключ Ably в настройки «Переменные среды».

Вот и все, ваша Лямбда настроена!

В файле index.js мы читаем из process.env.ABLY_API_KEY. Вам нужно будет сгенерировать новый умный ключ API, а затем определить эту переменную среды с ее значением ключа в пользовательском интерфейсе AWS (или с помощью инструмента автоматизации по вашему выбору).

Как только наша функция Lambda будет создана, нам нужно будет добавить триггер AWS API Gateway, чтобы предоставить нашей лямбде доступный извне URL. Это URL-адрес, который мы можем безопасно настроить в нашей разметке HTML вместо нашего фактического ключа API. Об остальном позаботится Ably SDK.

Размещение вашего веб-компонента на Amplify

Теперь мы можем пройти через размещение вашего компонента на Amplify, сервисе статического веб-хостинга AWS.

  1. Найдите Amplify в строке поиска Services и щелкните появившуюся ссылку AWS Amplify.

2. Щелкните «НАЧАТЬ».

3. Прокрутите полученную страницу вниз до «Разместите свое веб-приложение» и нажмите «Начать».

4. Аутентифицируйте AWS Amplify с помощью своей учетной записи GitHub.

5. Выберите репозиторий вашего веб-компонента.

6. Измените настройки сборки, включив npm run ci в качестве команды предварительной сборки, и установите для baseDirectory значение «/ build».

7. Нажмите кнопку «Сохранить и развернуть», чтобы разместить компонент.

8. Если все в порядке, компонент будет успешно подготовлен, построен и развернут. Пользовательский интерфейс предоставит вам URL-адрес для просмотра размещенного компонента.

Размещенный веб-компонент будет выглядеть примерно так:

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

Здесь, в Ably, мы опубликовали компонент в NPM как ably-chat-component, и вы можете ссылаться на него напрямую, используя Skypack CDN. Это гарантирует, что пакеты совместимы с браузером.

Чтобы компонент работал, вам необходимо сослаться на клиентский пакет Ably SDK. Однако как только вы это сделаете, вы можете ссылаться на URL Skypack для нашего компонента и добавить тег ably-chat на свою страницу, установить свой ключ API, и все будет просто работать.

Это самый простой из поддерживаемых способов использования этого компонента в режиме разработки. Однако, как упоминалось выше, вам потребуется заменить ключ API на собственный URL-адрес запроса токена.

Компонентная архитектура

Схематически компонентную архитектуру можно изобразить так:

С соответствующей схемой последовательности:

В этом разделе мы разбили, как работают веб-компоненты, изучили Ably и веб-компоненты, а также рассмотрели, как мы можем использовать AWS Amplify и AWS Lambda для размещения приложений, поддерживающих чат в реальном времени.

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

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

О Ably

Ably предоставляет API-интерфейсы для реализации обмена сообщениями pub / sub для функций в реальном времени в ваших приложениях. Вы также получаете готовую глобально распределенную масштабируемую инфраструктуру и набор услуг. К ним относятся такие функции, как присутствие - которое показывает онлайн / офлайн-статус различных участников, автоматическое переподключение и возобновление сообщений в случае периодических проблем с сетью, упорядочивание сообщений и гарантированная доставка, а также простые способы интеграции со сторонними API ».

Ably позволяет обмениваться сообщениями pub / sub, в основном через WebSockets. Концепция каналов позволяет классифицировать данные и решать, какие компоненты к каким каналам имеют доступ. Вы также можете указать возможности для различных участников на этих каналах, такие как только публикация, только подписка, история сообщений и т. Д.

Узнать больше о платформе Ably

Первоначально опубликовано по адресу https://ably.com/blog/ably-aws-web-components.

Больше контента на plainenglish.io