Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make vue available to other libraries without having to import it #8278

Open
KaelWD opened this issue May 30, 2018 · 13 comments
Open

Make vue available to other libraries without having to import it #8278

KaelWD opened this issue May 30, 2018 · 13 comments

Comments

@KaelWD
Copy link
Contributor

KaelWD commented May 30, 2018

Title needs work, idk what to call this.

What problem does this feature solve?

Writing a component library with typescript requires importing Vue so you can use Vue.extend(...) to get typings. This causes problems when webpack decides to load a different instance of vue.
See vuetifyjs/vuetify#4068

What does the proposed API look like?

Local registration to accept a function that synchronously returns a component, calling it with the parent vue instance.

The library can then do:

export default function MyComponent (Vue: VueConstructor) {
  return Vue.extend({ ... })
}

And be used like:

import MyComponent from 'some-library'

export default {
  components: { MyComponent }
}

Of course that would then cause other problems, particularly where we use methods directly from other components. Maybe something that adds types like Vue.extend() but doesn't have any runtime behaviour would be better instead?

// When used, this will behave the same as a bare options object, instead of being an entire vue instance
export default Vue.component({
  ...
})
@ktsn
Copy link
Member

ktsn commented May 30, 2018

@KaelWD
Copy link
Contributor Author

KaelWD commented May 30, 2018

👌

How does that handle stuff like mixins (usage) or directly calling methods?

EDIT: Didn't rtfm, this looks perfect. I'll have to try it out to see how well it actually works though. I might have to massage the types a bit to get #2 working.

@posva
Copy link
Member

posva commented May 30, 2018

@ktsn can we close this or is there anything we could do in Vue core to improve the situation?

@KaelWD
Copy link
Contributor Author

KaelWD commented May 31, 2018

The plugin doesn't work on everything, it seems to assume that all exports are Vue.extend()
image

EDIT: Or not, I still get the same error if I wrap that component.

@KaelWD
Copy link
Contributor Author

KaelWD commented May 31, 2018

I'm fine with having to use Vue.extend, it's just not ideal that it creates a standalone instance. It would be nice if vue still resolved component references and everything else the same way as with bare options objects. It isn't really a problem anyway unless one of our users messes up and somehow ends up with two different vue imports.

@yyx990803
Copy link
Member

Alternatively we may try to detect duplicate Vue imports.

@KaelWD
Copy link
Contributor Author

KaelWD commented Jun 2, 2018

Yeah that would be helpful, the current errors don't really indicate what's actually going on very well. Is our current setup likely to be a problem with vue-test-utils and createLocalVue though? vuejs/vue-test-utils#532 seems similar to this one.

@uris-dev
Copy link

uris-dev commented Jul 17, 2018

A few months ago I have asked for help with an issue related to this one on stackoverflow.
I've been living with my temporary solution since then (passing the Vue instance to the library initializer, and not importing Vue in the library), but I'm not very happy about it and now I'm stuck because Vuetify also started to use typescript and creates a separate Vue instance and it's not clear how to tell to webpack (or to Vue) that it's always the same instance that has to be used.
The solution provided at the top of this thread does not help me much, since I'm not using babel.
Are you aware of an existing solution to have a package and dependencies, all written in typescript and compiled with webpack, to share the same Vue instance?

@uris-dev
Copy link

The solution should also allow to use .vue files in packages, which, with the original proposal at the top of this thread would probably not be possible.

@uris-dev
Copy link

After giving a hard look at the problem, the only solution that I found at the moment (with webpack) is to add vue as an external import wherever you import it.
In webpack.config.js

externals: {
    vue: 'Vue'
},

and then import vue.js with a script tag (or an equivalent solution) in your html file.
This avoids that vue is instantiated more than once. Depending on your package and subpackage structure, you probably need to import your subpackages and or vuetify also as external modules.

That means that you have to pack them as standalone libraries, with, in webpack.config.js

    output: {
        path: 'path/to/your/output',
        filename: 'build.js',
        library: 'LibraryName',
    },

and in tsconfig.json

"compilerOptions": {
    ...
    "module": "es2015"
    ...
}

As a side effect with this solution you can also work with vue files in your libraries. The proposed solution at the top of this issue would in any case not allow that, because you cannot inject the vue constructor in a vue file (as far as I understand).
If you are working with tests suites like karma and phantomjs you need to inject vue and the other external libraries there as well.
This is now a workaround and a better solution would be if vue would have at initialization an option to detect if there is another VueConstructor outside the actual bundle and use it (as an option, because that may or may not be desired).

@KaelWD
Copy link
Contributor Author

KaelWD commented Nov 30, 2018

I use this to make sure the same file is always loaded, you can omit the path.resolve if there's no symlinks or other weird directory setup being used.

resolve: {
  alias: {
    'vue$': path.resolve(__dirname, '../node_modules/vue/dist/vue.runtime.esm.js')
    // 'vue$': 'vue/dist/vue.runtime.esm.js'
  }
}

@wfischer42
Copy link

wfischer42 commented Jan 11, 2020

I had the "duplicate import" problem in a monorepo project using Lerna, and it seems to be related to the same problem when using npm link or yarn link. My project has a shared component package that multiple apps import, including an Electron app and a browser extension. It seems that lerna bootstrap uses symlinks, ultimately leading to some bundle confusion, in which Vue is separately imported for the main repo and the dependency, thereby causing the '$listeners and $attrs are readonly' errors.

From what I've found, the common generalized solution seems to be to mark your dependency as an 'external' in whatever build configuration you're using.

I'm using Electron with electron-builder and @vue/cli. I solved this problem by adding this to my vue.config.js:

module.exports = {
  pluginOptions: {
    electronBuilder: {
      // List native deps here if they don't work
      externals: ['my-shared-components'],
    }
  }
}

PixelThin pushed a commit to PixelThin/pixivue that referenced this issue Apr 12, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants