Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue 3 component incorrectly initialized when module is `npm link`ed

Following is the entry point to my library, it generates a component with a dynamic tag:

// muvement.js

import { defineComponent, ref, onMounted, h } from 'vue';

const createMuvement = (tag) => {
    return defineComponent({
        name: `m-${tag}`,
        setup(props, context) {
            const root = ref(null);
            onMounted(() => {
                console.log(root.value);
            });
            return () => h(tag, { ...context.attrs, ref: root }, context.slots);
        }
    });
};

const muvement = (...tags) => {
    const components = {};
    tags.map((tag) => (components[`m-${tag}`] = createMuvement(tag)));
    return components;
};

export { muvement };

It's expected to be consumed like so:

// Home.vue

<template>
  <div>
    <m-div>div</m-div>
    <m-button>button</m-button>
  </div>
</template>

<script>
import { muvement } from "muvement";

export default {
  name: "Home",
  components: {
    ...muvement("div", "button")
  }
};
</script>

This works as expected when the library code is contained within the Vue app folder (assuming we are now importing from "@/components/muvement.js" instead of "movement").
That is:

-muvement-test-project (scaffolded with vue-cli)
    - src
        - views
            - Home.vue
        - components
            - muvement.js

I've also published an alpha release that works fine when importing "muvement" after installing it directly from the npm registry (that is, npm install muvement instead of npm link muvement).

The Problem

During development, I want an app to test the library with that is separate from the library's directory.

I've used npm link to link the library to the test app (as I have done with many other projects in the past).

From /path/to/library

$ npm link

From /path/to/test/app

$ npm link muvement

So far so good. The module is available as a symlink in the test app's node_modules folder. So I import { muvement } from "muvement", run npm run serve, and... BOOM.
Everything explodes (see errors below). It's also probably worth noting that trying to import from the full path (i.e. C:/dev/npm/muvment/dist/es/index.js) results in the same issues as npm link does, so I don't think it has anything to do with the symlink directly.

This is what appears in the console:

warning in Chrome devtools

For pretty much the entire day I have been trying to solve this one issue. I've seen several seemingly similar questions that were solved by settings Webpack's resolve.symlinks to false but that has no effect on my problem. I've read all through the docs and even Vue's source code (here is the offending line for those who are curious).

Since the warning suggests that the error is commonly attributed to async setup I thought maybe webpack was doing something weird that would make my code async. This doesn't seem to be the case as the call stack of both the working attempt and failed attempt are identical.

What's not identical is the scope.

Here is the scope for the example that is working:
enter image description here

And here is the failing one: enter image description here

(Notice that the target parameter is null during the call to injectHook, which is obviously what prompts Vue to show a warning).

My question is, why does the location of the imported module make such a difference during the execution of the said module?

The library code and build setup are available here:
https://github.com/justintaddei/muvement

The test app is available here:
https://github.com/justintaddei/muvement/tree/example

If I've left out something important, please let me know in the comments. It's been a long day so I'm sure I've probably missed something.

Thank you.

like image 260
Justin Taddei Avatar asked Oct 28 '25 08:10

Justin Taddei


1 Answers

The problem is your app is using two different vue dependencies under the hood - vue requires the same dependency to be used to keep track on reactivity, lifecycle, etc.

When you link a library npm/yarn will use that linked folder node_modules, but your app is using it's dependencies from it's node_modules.

When your app imports vue it will go app/node_modules/vue but when you import from your linked dependency it will be going to linked_dep/node_modules/vue.

app
  node_modules
    vue
linked library
  node_modules    
    vue

One easy way to debug this issue is to change both vue dependency files with a console.log and check if the console is logging both.

like image 116
pikax Avatar answered Oct 29 '25 21:10

pikax



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!