Стайлинг не применяется к веб-компоненту vue во время разработки

При разработке веб-компонента Vue style не применяется к веб-компоненту, но добавляется в head документа. Это означает, что стиль игнорируется в тени DOM. Вот как я обертываю веб-компонент в main.js:

import Vue from 'vue';
import wrap from '@vue/web-component-wrapper';
import MyWebComponent from './components/MyWebComponent';

const WrappedElement = wrap(Vue, MyWebComponent);

window.customElements.define('my-web-component', WrappedElement);

Опять же, любые правила CSS внутри тегов стиля не вступают в силу.

Когда я создаю для производства, стили добавляются в веб-компонент. Я использую следующую команду для переноса:

vue-cli-service build  --target wc --name my-web-component ./src/components/MyWebComponent.vue

Есть ли способ достичь того же, что и vue-cli-service serve?

edit: example repo здесь: https://github.com/snirp/vue-web-component

edit2: У меня такое чувство, что моя проблема тесно связана с этой проблемой. Я не могу разобраться в обходных решениях, и я бы оценил более базовое решение.

Ответ 1

Я не использую VUE, но этот код должен работать в любом месте и помещать ваш CSS в нужное место.

Я делаю некоторые предположения в этом коде, вы передаете свой пользовательский элемент (если вы не используете shadowDOM) или ваш shadowRoot (если вы используете shadowDOM) в качестве первого параметра, и вы передаете элемент <style> в качестве второго параметра, Также <style> должен включать атрибут с именем component="<your component name>" чтобы код не вставлял стиль более одного раза.

// Check to see if 'el' has been placed into a shadow root.
// If it has then add our CSS into that shadow root.
// Otherwise place the CSS into the '<head>' element.
function setCss(el, styleEl) {
  let comp = (styleEl instanceof DocumentFragment ? styleEl.querySelector('style') : styleEl);
  comp = comp.getAttribute('component');
  if (!comp) {
    throw new Error('Your '<style>' tag must set the attribute 'component' to the component name. (Like: '<style component="my-element">')');
  }

  let doc;
  // istanbul ignore else
  if (el.getRootNode) {
    doc = el.getRootNode();
    // istanbul ignore else
    if (doc === document) {
      doc = document.head;
    }
  }
  else {
    doc = document.head; // Shadow DOM isn't supported so place it in '<head>'
  }

  // istanbul ignore else
  if (!doc.querySelector('style[component="${comp}"]')) {
    doc.appendChild(styleEl.cloneNode(true));
  }
}

export default setCss;

Ответ 2

Основываясь на проблеме GitHub, которую вы связали, решение заключается в установке опции shadowMode в vue-loader и vue-style-loader. shadowMode по умолчанию является false в проекте Vue CLI, но мы можем настроить его в vue.config.js.

Во-первых, мы проверили конфигурацию Webpack, чтобы определить, какие загрузчики должны измениться:

# run at project root
vue inspect

Вывод команды показывает несколько shadowMode: false загрузчика с shadowMode: false:

/* config.module.rule('css') */
{
  test: /\.css$/,
  oneOf: [
    /* config.module.rule('css').oneOf('vue-modules') */
    {
      resourceQuery: /module/,
      use: [
        /* config.module.rule('css').oneOf('vue-modules').use('vue-style-loader') */
        {
          loader: 'vue-style-loader',
          options: {
            sourceMap: false,
            shadowMode: false  // <---
          }
        },
        /* ... */
      ]
    },
    /* ... */

полный список конфигураций загрузчика Webpack с shadowMode: false:

config.module.rule('vue').use('vue-loader')
config.module.rule('css').oneOf('vue-modules').use('vue-style-loader')
config.module.rule('css').oneOf('vue').use('vue-style-loader')
config.module.rule('css').oneOf('normal-modules').use('vue-style-loader')
config.module.rule('css').oneOf('normal').use('vue-style-loader')
config.module.rule('postcss').oneOf('vue-modules').use('vue-style-loader')
config.module.rule('postcss').oneOf('vue').use('vue-style-loader')
config.module.rule('postcss').oneOf('normal-modules').use('vue-style-loader')
config.module.rule('postcss').oneOf('normal').use('vue-style-loader')
config.module.rule('scss').oneOf('vue-modules').use('vue-style-loader')
config.module.rule('scss').oneOf('vue').use('vue-style-loader')
config.module.rule('scss').oneOf('normal-modules').use('vue-style-loader')
config.module.rule('scss').oneOf('normal').use('vue-style-loader')
config.module.rule('sass').oneOf('vue-modules').use('vue-style-loader')
config.module.rule('sass').oneOf('vue').use('vue-style-loader')
config.module.rule('sass').oneOf('normal-modules').use('vue-style-loader')
config.module.rule('sass').oneOf('normal').use('vue-style-loader')
config.module.rule('less').oneOf('vue-modules').use('vue-style-loader')
config.module.rule('less').oneOf('vue').use('vue-style-loader')
config.module.rule('less').oneOf('normal-modules').use('vue-style-loader')
config.module.rule('less').oneOf('normal').use('vue-style-loader')
config.module.rule('stylus').oneOf('vue-modules').use('vue-style-loader')
config.module.rule('stylus').oneOf('vue').use('vue-style-loader')
config.module.rule('stylus').oneOf('normal-modules').use('vue-style-loader')
config.module.rule('stylus').oneOf('normal').use('vue-style-loader')

Таким образом, мы можем установить shadowMode: true для этих vue.config.js в vue.config.js с помощью этого фрагмента:

function enableShadowCss(config) {
  const configs = [
    config.module.rule('vue').use('vue-loader'),
    config.module.rule('css').oneOf('vue-modules').use('vue-style-loader'),
    config.module.rule('css').oneOf('vue').use('vue-style-loader'),
    config.module.rule('css').oneOf('normal-modules').use('vue-style-loader'),
    config.module.rule('css').oneOf('normal').use('vue-style-loader'),
    config.module.rule('postcss').oneOf('vue-modules').use('vue-style-loader'),
    config.module.rule('postcss').oneOf('vue').use('vue-style-loader'),
    config.module.rule('postcss').oneOf('normal-modules').use('vue-style-loader'),
    config.module.rule('postcss').oneOf('normal').use('vue-style-loader'),
    config.module.rule('scss').oneOf('vue-modules').use('vue-style-loader'),
    config.module.rule('scss').oneOf('vue').use('vue-style-loader'),
    config.module.rule('scss').oneOf('normal-modules').use('vue-style-loader'),
    config.module.rule('scss').oneOf('normal').use('vue-style-loader'),
    config.module.rule('sass').oneOf('vue-modules').use('vue-style-loader'),
    config.module.rule('sass').oneOf('vue').use('vue-style-loader'),
    config.module.rule('sass').oneOf('normal-modules').use('vue-style-loader'),
    config.module.rule('sass').oneOf('normal').use('vue-style-loader'),
    config.module.rule('less').oneOf('vue-modules').use('vue-style-loader'),
    config.module.rule('less').oneOf('vue').use('vue-style-loader'),
    config.module.rule('less').oneOf('normal-modules').use('vue-style-loader'),
    config.module.rule('less').oneOf('normal').use('vue-style-loader'),
    config.module.rule('stylus').oneOf('vue-modules').use('vue-style-loader'),
    config.module.rule('stylus').oneOf('vue').use('vue-style-loader'),
    config.module.rule('stylus').oneOf('normal-modules').use('vue-style-loader'),
    config.module.rule('stylus').oneOf('normal').use('vue-style-loader'),
  ];
  configs.forEach(c => c.tap(options => {
    options.shadowMode = true;
    return options;
  }));
}

module.exports = {
  // https://cli.vuejs.org/guide/webpack.html#chaining-advanced
  chainWebpack: config => {
    enableShadowCss(config);
  }
}

Создание <projectroot>/vue.config.js с приведенным выше фрагментом позволяет использовать Shadow CSS в вашем проекте. См. Https://github.com/snirp/vue-web-component/pull/1.