strokoff

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

vue-wc-likes

На примере веб-компонента 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