4

Any help with the following problem would be greatly appreciated!

Situation:

My project contains two packages:

  1. child-component-lib

    • contains a single view About.vue written in composition-API-style (with vue2 helper libraries @vue/composition-api and vuex-composition-helpers)
    • exports a single RouteConfig
    • build as a lib

    views/About.vue (child)

    <template>
      <div class="about">
        <h1>This is an about page (as component lib)</h1>
      </div>
    </template>
    
    <script>
    import { defineComponent } from "@vue/composition-api";
    import { createNamespacedHelpers } from "vuex-composition-helpers";
    
    export default defineComponent({
      components: {},
      setup(_, { root }) {
        const { useGetters, useActions } = createNamespacedHelpers("account"); // error thrown here!
      }
    });
    </script>
    

    router/index.ts (child)

    export const routes: Array<RouteConfig> = [{
        path: "/",
        name: "About",
        component: () => import(/* webpackChunkName: "about" */ "../views/About.vue")
    }];
    

    lib.ts (child)

    export const routes = require("@/router").routes;
    

    package.json (child)

    "scripts": {
      "build": "vue-cli-service build --target lib --name child-component-lib src/lib.ts"
    ...
    

  1. parent-app

    • imports the route from child-component-lib into its router
    • contains a simple view that displays one line of text and a <router-view />

    package.json (parent)

    "dependencies": {
        "@tholst/child-component-lib": "file:../child-component-lib",
    

    router/index.ts (parent)

    import { routes as childComponentRoutes } from "@tholst/child-component-lib";
    
    const routes: Array<RouteConfig> = [...childComponentRoutes];
    const router = new VueRouter({routes});    
    export default router;
    

    App.vue (parent)

    <template>
      <div id="app">
        <Home />
        <router-view />
      </div>
    </template>
    
    <script>
    import { defineComponent } from "@vue/composition-api";
    import Home from "@/views/Home.vue";
    
    export default defineComponent({
      components: {
        Home
      },
      setup(_, { root }) {
        ...
      }
    });
    </script>
    

Expected behavior

It works without problems.

Actual behavior

I see an error output in the console. [Vue warn]: Error in data(): "Error: You must use this function within the "setup()" method, or insert the store as first argument." The error message is misleading, because the error is actually thrown inside setup() method. It can be traced back to getCurrentInstance() returning undefined (inside @vue/composition-api).


Investigation:

  • It turns out that the error disappears when I include the same About.vue in the parent-app itself (just switch the route, to try it out), i.e., it works when we avoid the import from the built library.
  • So it looks like it's a problem with the build setup (one of vue.config.js, webpack, babel, typescript, ...)

Reproduce the error:

1. Clone, install, run

git clone git@github.com:tholst/vue-composition-api-comp-lib.git && cd vue-composition-api-comp-lib/child-component-lib && npm install && npm run build && cd ../parent-app/ && npm install && npm run serve

or one by one

git clone git@github.com:tholst/vue-composition-api-comp-lib.git
cd vue-composition-api-comp-lib/child-component-lib
npm install
npm run build
cd ../parent-app/
npm install
npm run serve

2. Open Browser

  • Go to http://localhost:8080/

3. Open Dev Tools to See Error

[Vue warn]: Error in data(): "Error: You must use this function within the "setup()" method, or insert the store as first argument."

found in

---> <Anonymous>
       <App> at src/App.vue
         <Root>

Error Screenshot

Error in console

Environment Info:

Node: 14.2.0
npm: 6.14.8
Chrome: 86.0.4240.198

npmPackages:
    @vue/babel-sugar-composition-api-inject-h:  1.2.1 
    @vue/babel-sugar-composition-api-render-instance:  1.2.4 
    ...    
    @vue/cli-overlay:  4.5.8 
    @vue/cli-plugin-babel: 4.5.8 
    @vue/cli-plugin-router: 4.5.8 
    @vue/cli-plugin-typescript: 4.5.8 
    @vue/cli-plugin-vuex:4.5.8 
    @vue/cli-service: 4.5.8 
    @vue/cli-shared-utils:  4.5.8 
    @vue/component-compiler-utils:  3.2.0 
    @vue/composition-api: 1.0.0-beta.19 
    @vue/preload-webpack-plugin:  1.1.2 
    typescript: 3.9.7 
    vue: 2.6.12 
    vue-loader:  15.9.5 (16.0.0-rc.1)
    vue-router: 3.4.9
    vue-template-compiler: 2.6.12 
    vue-template-es2015-compiler:  1.9.1 
    vuex: 3.5.1 
    vuex-composition-helpers: 1.0.21 

npmGlobalPackages:
    @vue/cli: 4.5.8
Thomas
  • 2,155
  • 16
  • 22

1 Answers1

3

I finally understood what the problems were. First, there was the actual problem. Second, there was a problem in the local development setup that made solutions to the actual problem look like they were not working.

The Actual Problem + Solution

The child-component-lib was bundling their own versions of the npm packages @vue/composition-api and vuex-composition-helpers. This had the following effect: When I was running the parent-app there were actually two instances of those libraries and the vue component from the child-component-lib was accessing the wrong object that had not been properly initialized.

The solution was to prevent the bundling of those libraries in the child-component-lib, by

  1. making them devDependencies and peerDependencies.

  2. instructing webpack not to bundle them on npm run build.

    package.json

    "dependencies": {
        ...
    },
    "devDependencies": {
        "@vue/composition-api": "^1.0.0-beta.19",
        "vuex-composition-helpers": "^1.0.21",
        ...
    },
    "peerDependencies": {
        "@vue/composition-api": "^1.0.0-beta.19",
        "vuex-composition-helpers": "^1.0.21"
    },
    

    vue.config.js

    configureWebpack: {
        externals: {
            "@vue/composition-api": "@vue/composition-api",
            "vuex-composition-helpers": "vuex-composition-helpers"
        },
        ...
    }
    

The Tricky Problem that Made Things Difficult

I was trying to fix this problem locally, without actually publishing the package. And it seemed to work, because I was seeing the same problem locally that I also saw in the published packages.

I did local development by directly linking the parent-app and child-component-libs. I tried both

  1. a direct folder dependency

    package.json

    "dependencies": {
        "@tholst/child-component-lib": "file:../child-component-lib",
    },
    
  2. npm link

    cd child-component-lib
    npm link
    cd ../parent-app
    npm link @tholst/child-component-lib
    

Both approaches have the effect that they actually import (=symlink to) the child-component-lib's folder with all files and folders (instead of only the files that would be published in the npm package).

And that meant the following: Even though I had excluded the two composition-API libs from the bundle and made them dev/peer dependencies (see solution to actual problem), they were still installed and present in the child-component-lib's node_modules. And that node_modules folder was symlinked into the parent-app package. And in this way the child-component-lib still had access to their own copy of the libraries that we wanted to exclude from the build (see actual problem). And I was still seeing the error as before.

And this way my local development approach obscured the fact that the solution to the actual problem was actually working.

Thomas
  • 2,155
  • 16
  • 22