Используем веб-компоненты в vue.js 3+

На примере веб-компонента wc-like рассмотрим интеграцию с vue3+ фреймворком, посмотрим насколько секонмит нам времени веб-компонент и сколько кода нам надо добавить на vue чтобы все заработало. Демонстрацию смотрите тут. Также доступен git репозиторий с демкой и npm пакет vue-wc-likes.
Создаем Vue приложение
Создаем приложение песочницу и проверяем совместимость, я делал так:
npm install@vue-latest
Отмечаю поддержку typescript, а также для практики напишем простенький тест на vitest для нашей обертки над веб-компонентом, все остальное нам не понадобится.Далее добавляем в поддержку к нашему проекту веб-компонент лайков
npm install wc-likes
Теперь приступим к настройке vue3 и нашего веб-компонента, первым делом чтобы заработали веб-компоненты и не путали templateCompiler vue кто из них vue-component, а кто custom-element
Пример конфига vite.config.ts
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue({
template: {
compilerOptions: {
// все теги с дефисом в названии считать за custom-element
isCustomElement: (tag) => tag.includes('-')
}
}
})
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
tsconfig.app.json и остальные части проекта я оставил по умолчанию как и создалось после выполнения npm install@vue-latest
Создаем vue компонент
перейдем к нашему компоненту like.vue
<template>
<wc-likes ref="like"
:data-hint="hint"
:value="value"
:liked="liked"
:fetch="fetch"
:disabled="disabled">
</wc-likes>
</template>
Все очень просто и минималистично, мы просто добавляем наш wc-likes пользовательский элемент как корневой элемент vue, чтобы не плодить лишний оборачивающий тег.
Рассмотрим теперь сколько кода нам пришлось написать?
— по сути только прокинуть properties и
Важный момент
пользовательские элементы, как и vue компоненты, необходимо регистрировать заранее перед использованием или подтягивать и регистрировать динамически т.е. вызовwindow.customElements.define('wc-likes', WCLikes);не обязательно должен находится вlikes.vueа может быть зарегистрирован выше в приложении 1 раз, все зависит от вашей реализации и потребностей.
import WCLikes from 'wc-likes';
export default {
name: 'v-likes',
props: {
submitLike:Function,
fetchLikes:Function,
value:Number,
liked:Boolean,
fetch:Boolean,
disabled:Boolean,
hint:String,
},
mounted() {
if(customElements.get('wc-likes') === undefined) {
window.customElements.define('wc-likes', WCLikes);
}
let Like = this.$refs.like as any;
//Заменяем функциями из props стандартные методы у пользовательского компонента
Like.fetchSubmitLikes = this.submitLike;
Like.fetchAsyncLikes = this.fetchLikes;
}
}
Осталось лишь начать подключить в нашем app.vue:
import VLikes from './components/likes.vue';
//...
components: {
VLikes
},
И вставить в шаблон по необходимости
<VLikes :value="like" :hint="hint" :submitLike="submitLike" :fetchLikes="fetchLikes"/>
Интеграция CSS
Т.к. пользовательские элементы по умолчанию не имеют никакой логики динамического подключения стилей и разработчик на vue не должен сам управлять подключаемыми CSS в компонентах, я просто перенес sass стили в like.vue компонент
Простой тест для vitest
В папке с тестами добавляем vue-likes.spec.ts
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import Likes from '../likes.vue'
describe('Vue wc-likes', () => {
it('render :value prop correct', () => {
const wrapper = mount(Likes, { props: { value: 1020 } })
expect(wrapper.text()).toContain('1020');
})
})
Подитог
В целом использование пользовательских элементов вместе с vue оставили приятные впечатления, стоит обратить внимание, что сам vue предлагает на его основе билдить кастомные элементы, что конечно же классная функция, но работать фундаментально должно ровно наоборот, пользовательские веб-компоненты должны помогать фреймворкам писать еще меньше кода и переиспользовать код между фреймворками, если же мы бы разрабатывали веб компонент на vue.js, он бы имел в зависимостях vue.js что делало бы его непригодным для переиспользования в целом в веб разработке, а не только на vue стеке.
Последняя редакция 1 февраля, 2023 в 03:02