简体   繁体   中英

Inject CSS styles inside of the shadow-root instead of the head tag | Vue.js & Webpack

I'm making an embeddable widget for websites using Vue.js and vue-custom-element . Everything was going smoothly until I ran into a problem.

When I'm trying to use a component (with css) from a package. Like vue-number-input for example. The css gets injected in the head of the webpage even though it should be added inside the shadow root.

As you can see here you see that the css from the number input package is injected inside the head while the other stylings are in the shadow root like they should.

As far as I know I changed all of the settings needed to make the application work inside a shadow root.

This is my vue.config.js , my main.js (where I register the custom element) and my component (where I import the component from the package).

Does anyone know how I can do this or is this even possible?

If the library is itself embedding the styles to the head of the page, then there is nothing you can do other than to write some script to manually copy it into the shadow dom after initialization. I ran into the same issue for Font Awesome icons. Luckily, they provided a fix for this. ( https://github.com/FortAwesome/vue-fontawesome#web-components-with-vue-web-component-wrapper )

After a lot of research for a solution which actually lazy-loaded our styles into the shadow-root, we didn't find one. We went with suboptimal solution which is loading all the styles on first load.

Note: This makes it work for QA and production environments. When developing, the styles will still be loaded inside of the head of the page. So you have to make sure no other styles conflict with that.

This was our way of accomplishing this:

// vue.config.js

const webpack = require('webpack');

module.exports = {
  configureWebpack: {
    // ...
    plugins: [
      new webpack.optimize.LimitChunkCountPlugin({
        maxChunks: 1,
      }),
    ],
  },
  chainWebpack: (config) => {
    config.optimization.delete('splitChunks');
  },
};
// main.js

import createStyleLink from '@/helpers/createStyleLink';

// ...

const options = {};

// Enable shadow root for production build
if (process.env.NODE_ENV === 'production') {
  options.shadow = true;
  options.beforeCreateVueInstance = (root) => {
    const rootNode = root.el.getRootNode();
    if (rootNode instanceof ShadowRoot) {
      root.shadowRoot = rootNode;
    } else {
      root.shadowRoot = document.head;
    }
    // After deployment, this will create a style link and put it inside the shadow root. 
    // Preferably, you would use a CDN for this.
    createStyleLink(rootNode, `${process.env.VUE_APP_URL}/css/app.css`);
    return root;
  };
}

// ...

Vue.customElement('widget', App, options);
// createStyleLink.js

export default function (node, url) {
  const link = document.createElement('link');
  link.href = url;
  link.type = 'text/css';
  link.rel = 'stylesheet';
  node.appendChild(link);
}

I'll not mark this as a solution as this is more like a suboptimal workaround.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM