DeliciousPickle
DeliciousPickle

Reputation: 21

How to import a Vue component that is packaged into a JavaScript library?

I am attempting to package a Vue component into a JavaScript library and then use it in another project using vue-sfc-rollup.

I am able to package the component just as the README says to do. Then when I copy the .min.js file into another project and attempt to use the component, I always get the error:

Vue warn handler:  Failed to mount component: template or render function not defined.

The way I'm trying to use the component from inside another Vue component is this:

import Vue from 'vue'
import MyComponent from '../lib/my-components.min.js'
Vue.use(MyComponent)

Then in the components section:

components: {
    'my-component': MyComponent
}

Then in the template:

<my-component></my-component>

What am I missing here? What is the correct way to use the component in another project?

EDIT: Adding component code in response to comment.

<template>
    <div class="my-component">
        <p>The counter was {{ changedBy }} to <b>{{ counter }}</b>.</p>
        <button @click="increment">
            Click +1
        </button>
        <button @click="decrement">
            Click -1
        </button>
        <button @click="increment(5)">
            Click +5
        </button>
        <button @click="decrement(5)">
            Click -5
        </button>
        <button @click="reset">
            Reset
        </button>
    </div>
</template>

<script>
    export default {
        name: 'MyComponent', // vue component name
        data() {
            return {
                counter: 5,
                initCounter: 5,
                message: {
                    action: null,
                    amount: null,
                },
            };
        },
        computed: {
            changedBy() {
                const {
                    message
                } = this;
                if (!message.action) return 'initialized';
                return `${message?.action} ${message.amount ?? ''}`.trim();
            },
        },
        methods: {
            increment(arg) {
                const amount = (typeof arg !== 'number') ? 1 : arg;
                this.counter += amount;
                this.message.action = 'incremented by';
                this.message.amount = amount;
            },
            decrement(arg) {
                const amount = (typeof arg !== 'number') ? 1 : arg;
                this.counter -= amount;
                this.message.action = 'decremented by';
                this.message.amount = amount;
            },
            reset() {
                this.counter = this.initCounter;
                this.message.action = 'reset';
                this.message.amount = null;
            },
        },
    };
</script>

<style scoped>
    .my-component {
        display: block;
        width: 400px;
        margin: 25px auto;
        border: 1px solid #ccc;
        background: #eaeaea;
        text-align: center;
        padding: 25px;
    }

    .my-component p {
        margin: 0 0 1em;
    }
</style>

Upvotes: 1

Views: 922

Answers (1)

DeliciousPickle
DeliciousPickle

Reputation: 21

I found one way to do this at this Stack Overflow question: Register local Vue.js component dynamically.

I got it to work by implementing a simpler version of the solution shown there. I removed the component section from the outer component, then added this created() lifecycle hook:

created() {
    console.log('pages/PageHome.vue: created(): Fired!')
    // From https://stackoverflow.com/questions/40622425/register-local-vue-js-component-dynamically
    // "This is how I ended up importing and registering components dynamically to a component locally"
    const componentConfig = require('../lib/components/my-component.js')
    console.log('pages/PageHome.vue: created(): componentConfig.default = ')
    console.log(componentConfig.default)
    const componentName = 'my-component'
    console.log('pages/PageHome.vue: componentName = ' + componentName)
    this.$options.components[componentName] = componentConfig.default
}

The component is imported using a require() call, then registered locally by adding it to the this.$options.components dictionary. The secret sauce is to add .default to the componentConfig expression. This doesn't seem to be formally documented anywhere.

Editorial comment: I'm surprised the Vue documentation pays such little attention to distribution patterns for re-usability. As great as the Vue docs are, this is a glaring omission.

Upvotes: 1

Related Questions