Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vue 3 Web Component target #6033

Open
ivansieder opened this issue Nov 4, 2020 · 42 comments
Open

Vue 3 Web Component target #6033

ivansieder opened this issue Nov 4, 2020 · 42 comments
Labels
contribution welcome intend to implement The team has the intention to implement this feature at some point. Contribution is also welcome.

Comments

@ivansieder
Copy link
Contributor

What problem does this feature solve?

As a vue-cli and Vue 3 user I would like to be able to build and deploy a package as a web compomponent, so that it can be built and deployed in the same way as it's currently possible with vue-cli and Vue 2

What does the proposed API look like?

i.e. vue-cli-service build --target wc --name my-web-component-name --inline-vue

@y-sanchez
Copy link

Hi,

I have the same require, i would like to be able to build and deploy a package as a web compomponent with vue3.

image

Is there a date to support of the web component with vue3?

Thks

@haoqunjiang haoqunjiang added contribution welcome intend to implement The team has the intention to implement this feature at some point. Contribution is also welcome. labels Nov 26, 2020
@ivansieder
Copy link
Contributor Author

@y-sanchez we for our part didn't necessary require web components for now, so we've been ok with custom elements, too. So for now we've used custom elements, following is a (shortened) sample code that we use:

import { createApp, watch } from "vue";

import App from "./App.vue";

class CustomElement extends HTMLElement {
  constructor() {
    super();
  }

  connectedCallback() {
    const options = typeof App === "function" ? App.options : App;
    const propsList: string[] = Array.isArray(options.props) ? options.props : Object.keys(options.props || {});

    const props: { [index: string]: string} = {};
    // Validate, if all props are present
    for (const prop of propsList) {
      const propValue = process.env.NODE_ENV === "development" ? process.env[`VUE_APP_${prop.toUpperCase()}`] : this.attributes.getNamedItem(prop)?.value;

      if (!propValue) {
        console.error(`Missing attribute ${prop}`);
        return;
      }

      props[prop] = propValue;
    }

    const app = createApp(App, props);

    const wrapper = document.createElement("div");
    app.mount(wrapper);

    this.appendChild(wrapper.children[0]);
  }
}

window.customElements.define("simedia-cloud-checkin", CustomElement);

@y-sanchez
Copy link

@ivansieder, Thank you for your sample.
I will try this.

@dekadentno
Copy link

Does someone have proof-of-concept repo or codesandbox for a workaround on this? @y-sanchez @ivansieder
Building components and publishing them in Vue 3 would be awesome.

@ivansieder
Copy link
Contributor Author

@dekadentno we've worked around by just using custom elements (instead of actually web components) with the above little wrapper: #6033 (comment)

@melenaos
Copy link

I am using defineComponent for every component the library has and then I export all in the index.ts file

File: DateRangePicker.vue
export default defineComponent({
  name: 'DateRangePicker',
...
});

File: index.ts
import DateRangePicker from '@/components/DateRangePicker.vue';
export{
DateRangePicker
....
};

Except from the triviality of having to export the components in the index.ts file, the lib is working great.

Is there anything that could go wrong with this approach? It's my first lib and I am not sure if this is the best aproach.

@dekadentno
Copy link

Thank you for your quick replies @melenaos @ivansieder

@ranjanngc
Copy link

Please let me know when this will be supported

@ricvillagrana
Copy link

I totally need this feature, I am trying to export some components to let users embed them into their website by just importing them via script tag. If anyone has a workaround on how to achieve this, would love to know. Thanks in advance.

Previously achived with Vue2 but needed to upgrade to Vue3, and it broke everything.

@ivansieder
Copy link
Contributor Author

@ricvillagrana if customElements is enough for you, we're currently using this workaround: #6033 (comment)

But please be aware that this implementation ist not WebComponent-compliant. It's just one part of the WebComponent spec (customElements)

@mrfy
Copy link

mrfy commented Jun 6, 2021

@ivansieder
Thank you for this workaround. If this is how we define a CustomElement, then how to build it, with the lib as --target flag? Ex. vue-cli-service build --target lib?

@ivansieder
Copy link
Contributor Author

@mrfy yes, we're exporting the package as library. If you need/want to include Vue itself in the package (it gets externalized by default), you need to change that in your config, too.

@bdelate
Copy link

bdelate commented Jun 15, 2021

Does anyone know if there is a roadmap for future Vue development which might give some insight into when exporting a Vue app as a web component will be implemented?

Unfortunately upgrading to Vue 3 is not an option until this is available as it is with Vue 2.

ps. Thanks to all the Vue contributors for continually making Vue amazing.

@dsldiesel
Copy link

Anyone in dev to know when this npm run build --target wc is planned out for vue3?

@vikaspotluri123
Copy link

Vue 3.2 added support for compiling WebComponents, but the implementation is different from Vue 2

https://v3.vuejs.org/guide/web-components.html

@dsldiesel
Copy link

Thanks @vikaspotluri123
Although this 3.2 added feature is published, I can't see any reason to believe they won't have soon also the npm run build --target wc in the CLI. The error message today is 'it is not implemented YET'.
The solution I seek, always existing in vue2, does not need a single line of coding or preparation. It takes a SFC .vue as input and does all the job:
npm run build -- --target wc --name ui-leafletmap 'src/components/organisms/ui-leaflet-map/index.vue'

That's all I ask :)

@Tibze
Copy link

Tibze commented Sep 16, 2021

Is it available in Vue-CLI 5 Beta ?
thank you to vue3 for making the creation of web component even easier but if you can't compile into a custom element target with Vue cli it is currently blocking

@TobiasFP
Copy link

TobiasFP commented Dec 15, 2021

For anybody that is having a bit of a hard time getting ivansieders neat and great fix to work, here is a little bit of a helping hand.
I am currently using vue 3.2, with typescript and using the vue-class-component.
With the vue-class-component it is not possible to use ivansieders fix in its entirity, and the options (especially the props) are not defined explicitly on the components.

Besides my main.ts file, I have created the file export-wp-plugin.ts (as I am exporting the component as a customElement to become part of a wordpress plugin).
For clarification, this exports a component (in my case a subcomponent), not the entire app, as a customElement.

My code now looks like this

import { createApp } from 'vue';
import router from './router';
import store from '@/store';
import App from './components/global/Auth/Auth.vue';

class CustomElement extends HTMLElement {
    constructor() {
        super();
    }

    connectedCallback() {
        // Manually added propslist, found in 
        // ./components/global/Auth/Auth.vue, under @options decorator, .props
        const propsList: string[] = ['logoPath', 'plan_id'];

        const props: { [index: string]: string } = {};
        // Validate, if all props are present
        for (const prop of propsList) {
            const propValue = process.env.NODE_ENV === 'development' ? process.env[`VUE_APP_${prop.toUpperCase()}`] : this.attributes.getNamedItem(prop)?.value;

            if (!propValue) {
                console.error(`Missing attribute ${prop}`);
                return;
            }

            props[prop] = propValue;
        }

        const app = createApp(App, props);
        app.use(store);
        app.use(router);
 
        const wrapper = document.createElement('div');
        app.mount(wrapper);

        this.appendChild(wrapper.children[0]);
    }
}

window.customElements.define('wp-plugin-auth', CustomElement);

The key differences is that I create my app with createApp and then load all the necessary stuff, like app.use(store) that I had defined in my main.ts file before (as this is not loaded in by this script).
The other difference is that I have written my props explicitly, like so:
const propsList: string[] = ['logoPath', 'plan_id']; instead of getting them from the file (which is now, to my knowledge, impossible with the vue-class-component).

I can build this by running:
vue-cli-service build --target lib --inline-vue ./src/export-wp-plugin.ts
Now, in my dist folder, I can simply add the following index.html file (derived from the demo.html in the same folder):

<meta charset="utf-8">
<title>auth demo</title>
<script src="./auth.umd.js"></script>

<link rel="stylesheet" href="./auth.css">
<wp-plugin-auth logoPath="myLogoPath.png" plan_id="testplan"></wp-plugin-auth>

I hope this can be of help to somebody.
sadly, I could not get proper WC working with vue 3.2 yet :(

@nztomas
Copy link

nztomas commented Jan 2, 2022

Okay I'm confused - so I jumped onto vue3 / typescript / composition API / Web Component band wagon after reading multiple blogs about developing web components with the Vue and also read https://cli.vuejs.org/guide/build-targets.html#web-component

I have my tiny web component running in dev mode on my local and now I'm finding out that it's not possible to build it with the 'wc' target? Or am I doing something wrong ?

Why this is not mentioned anywhere in the documentation? Why we are saying Vue 3.2 release has support for defineCustomElement when (if I understand correctly) is not possible to build it?

Sorry but I'm completely confused now...

@dparish
Copy link

dparish commented Jan 11, 2022

I'm not sure we are looking at this correctly. Do we even NEED a web component build target?

The purpose of defineCustomElement appears to be to create a registrable web component. I converted my main.ts to look like this:

(notice ALL the Vue bootstrapping is gone)

import { defineCustomElement } from 'vue';
import SwFullName from './components/SwFullName.ce.vue';

const FullNameElement = defineCustomElement(SwFullName);
customElements.define('sw-full-name', FullNameElement);

And my public/index.html is now:

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div>
        <sw-full-name></sw-full-name>
    </div>
    <!-- built files will be auto injected -->
  </body>
</html>

This works GREAT in both npm run serve AND npm run build.

@leonheess
Copy link

Proper building as WC is definitely needed asap for Vue 3 adoption.

@Makoehle
Copy link

Makoehle commented Jan 18, 2022

This works GREAT in both npm run serve AND npm run build.

I thought I can get arround that web component error as well by using custom element but as soon as you want to build something bigger you're going to come accross:

@telion2
Copy link

telion2 commented Jan 28, 2022

@dparish
Follow up question though: If you simply run build, it would build a whole vue application correct?
Including the index.html etc.

Isn't the idea, to get the webcomponent only (including everything that is needed for the webcomponent to function)?
I mean the idea is usually to publish the webcomponent somewhere, for other to use with minimal integration effort.

Or in other words: How do I use the result of the build as a webcomponent?

@telion2
Copy link

telion2 commented Jan 28, 2022

Also, what is the status? Is it work in Progress? Blocked by something? Is there some estimate when it will be implemented?

@dparish
Copy link

dparish commented Jan 28, 2022

@telion2

It will generate the .js file you need to include to run your web component. I modified my index.html to use my web component tags instead of bootstrap the vue app and it just worked.

There should be enough in my example above for you to be able to bootstrap something on your own. If there's not let me know and I'll see what I can do.

@telion2
Copy link

telion2 commented Jan 28, 2022

@dparish
Well npm run serve works for me too. But if I run the normal build then I get
Screenshot from 2022-01-28 16-35-55
Say I publish that to npm and hit npm install on it: How would someone use it?
I admit I am somewhat new to WC, but I would expect:

  1. that the user of the WC has to add <sw-full-name></sw-full-name> somewhere
  2. that the user of the WC has to import some js somewhere. This is where I got lost.
    One way would be to do something like this:
    <script src="pathOrLinkToWC" />
    or something like
    import swFullName from "swFullName"; //and continue integrating the wc depending on the framework, doesn't have to be Vue

Edit: Upon Rereading the Docs:
Screenshot from 2022-01-28 17-02-56

So the main question is the part with <script src="path/to/my-element.js"></script>
as I don't know what the user should provide into 'src'.

@telion2
Copy link

telion2 commented Jan 28, 2022

Ok I managed to pull it of somehow:

so my final command is:
"build:wc": "vue-cli-service build --target lib --name myElement src/main.ts --inline-vue",
This results into this dist folder:
Screenshot from 2022-01-28 18-18-56

Now the demo was the part I was interested in:

<!DOCTYPE HTML>
<meta charset="utf-8" /> 
<title>myElement demo</title>
<script src="./myElement.umd.js"></script>
<link rel="stylesheet" href="./myElement.css" />
<my-element></my-element>
<script>
  console.log(myElement);
</script>

However, I had to manually add the line <my-element></my-element>

//main.ts
import { defineCustomElement } from "vue";
import App from "./App.ce.vue";              

customElements.define("my-element", defineCustomElement(App));

To get around vuejs/core#4662 and while I was at dealing with https://stackoverflow.com/questions/70878556/how-do-i-use-primevue-in-customelements as well, I did this:

// App.ce.vue 
<template>
  <someOtherComponent msg="name" />
</template>

<script lang="ts">
import { defineComponent } from "vue";

import someOtherComponent from "@/components/someOtherComponent.ce.vue";
export default defineComponent({
  name: "my-element",    //I think this should be the same string as the one used in customElements.define() but not sure
  components: { someOtherComponent },
});
</script>

<style>
@import url("https://unpkg.com/primevue/resources/primevue.min.css");
@import url("https://unpkg.com/primeflex@3.1.0/primeflex.css");
@import url("https://unpkg.com/primevue/resources/themes/tailwind-light/theme.css");
</style>
<style lang="scss" src="@/assets/scss/globals.scss"></style>
/*//globals.scss*/
/* Global Styles here:  */
.someClass { color: red; }
/* Component Style should be imported here till https://github.com/vuejs/core/issues/4662 is resolved */
@import "@/components/someOtherComponent.scss";

Apparently, there are Issues with scoped (s)css so I added them on the "root" wc component. Then I created a global .scss file where I instruct everyone in the team to import the scss code from their child components as shown.
Additionally, they should add them as they normally would.
This way, when the styles Issue is resolved the workaround can be deleted rather easily.
However the team has to add a prefix to each css rule. e.g. `someOtherComponent-colorText{ color: blue; }

//vue.config.js
const { defineConfig } = require("@vue/cli-service");
module.exports = defineConfig({
  transpileDependencies: true,
  chainWebpack: (config) => {
    config.module
      .rule("vue")
      .use("vue-loader")
      .tap((options) => {
        options.customElement = true;
        return options;
      });
  },
});

I'm am not sure if the change in vue.config.js is necessary, but I just report what I changed in my project to get where I am.
So for anyone trying to build WC with Vue3: Its possible, but wait until they fix it properly, if you can.

@leonheess
Copy link

wait until they fix it properly, if you can.

@telion2, that's exactly the conclusion I came to. Do you think it will be fixed?

@telion2
Copy link

telion2 commented Jan 31, 2022

@leonheess I have no Idea. I am just a Developer of which a client asked to create a Web Component. After Planning, Comparing SPA-Frameworks, Reading Documentation and evaluating multiple aspects of a project, we decided to use Vue 3, just to find out, it only works with serious research and workarounds.
That is not to take away from the otherwise superb library and Framework, that Vue and Vue CLI usually is. So me sounding rather annoyed about the documentation should not overshadow the fact that I see great value and quality in Vue 3 and Vue CLI.

The Problem with Vue 2 is that you are forced to use Libraries that don't look like they are actively maintained:

  • "@vue/web-component-wrapper" last commit 12 months old
  • vue-custom-element last commit 11 months old.

Meaning, that for a new project you risk the lack of support or fixes in that area.

@tunessofia
Copy link

@sodatea do you know the current progress of this feature? it will be present in vue-cli 5 ?

@3zzy
Copy link

3zzy commented Feb 22, 2022

@tunessofia Probably not, judging from this:

Vue CLI is the official webpack-based toolchain for Vue. It is now in maintenance mode and we recommend starting new projects with Vite unless you rely on specific webpack-only features. Vite will provide superior developer experience in most cases.

— https://vuejs.org/guide/scaling-up/tooling.html#project-scaffolding

@yoyo837
Copy link

yoyo837 commented Apr 25, 2022

Although this 3.2 added feature is published, I can't see any reason to believe they won't have soon also the npm run build --target wc in the CLI. The error message today is 'it is not implemented YET'. The solution I seek, always existing in vue2, does not need a single line of coding or preparation. It takes a SFC .vue as input and does all the job: npm run build -- --target wc --name ui-leafletmap 'src/components/organisms/ui-leaflet-map/index.vue'

That's all I ask :)

That's exactly what I want to need.

@cookiejest
Copy link

is this dead then?

@snake-py
Copy link

having the same problem

@yoyo837
Copy link

yoyo837 commented Dec 13, 2022

Even though it may be far away, is there a planned implementation date by official?

@nztomas
Copy link

nztomas commented Dec 13, 2022

I'm doing web components via Vite + Vue3 composition API now - if someone would be interested I could create and upload base project...

@3zzy
Copy link

3zzy commented Dec 13, 2022

@nztomas Yes please, thanks.

@leonheess
Copy link

@nztomas how did you solve the scoped styles issue?

@ahmedsakri
Copy link

I'm doing web components via Vite + Vue3 composition API now - if someone would be interested I could create and upload a base project...

@nztomas Can you please share the sample repository doing so?

@yashwanth2714
Copy link

I'm following up on this issue. Do we have an estimated time of completion for the fix?

@stefanonepanedap
Copy link

Interested here 🤗

@rajeevverma076
Copy link

When I'm trying to publish multiple web components using the vue cli command vue-cli-service build --target wc --inline-vue --name my-element src/*.vue. it is building perfectly fine but external dependencies like CSS and javascript file is not importing in the build.

Can anyone help me with how to build web components with all external dependencies? import all CSS from node_modules as a dependency.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
contribution welcome intend to implement The team has the intention to implement this feature at some point. Contribution is also welcome.
Projects
None yet
Development

No branches or pull requests