EranGrin / vue-web-component-wrapper

vue3 - web component wrapper plugin
https://erangrin.github.io/vue-web-component-wrapper/
50 stars 5 forks source link
custom-elements vue3 web-component

vue-web-component-wrapper

Transforming full-fledged Vue3 applications into reusable web components

License MIT version 1.6.4 maintained yes


Introduction

vue-web-component-wrapper is a powerful Vue 3 plugin designed for transforming full-fledged Vue applications into reusable web components (custom elements). These web components can be integrated into any website, enhancing flexibility and reusability.

Why use vue-web-component-wrapper?

As of now, Vue 3 does not support the creation of full aplication as web components out of the box. This plugin aims to solve this problem by providing a simple and easy-to-use solution for creating web components from Vue applications. It also provides support for Vue ecosystem plugins such as Vuex or Pinia, Vue Router, Vue I18n and VeeValidate.

Demo

Check out these demo projects to see vue-web-component-wrapper in action:

Documentation

See Docs

Key Features:

CSS Frameworks Examples

See documentation for more details Docs

Installation

npm install vue-web-component-wrapper
# or
yarn add vue-web-component-wrapper
# or
pnpm add vue-web-component-wrapper

Usage

To create a web component using vue-web-component-wrapper, follow the steps below:

  1. Import the necessary modules in your entry file:
    import App from './App.vue';
    import tailwindStyles from './assets/tailwind.css?raw';
    import { createWebHashHistory, createRouter } from "vue-router";
    import { createI18n } from 'vue-i18n';
    import { createStore } from 'vuex'
    import { createPinia } from 'pinia'
    import { defaultRoutes } from './main.routes.js'
    import { store } from './store/index.js'
    import { defineCustomElement as VueDefineCustomElement, h, createApp, getCurrentInstance } from 'vue';
    import { createWebComponent } from 'vue-web-component-wrapper';
  2. Set up the instances and use your plugins. This is where you configure your Vuex/Pinia store, Vue router, and other Vue plugins.

    export const pluginsWrapper = {
    install(GivenVue: any) {
    const Vue = GivenVue
    
    //Vuex
    const createdStore = createStore(store)
    Vue.use(createdStore)
    
    //or Pinia
    const pinia = createPinia()
    Vue.use(pinia)
    
    //Vue Router
    const router = createRouter({
      history: createWebHashHistory(),
      routes: defaultRoutes,
    })
    Vue.use(router)
    
    //Vue I18n
    const i18n = createI18n({
      locale: 'en',
      fallbackLocale: 'en',
    })
    Vue.use(i18n)
    },
    }
  3. Create your web component using createWebComponent. It takes an options object where you specify your root Vue component, the element name for your custom element, any plugins you want to use, and any CSS framework styles.
    createWebComponent({
    rootComponent: App,
    elementName: 'my-web-component',
    plugins: pluginsWrapper,
    cssFrameworkStyles: tailwindStyles,
    VueDefineCustomElement,
    h,
    createApp,
    getCurrentInstance,
    disableStyleRemoval: false, //default is false
    disableShadowDOM: false, //default is false
    });

    Each option in the createWebComponent function has a specific purpose:

    • rootComponent: The root component of your Vue application.
    • elementName: The tag name for your custom web component. It must contain a hyphen and be lowercase.
    • plugins: Any Vue plugins you want to use in your application.
    • cssFrameworkStyles: Any CSS or SCSS styles that your application needs.
    • VueDefineCustomElement: The defineCustomElement function from Vue.
    • h: The h function from Vue.
    • createApp: The createApp function from Vue.
    • getCurrentInstance: The getCurrentInstance function from Vue.
    • disableStyleRemoval: Boolean to disable removal of styles on unmount.
    • disableShadowDOM: Boolean to disable shadow DOM.

cssFrameworkStyles

The cssFrameworkStyles option is used to import the CSS of your CSS framework or any other css style that your application needs to have globally, this option can also handle css vars that define on a :root selector.

  1. Build your application. Tested bundler to build the web-component application.

    Bundlers Configuration

Vite Configuration ## Vite.js Configuration Here's a sample Vite configuration. Comparing with Webpack, Vite.js is able to handle assets files like .css and .scss, and media files, importing them as you do regularly. Vue files will be parsed using oficial [@vitejs/plugin-vue](https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue) depending of config. If you would like to add plugins for Vite, just install them with your favorite Node package manager. ```javascript import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ build: { sourcemap: 'inline', }, plugins: [ vue({ customElement: true, }), ], }) ``` In your main.js/ts file, you will have to import the css framework in slightly different way then webpack with ```?inline``` at the end of the import statement. This leads to a new iusse with fonts, which are not loaded when using ```?inline```. To fix this, you can import the font css in the App.vue file. ### main.js/ts ```javascript // ?inline can not handle import url() in css therefore fonts are not loaded, workaround is to add font css to the App.vue import style from './style.css?inline' ``` Workaround for fonts: ### App.vue ```css ```
Webpack Configuration ## Webpack Configuration Here's a sample webpack configuration that helps webpack understand how to load and process .vue, .css, and .scss files. It also sets up an HTML plugin for webpack. ```javascript const path = require('path'); const { VueLoaderPlugin } = require('vue-loader'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { mode: 'production', entry: './src/main.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'my-web-component.js', }, module: { rules: [ { test: /\.(vue|ce\.vue)$/, loader: 'vue-loader', options: { customElement: true, }, }, { test: /\.(css|scss)$/, oneOf: [ { resourceQuery: /raw/, use: [ 'to-string-loader', 'css-loader', 'postcss-loader', { loader: 'sass-loader', options: { sassOptions: { indentedSyntax: false, // Use the SCSS syntax }, }, }, ], }, { use: [ 'style-loader', 'css-loader', 'postcss-loader', { loader: 'sass-loader', options: { sassOptions: { indentedSyntax: false, // Use the SCSS syntax }, }, }, ], }, ], }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, loader: 'file-loader', options: { name: 'assets/[name].[hash:7].[ext]', }, }, ], }, plugins: [ new VueLoaderPlugin(), new HtmlWebpackPlugin({ template: './public/index.html', }), ], resolve: { alias: { vue$: 'vue/dist/vue.esm-bundler.js', }, extensions: ['.js', '.vue', '.json'], }, }; ``` With webpack you will have to import the css framework in slightly different way then vite with ```?raw``` at the end of the import statement. ### main.js/ts ```javascript import style from './style.css?raw' ```

Web Component without Shadow DOM

If you want to create a web component without shadow DOM, you can set the disableShadowDOM option to true in the createWebComponent function. This will create a web component without shadow DOM encapsulation. This feature uses a patch to the Vue source code, which could lead to some issues with future versions of Vue. If you encounter any issues, please report them in the issues section of this repository.

Demo without Shadow DOM

Demo Link

SFC as Custom Element

enhance the core functionality of SFC as Custom Element defineCustomElement with 2 new features:

  1. Nested Components: You can use nested components with styles and for example share base components between multiple custom elements.
  2. Shadow DOM option: You can disable shadow DOM for the SFC custom element.

Usage

// main.js
import { defineCustomElementSFC } from 'vue-web-component-wrapper';
const MyComponentElement = defineCustomElementSFC(MyComponent, {shadowRoot: false})
customElements.define('my-component', MyComponentElement)

Demo SFC Custom Element

Demo Link

Tips

  1. TypeScript Support: Adding proper strict types.

Contributing

Contributions are welcome! To contribute to the project, please follow these steps:

Please make sure to follow the code style and conventions used in the project. If you find a bug or have a feature request, please open an issue on the repository.

License

This project is licensed under the MIT License