How to create Vue package that imports another component
Asked Answered
R

2

7

We are attempting to create a Vue npm package where one component imports another component (a simple example might be - the package contains a generic button component, and a table component that uses my button component for paging). I can import the child component into the parent component and build the package successfully. But when I import and use the parent component (from the package) in an application, the child component content does not display. Skips past it like it isn't there.

I am at a loss.

I am using some very simple components for testing:

<!-- Child Component -->
<template>
    <div id="childElement">Child Component</div>
</template>

<script>
    export default {
        name: "childComponent"
    }
</script>

<style>
    #childElement {
        font-weight: bold;
    }
</style>
<!-- Parent Component -->
<template>
    <div>
        <div id="parentElement">Parent Component</div>
        <child-component></child-component>
    </div>
</template>

<script>
    import ChildComponent from "./ChildComponent.vue";

    export default {
        name: "parentComponent",
        components: {
            ChildComponent
        }
    }
</script>

<style>
    #parentElement {
        font-weight: bold;
    }
</style>

(Edit) Here is my index.js file

import ParentComponent from './components/ParentComponent.vue';
import ChildComponent from './components/ChildComponent.vue';

const install = Vue => {
  Vue.component("ParentComponent", ParentComponent);
  Vue.component("ChildComponent", ChildComponent);
};

export default {
  install
};

export { ParentComponent, ChildComponent };

Usage (in a different app, after a "yarn add ...")

<template>
  <div>
    <div>
      <h1>Testing Section</h1>
      <parent-component></parent-component>
      <h1>End Testing Section</h1>
    </div>
  </div>
</template>

<script>
  import { ParentComponent, ChildComponent } from ...;

  export default {
    name: 'TestApp',
    components: {
      ParentComponent,
      ChildComponent
    }
  };
</script>

And this is what I get on the page:

Testing Section
Parent Component
End Testing Section

Here are some things that I know:

  • The import path in ParentComponent to ChildComponent is correct. If I purposefully change the path to something that doesn't exist, it errors when I try to build.
  • If I import and use ChildElement directly in the app, it works as expected.
  • If I take ParentComponent and ChildComponent and copy them into the app structure, then import them that way (instead of importing them from the package), then ParentComponent displays ChildComponent exactly the way I would expect it to.

I see other packages out there that seem to import components this way, so I think what I'm doing is possible.

I would be grateful for any thoughts anyone has!

(and let me know if it would be helpful if I included anymore code from the project)

UPDATE I've tracked this down a little more. When I include the child component, I'm getting this warning from the app:

[Vue warn]: resolveComponent can only be used in render() or setup().

which has helped me find other resources on the internets, but nothing has helped. My simple tests aren't explicitly using resolveComponent anywhere. And if I do use it and a render function instead of a template, same result, same warning.

Rattigan answered 11/11, 2020 at 4:42 Comment(4)
I can't reproduce the problem with the example you've given. However, the problem could be in the way you've built the library, or how you've imported the components in the app. Can you provide a repro in GitHub?Karyosome
Do you include the template compiler in the package? In vue.config.js include runtimeCompiler: true.Gisele
@Gisele That wouldn't be needed, since the OP is using SFCs.Karyosome
With a template in the sfc...Gisele
I
5

After some rather exhaustive googling and experimentation, I believe the problem you are seeing has to do more with how imported packages work when added from a local folder (pre-published to npm or not added from npm) versus packages that are added via npm or yarn.

The biggest clue to figure this out was the two instances of Vue, which triggers the error in your update.

[Vue warn]: resolveComponent can only be used in render() or setup().

Running down that error, was not so much helpful in and of itself, however, the information that it is happening due to two Vue instances (like you also mentioned finding), was key. It seems that this occurs with local implementations of npm packages. So the solution is to do one or a combination of the following:

A) Find a testing workflow for use in the development of the package that doesn't require you to install the package in a traditional sense and instead import it from a local folder that lives outside of your testing area. Or, build your testing/docs app into your package repo. This way you can build normally without version bumping every little change and only publish and bump versions when you are truly ready.

Then test the usage of your package with a full install from the published npm source as a separate step only after you publish each new version.

B) Locally publish your file using npm pack or yarn pack. This will create a .tgz file. You can use it in your project by adding/installing it from the local path. npm install local-path-to\your-cool-package.tgz or yarn add local-path-to\your-cool-package.tgz. This comes with the caveat that for every change you want to see in your testing app, you will have to remove and re-install it pointing to the new version bump it will append to your file name. You can't simply save and publish to that same file to see updates. You have to bump each change.

Obviously, this is a bit of a clunky workflow, but it works. Perhaps it could be a medium step between a full publish to npm to test a build change. But it seems like, to me anyway, not having worked on packages much personally, that A is a slightly better workflow than B.

Here are some related posts that explain more about similar duplicate Vue with npm install/link/pack and how they work differently for local packages:

Bundling a plugin with Rollup but having duplicate Vue.js package imported in the client app's bundle (Nuxt)

Difference between npm link x and npm install /path/to/x

Intercalate answered 19/11, 2020 at 22:28 Comment(0)
M
1

First, you should check your console if there are any errors.

Also this flat import looks weird import { ParentComponent, ChildComponent } from ...; If you already imported the child component in the Parent, then no need to import again.

I don't know what do you have in your index.js of your package. but I can recommend it to be something like this:

import ParentComponent from './components';
 
export default ParentComponent;

Then, you can use it this way: import ParentComponent from 'NameOfYourNpmPackage'

Milly answered 11/11, 2020 at 6:38 Comment(2)
Thanks for your response. I've updated my question to include my index.js file. You're right, the import doesn't need both components, that's just left over from when I added both to the page separately (same as having both in the index.js file). But removing them makes no difference.Rattigan
I've updated with some further investigation I've done, maybe it'll help...Rattigan

© 2022 - 2024 — McMap. All rights reserved.