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

Рендеринг компонентов Svelte как веб-компонентов в Angular: пошаговое руководство


В предыдущие годы Svelte быстро росла и вызывала интерес, как показано в предыдущем обзоре State of JS:

С его подробным учебным пособием может возникнуть соблазн сделать еще один шаг и использовать Svelte в реальном приложении.

Тем не менее, миграция всего приложения в стиле Big Bang может оказаться непростым способом погрузиться в него. Решением может быть создание отдельного компонента Svelte и его использование в качестве веб-компонентов.

В учебнике меня привлекла фраза:

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

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

В этой статье мы рассмотрим, как интегрировать компоненты Svelte в существующее приложение Angular в виде веб-компонентов.

✋ Внимание — Svelte 4 только что был выпущен, но в этом руководстве используется Svelte 3: используемый здесь пакет также использует Svelte 3, поэтому миграция сейчас была бы немного преждевременной.

Что такое веб-компоненты?

Прежде чем погрузиться с головой в взаимодействие Svelte и Angular, давайте сначала посмотрим, что будет клеем, связывающим их вместе: веб-компоненты.

Как указано в веб-документах MDN:
Веб-компоненты — это набор различных технологий, позволяющих создавать многократно используемые настраиваемые элементы — с их функциональностью, изолированной от остального кода — и использовать их в своих веб-приложениях.

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

В основе веб-компонентов лежат три технологии:

  • Пользовательские элементы, которые позволяют создавать собственные HTML-элементы с собственным поведением, стилем и/или шаблонами.
  • Теневой DOM, который действует как локальный DOM для компонента, позволяя ему определять все свои элементы в полной изоляции от глобального DOM.
  • Шаблоны HTML, которые позволяют определять и повторно использовать фрагменты HTML, вставляя их в модель DOM.

Если вы ранее не сталкивались со Svelte, наше руководство поможет вам настроить и создать собственное приложение. 

Наш стройный проект

Перед экспортом любого компонента Svelte нам сначала нужно создать библиотеку компонентов.

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

Настройка нового проекта

Поскольку Svelte — это «просто» компилятор, мы создадим необработанный проект JavaScript.

В новой папке svelte-web-components создайте файл package.json, содержащий следующую конфигурацию:

{
  "name": "svelte-web-components",
  "version": "1.0.0",
  "scripts": { },
  "devDependencies": {
    "svelte": "^3.59.1"
  },
  "type": "module"
}

Мы готовы к работе!

Создание пользовательского компонента Svelte

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

Определите код компонента в новом файле components/Counter.svelte:

<!-- components/Counter.svelte -->
<script>
  export let initialValue = 0;

  let count = initialValue;
  $: isInitialValue = count === initialValue;

  const increment = () => (count += 1);
  const decrement = () => (count -= 1);
  const reset = () => (count = initialValue);
</script>

<div>
  <span>{count}</span>

  <button type="button" on:click={decrement}>-</button>
  <button type="button" on:click={increment}>+</button>
  <button type="button" on:click={reset} disabled={isInitialValue}>Reset</button>
</div>
Если вы хотите попробовать, у Svelte есть онлайн-REPL.

Вы должны увидеть что-то похожее:


Поскольку мы создаем библиотеку, давайте не забудем выставить ее в нашем публичном API:

// components/index.js
export { default as Counter } from './Counter.svelte';

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

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

Обновленная версия лишь немного отличается:

<!-- components/Counter.svelte -->
<script>
  import { writable } from 'svelte/store';

  export let initialValue = 0;

  let count = writable(initialValue);
  $: isInitialValue = $count === initialValue;

  const increment = () => count.update((n) => (n += 1));
  const decrement = () => count.update((n) => (n -= 1));
  const reset = () => count.set(initialValue);
</script>

<div>
  <span>{$count}</span>

  <button type="button" on:click={decrement}>-</button>
  <button type="button" on:click={increment}>+</button>
  <button type="button" on:click={reset} disabled={isInitialValue}>Reset</button>
</div>
Так как мы будем использовать наш компонент в другом месте, давайте также немного стилизуем его, чтобы им было приятнее пользоваться✨ Дополнительный CSS

<!-- components/Counter.svelte -->
<style>
  div {
    display: flex;
    align-items: center;
    gap: 5px;
    border: 1px solid #999;
    width: fit-content;
    padding: 5px;
    border-radius: 5px;
  }

  div span {
    font-size: 18px;
    font-weight: bold;
    margin: 0 10px;
  }

  div button {
    padding: 5px 10px;
    border: 1px solid #ccc;
    background-color: #f0f0f0;
    color: #333;
    font-size: 16px;
    transition: background-color 0.3s ease;
    border-radius: 5px;
  }

  div button:hover {
    background-color: #e0e0e0;
  }

  div button:active {
    background-color: #ccc;
  }

  div button:disabled {
    opacity: 40%;
  }
</style>

Наш компонент все еще работает нормально, используя свое хранилище:

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

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

Как мы видели ранее, способ запустить наш компонент вне этой среды — преобразовать его в веб-компонент.

Давайте просмотрим контрольный список и посмотрим, что мы уже проверили:

  • ❌ Пользовательские элементы
  • ✅ 🌑 Тень ДОМ
  • ✅ HTML-шаблоны

Мы почти там!

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

Для этого мы можем использовать специальный элемент <svelte:options>, чтобы указать, какой тег использовать:

<!-- components/Counter.svelte -->
<svelte:options tag="svelte-counter" />

<!-- Counter component code here -->

Теперь у нас есть компонент Counter, удовлетворяющий всем трем требованиям веб-компонента.

Компиляция компонента Svelte в чистый JavaScript

Теперь, когда наш компонент готов к экспорту, мы можем сделать это, скомпилировав его в чистый JavaScript.

Для этого мы будем использовать esbuild для сборки или компонента. Нам нужно будет получить еще две зависимости: esbuild и esbuild-svelte:

npm i -D esbuild esbuild-svelte

Затем мы можем добавить скрипт, который будет считывать точку входа в нашу библиотеку и выводить результат JS:

// esbuild-bundle.js
import esbuild from "esbuild";
import sveltePlugin from "esbuild-svelte";

esbuild
  .build({
    entryPoints: ["./components"],
    bundle: true,
    outfile: "dist/web-components.js",
    plugins: [
      sveltePlugin(),
    ],
    logLevel: "info",
  })
  .catch(() => process.exit(1));

Однако мы должны сообщить eslint, что мы создаем веб-компоненты, а не просто связываем наше приложение:

esbuild
  .build({
    entryPoints: ["./components"],
    bundle: true,
    outfile: "dist/web-components.js",
    plugins: [
      sveltePlugin({
+       compilerOptions: {
+         customElement: true,
+       },
      }),
    ],
    logLevel: "info",
  })
  .catch(() => process.exit(1));

Наконец, чтобы упростить запуск, мы можем добавить запись в скрипты нашего package.json:

{
  "name": "svelte-web-components",
  "version": "1.0.0",
  "scripts": {
+   "build": "node esbuild-bundle.js"
  },
  "devDependencies": {
    "esbuild": "^0.18.2",
    "esbuild-svelte": "^0.7.3",
    "svelte": "^3.59.1"
  },
  "type": "module"
}

Теперь мы можем запустить npm run build и увидеть результат:

У нас должен быть вывод нашей библиотеки в dist/web-components.js, содержащий наш веб-компонент 📦

Интеграция веб-компонентов Svelte в Angular

Когда наш пакет готов, остаётся его только использовать.

Настройка приложения Angular.

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

ng new angular-wrapper --standalone --defaults

и замените сгенерированный app.component.ts следующим кодом:

import { Component } from "@angular/core";

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <h1>Svelte in Angular!</h1>
  `,
})
export class AppComponent {}

Теперь, когда наше приложение Angular готово и наш веб-компонент Svelte тоже, давайте подключим его!

Использование веб-компонентов Svelte в Angular

Чтобы Angular знал о нашем внешнем пакете JS, нам нужно добавить его как зависимость нашего проекта.

Для этого возьмите ранее сгенерированный файл web-components.js и поместите его в новую папку в корне вашего проекта Angular в src/scripts:

И укажите его в файле angular.json в массиве скриптов, расположенном по адресу angular.json > projects > angular-wrapper > architect > build > options > scripts:

"styles": [
  "src/styles.css"
],
"scripts": [
+ "src/scripts/web-components.js"
]

Angular связал наш веб-компонент вместе с нашим приложением, и теперь мы можем использовать наш счетчик!

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <h1>Svelte in Angular!</h1>
+   <svelte-counter />
  `,
})
export class AppComponent {}

...или мы можем?

Пытаясь уберечь нас от ошибок, Angular сканирует используемые нами HTML-элементы и проверяет, являются ли они родным HTML-элементом или разрешаемым компонентом Angular.

Однако в нашем случае это ни то, ни другое.

Надеюсь, Angular даст нам решение для нашего варианта использования:

Если «svelte-counter» является веб-компонентом, добавьте «CUSTOM_ELEMENTS_SCHEMA» в «@Component.schemas» этого компонента, чтобы подавить это сообщение.
+ import { Component, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";

@Component({
  selector: "app-root",
  standalone: true,
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
  template: `
    <h1>Svelte in Angular!</h1>
    <svelte-counter />
  `,
})
export class AppComponent {}

Теперь вы можете увидеть веб-компонент Svelte, работающий внутри нашего приложения Angular!

Выводы

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

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

Если вы хотите проверить полученный код, вы можете перейти к соответствующему репозиторию GitHub.

Источник:

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

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

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

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