karol-f / vue-custom-element

Vue Custom Element - Web Components' Custom Elements for Vue.js
https://karol-f.github.io/vue-custom-element/
MIT License
1.97k stars 187 forks source link

added shadowRoot for vue-style-loader #238

Closed SergkeiM closed 3 years ago

SergkeiM commented 3 years ago

Hi, vue-style-loader can inject css into shadowDom, what is missing from vue-custom-element is reference to this shadowDom.

this PR fixes it.

Usage:

import Vue from 'vue';
import vueCustomElement from "vue-custom-element";

Vue.use(vueCustomElement);

Vue.customElement('test-component', () => import("./components/TestComponent").then(c => c.default), {
    shadow: true
});

//component
//
<template>
    <div class="myClass">This is red color</div>
</template>
<style lang="scss">
    .myClass{
        color: red;
    }
</style>

//Webpack
{
    test: /\.vue$/,
    use: [
        {
            loader: 'vue-loader',
            options: {
                shadowMode: true
            }
        }
    ]
},
{
    test: /\.scss$/, //as example I used scss
    use: [
        {
            loader: 'vue-style-loader',
            options: {
                shadowMode: true
            }
        }
    ]
}

Fixes: #214 #191 #156

SergkeiM commented 3 years ago

Hi @karol-f

I think I've rushed wil pull-request, we could do this in:

beforeCreateVueInstance(root){

    const rootNode = root.el.getRootNode();

    if(rootNode instanceof ShadowRoot){

        root.shadowRoot = rootNode

    }else{

        root.shadowRoot = document.head;
    }

    return root;

}       

I use document.head in case shadow dom is not available.

bryanvaz commented 3 years ago

@Froxz do you have an example project demonstrating how the fix works?

I'm trying to extrapolate how to use it for VueCLI 3 users, but webpack refuses to attach the styles to the shadowDOM. It seems that the shadowDOM does not exist yet when webpack tries to inject the styles

SergkeiM commented 3 years ago

Hi @bryanvaz ,

I use laravel-mix, but a fork because laravel-mix doesn't support yet vue-style-loader

Also becuase css-loader 4+ uses esModule loader and is not yet implemented in vue-style-loader

As you can see there are a lot "but". But here is working code sample:

// src/index.js
import Vue from "vue";
import vueCustomElement from "vue-custom-element";
import myComponent from "./components/myComponent";

Vue.customElement('my-component', myComponent, {
    shadow: true,
    beforeCreateVueInstance(root){

        const rootNode = root.el.getRootNode();

        if(rootNode instanceof ShadowRoot){

            root.shadowRoot = rootNode

        }else{

            root.shadowRoot = document.head;
        }

        return root;

    }
});

Full list of devDependencies Note: you don't need to install vue-style-loader it is included in vue-loader.

"devDependencies": {
  "postcss": "^8.2.2",
  "vue-loader": "^15.9.6",
  "vue-template-compiler": "^2.6.12",
  "css-loader": "^3.6.0",
  "laravel-mix": "https://github.com/Froxz/laravel-mix.git#custom-latest",
  "sass": "^1.32.0",
  "sass-loader": "^10.1.0",
  "vue": "^2.6.12",
  "vue-custom-element": "^3.2.14"
}

laravel-mix config

const mix = require('laravel-mix');

mix.js('src/index.js', 'dist/webcomponents.js').vue({
    version: 2,
    options: {
        shadowMode: true
    }
});

let me know if you need anything else.

Full example repo: https://github.com/Froxz/webcomponents

P.S. I think if you use Vue CLI it will not work espicially if you target wc

bryanvaz commented 3 years ago

Hi @Froxz,

Ok I think I get it now, your PR isn't necessarily required if you use the beforeCreateVueInstance. Theoretically all the pieces are there. Your custom laravel-mix should just be the same and adding the shadowMode:true directly to vue-loader and vue-style-loader's vue.config.js

Oh target wc tells vue cli to use the old "official" wc wrapper, which is just barely maintained. A straight target lib works fine when using karol's library.

SergkeiM commented 3 years ago

Hi @bryanvaz yeap closing the PR, as using beforeCreateVueInstance does the job.

bryanvaz commented 3 years ago

Hi @bryanvaz yeap closing the PR, as using beforeCreateVueInstance does the job.

Should probably create a separate PR to add this to the docs, since running in shadow mode requires users to pass specific code into the Vue.customElement call (Without the stylings, custom components in shadow mode are pretty useless).

I'm planning on updating the docs myself since there's no clear instructions on how to properly contain a component in the shadow DOM, but I still can't find out why Vue CLI is refusing to inject the styles (both in HMR, straight prod builds, and umd/cjs builds). Theoretically all the correct pieces are there:

I've set aside today to try to debug this. For some reason the final injection call for styles in vue-style-loader is never triggered.

If I can't figure it out, @Froxz can you take a look later this week? I saw you had some open PRs on the same topic in vue-style-loader and laravel-mix, so I'm sure you'll be able to find something I missed.

Cheers, Bryan

SergkeiM commented 3 years ago

Hi @bryanvaz Can you make a repo with full example, I will take a look.

bryanvaz commented 3 years ago

@Froxz, I managed to get it working this afternoon. You can have a look. It was actually quite simple once I paired your beforeCreateVueInstance fix with my vue.config.js updates for shadowMode.

Haven't tested it with Vue 3 or css-modules^4, but it theoretically should work. I'll update the docs tomorrow.

Right now I'm fighting with vuetify to create an example, however I think the library might be too far gone. Do you have any Material UI or visual primitive libraries which might make a good example of how the shadow DOM isolates visual styles?

Example: https://github.com/bryanvaz/vue-custom-element-shadow-examples (still have to clean up and doc the examples)

FuriosoJack commented 3 years ago

Hola @bryanvaz ,

Yo uso laravel-mix, sino un tenedor porque laravel-mezcla no apoyo aúnvue-style-loader

También porque css-loader 4+ usa esModule loader y aún no está implementado envue-style-loader

Como puede ver, hay muchos "pero". Pero aquí hay una muestra de código de trabajo:

  • Necesitas laravel mix con los mismos cambios que mi fork (o usa webpack directamente)
  • Necesitas la css-loaderversión ^ 3
  • Adjuntar shadowDomen vue-custom-element beforeCreateVueInstancedevolución de llamada
// src / index.js 
importa  Vue  desde  "vue" ; 
importar  vueCustomElement  de  "vue-custom-element" ; 
importar  myComponent  desde  "./components/myComponent" ;

Vue . customElement ( 'my-component' ,  myComponent ,  { 
  shadow : true , 
  beforeCreateVueInstance ( root ) {

      const  rootNode  =  root . el . getRootNode ( ) ;

      si ( rootNode  instanceof  ShadowRoot ) {

          raíz . shadowRoot  =  rootNode

      } más {

          raíz . shadowRoot  =  documento . la cabeza ; 
      }

      return  root ;

  } 
} ) ;

La lista completa de devDependenciesNota: no es necesario que la instale vue-style-loader, está incluida en vue-loader.

" devDependencies " : {
   " postcss " : " ^ 8.2.2 " ,
   " vue-loader " : " ^ 15.9.6 " ,
   " vue-template-compiler " : " ^ 2.6.12 " ,
   " css-loader " : " ^ 3.6.0 " ,
   " laravel-mix " : " https://github.com/Froxz/laravel-mix.git#custom-latest " ,
   "descaro " : "^ 1.32.0 " ,
   " sass-loader " : " ^ 10.1.0 " ,
   " vue " : " ^ 2.6.12 " ,
   " vue-custom-element " : " ^ 3.2.14 " 
}

laravel-mix config

const  mix  =  require ( 'laravel-mix' ) ;

mezclar . js ( 'src / index.js' ,  'dist / webcomponents.js' ) . vue ( { 
  versión : 2 , 
  opciones : { 
      shadowMode : true 
  } 
} ) ;

Déjame saber si necesitas algo más.

Repo de ejemplo completo: https://github.com/Froxz/webcomponents

PD: Creo que si usa Vue CLI, no funcionará especialmente si target wc

With your example you have saved my life, you should make the pull request of the changes so that laravel-mix takes them into account. Thanks a thousand thanks