Создание Web components на Vue 3
- суббота, 24 февраля 2024 г. в 00:00:18
Можно написать на Vue 3 какой-нибудь полезный компонент и захотеть дать возможность использовать его не только в Vue проектах, но и на любых других фронтендах. Сделать это несложно через регистрацию его как web component, или подключение "микро-фронтендом". Оба варианта будут рассмотрены ниже.
Рассмотрим универсализацию на примере release-timeline
При разработке используем Vite с двумя конфигами - один непосредственно для разработки и запуске в Dev режиме на сервере Vite, другой - для билда библиотеки.
// package.json
"scripts": {
"build": "vite build",
"build:lib": "vite --config vite.config-lib.ts build"
В vite.config-lib.ts указана точка входа index.ts
и также указано, чтобы не включать в бандл дистрибутив Vue.
Самое интересное это именно точка входа index.ts. Она общая для всех режимов - npm vue библиотека, web component и микро-фронтенд. Это очень удобно при разработке и сборке.
import { createApp, defineCustomElement } from "vue";
import { DefaultOptions } from "./options";
import ReleaseTimelineCE from "./ReleaseTimeline.ce.vue";
import ReleaseTimeline from "./ReleaseTimeline.vue";
import App from "./App.vue";
if (globalThis.window) {
window.customElements.define("release-timeline", defineCustomElement(ReleaseTimelineCE));
}
function mountRT(initOptions) {
const app = createApp(App, { initOptions });
app.mount("#release-timeline");
}
export { ReleaseTimeline, DefaultOptions, mountRT };
export {};
В данном режиме используется вызов mountRT()
:
<script type="module">
import { mountRT } from "https://esm.sh/release-timeline";
const options = {...};
mountRT(options);
</script>
После этого произойдет монтирование приложения на `<div id="release-timeline" />`
Библиотека экспортирует на 16 строке все нужные модули - ReleaseTimeline
и DefaultOptions
, и их можно сразу использовать в коде через импорт, как обычно.
<script setup>
import { ReleaseTimeline, DefaultOptions as options } from "release-timeline";
import "release-timeline/dist/style.css";
// repository to visualize
options.github.owner = "vuesence";
options.github.repo = "release-timeline";
</script>
<template>
<ReleaseTimeline :options="options" />
</template>
С веб компонентами два момента:
В качестве пропсов они принимают только строки. Поэтому если вам нужно передать массив или объект (как в нашем случае), нужно его либо сериализовать и десериализовать, либо передать опции через общий контекст - window
. Не очень красиво, но работает.
При подключении в проект как Vue библиотеку, CSS стили нужно импортировать отдельно. Практически всегда. В случае веб компонент стили итегрируются и не доступны для изменения снаружи, за исключением CSS переменных.
Чтобы организовать работу со стилями нам нужен ReleaseTimeline.ce.vue
:
<script setup>
import ReleaseTimeline from "./ReleaseTimeline.vue";
defineProps({
options: String,
});
let options = window.rt_options;
if (!options) {
options = JSON.parse(props.options);
}
</script>
<template>
<ReleaseTimeline :options="options" />
</template>
<style lang="scss">
@import "./css/user-badge.scss";
@import "./css/timeline-item.scss";
@import "./css/timeline-item-issues.scss";
@import "./css/release-timeline.scss";
</style>
Это обертка на ReleaseTimeline
, которая, во-первых, обрабатывает параметры, переданные через window
, и во-вторых, работает со стилями.
Если бы главный компонент был один, мы могли бы поместить все стили в его style
секцию. Но Vite при билде не собирает стили из компонент-потомков, поэтому приходится это делать вручную в ReleaseTimeline.ce.vue
. Именно поэтому пришлось размещать их в отдельных файлах и использовать SCSS импорт. Суффикс ce.vue
говорить сборщику использовать специальный режим для `custom elements`.
Возвращаясь к index.ts
, - на 7 строке мы проверяем, что находимся в браузере (на случай использования в SSR/SSG средах - например, при сборке VitePress
), и затем регистрируем веб компонент.
В итоге, для превращения Vue компонента в web component, нужна простая обертка и несколько строк в index.ts
Вот здесь - wc-demo - можно посмотреть код html страницы, чтобы увидеть пример использования release-timeline
как веб компонента. Дистрибутив Vue подключается отдельно.
Наш Telegram-канал о Vue и фронтенд-разработке: @vuefaq и вебсайт: Vue‑FAQ.org.