Subscribe on changes!

Incorrect 'computed' import can cause reactivity to break after transpiling

avatar
Jan 25th 2022

Version

3.2.8

Reproduction link

codesandbox.io

Steps to reproduce

  • See in the example how the css classes on a simple button component are updated with a computed property when its props change. This can be viewed with npm run serve. (Sometimes the sandbox throws errors about module import locations, but a refresh fixes that)
  • Run the included build:lib command to build that component as a library. Will probably have to be done outside of CodeSandbox
  • Import that built component into some other Vue application
  • The CSS classes should no longer update, because the computed property is no longer being recalculated when its dependencies change.

What is expected?

After days of debugging this behavior I noticed that VS Code had automatically imported computed for me as:

import { computed } from "@vue/reactivity";

rather than:

import { computed } from "vue";

This import works in development mode, and everything behaves how it should. However, my computed properties were not updating when the props they depended on were updated when I used the vue cli command to build them as a library.

I realize that I was not importing things as documented, but I would rather one of two things happen:

  • See a console warning that I'm importing compute incorrectly
  • VS Code not suggest the incorrect import from vue

What is actually happening?

Computed properties are transpiled differently based on which way you import computed when you're build target is a library. When using the components build as a library the computed properties are only ever calculated once and basically cause components to fail silently.


Ran into this bug while I was developing components for a UI library. VS Code tried to be helpful and import computed for me, but it used the wrong import statement and sent me down a rabbit hole for days. It would be really great if there were some guard rails in place so no one else gets stuck the same way I did!

I can supply more verbose examples of the transpiling differences I'm talking about if that would be helpful.

avatar
Jan 25th 2022

This isn't really a bug. Here's what's happening:

  • Vue 3 reactivity relies on a few singletons in that reactivity package.
  • You built your library and excluded 'vue', as you should, but since you imported from '@vue/reactivity', that package got bundled with your library.
  • So now the consuming app has two copies of @vue/reactivity: one, included in and only used by your lib, and the second one installed in the app's node_modules
  • And these two have their own singletons responsible for managing dependencies and effects.

there's two things that can prevent this from happening:

  1. Use a package manager that prevents you from importing from transitive dependencies, like pnpm.
  2. Use eslint's no-restricted-imports rule to disallow imports from @vue/* dependencies.

We can't really do much in core as we can't prevent consumers from doing this import.

You could maybe open an issue in our eslint-plugin's repo and propose a rule to prevent these imports in our presets?