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

[vuex] already installed. Vue.use(Vuex) should be called only once. #731

Closed
f1729 opened this issue Apr 6, 2017 · 33 comments · Fixed by #914 or tghelere/vue-tetris#2
Closed

[vuex] already installed. Vue.use(Vuex) should be called only once. #731

f1729 opened this issue Apr 6, 2017 · 33 comments · Fixed by #914 or tghelere/vue-tetris#2
Assignees

Comments

@f1729
Copy link

f1729 commented Apr 6, 2017

Version

2.0.0

Reproduction link

https://jsfiddle.net/15ww4vog/8/

Steps to reproduce

  1. Load jsfiddle page
  2. Vue init method is executed.(With CDN, Just VUE and Jquery)
  3. Vue init method makes a Ajax call to "https://api.myjson.com/bins/13xym3". and responds a HTML, JS injected to #dinamic-container div.
  4. HTML, JS injected also includes a Vue instance.
  5. creating conflicts "[vuex] already installed. Vue.use(Vuex) should be called only once."

What is expected?

  1. Load page, instantiate VUE.
  2. When a page is injected and includes a VUE instance. load with no conflicts.

What is actually happening?

  1. Load page, instantiate VUE.
  2. When page is injected and includes a VUE instance. a conflict is raised and show the "[vuex] already installed. Vue.use(Vuex) should be called only once." console error.

My parent view is loading Vue instance.
and I'm injecting a Child view that also contains its own Vue instance.

@jericopulvera
Copy link

I have the same problem

@ktsn
Copy link
Member

ktsn commented Apr 10, 2017

It already is answered several days ago in the other issue (#729), and it looks like exactly same situation.
If you really think this is a different problem, please describe why you think so.

@f1729
Copy link
Author

f1729 commented Apr 10, 2017

In this case, the external base files are just only JQuery and VueJS; Vuex arrives after ajax

@ktsn
Copy link
Member

ktsn commented Apr 10, 2017

Vuex automatically call Vue.use(Vuex) if Vue is on window object. And you seems to call Vue.use(Vuex) in your own script all.js. So this happens.

Workaround is just removing Vue.use(Vuex) from your code or checking the existence of window.Vue to clarify you can call Vue.use(Vuex).

@ktsn ktsn closed this as completed Apr 10, 2017
@LinusBorg
Copy link
Member

Well, not really.The point is that window.Vueis a different Vue object. So he has to call Vue.use(Vuex) in his app.

In fact, in this instance, the "previously installed" check in vuex is falsely triggered.

Not sure how to improve this atm though.

@yepesasecas
Copy link

I have the same problem :( @LinusBorg @ktsn

@dlvergara
Copy link

I have troubles with the same window.Vue how can i change properly the install function?

@LinusBorg
Copy link
Member

Are you all using two different versions of Vue on your website? Why?

@f1729
Copy link
Author

f1729 commented Apr 10, 2017

In this case, I don't have to much control. we developed a checkout that can be injected in any merchants page, using the example I show you.

We ask the merchant to call our endpoint and include the html, js to their page.

I have control over the Vue instance that is called through Ajax.

There is a way I can create a scope/namespace? so both Vue instances can live in the same page?? or how can I prevent my instance to instantiate again and use the one already created.

@ktsn
Copy link
Member

ktsn commented Apr 10, 2017

Oh, I understand.
Maybe, we can avoid this false positive by checking commonjs/amd specific variables in Vuex?

@ktsn ktsn reopened this Apr 10, 2017
@LinusBorg
Copy link
Member

Like if (window.Vue && window.Vue === Vue)?

@ktsn
Copy link
Member

ktsn commented Apr 10, 2017

In this situation, we don't want Vuex to use global Vue since it's loaded as ESModule. So I think we can skip implicit registration if there is exports, module or define like:

if (
  typeof window !== 'undefined' && window.Vue
  && typeof exports !== 'object' && typeof module === 'undefined' // commonjs
  && typeof define !== 'function' && !define.amd // amd
) {
  install(window.Vue)
}

But not sure this is right direction. And I noticed this may break apps that load Vue in global but Vuex by any module system 😞

@f1729
Copy link
Author

f1729 commented Apr 10, 2017

Can't install Vuex through the instance with the DIV ID?.
What if before the merchant app had VUEX?

@ktsn
Copy link
Member

ktsn commented Apr 11, 2017

Hmm, I think we only have a dirty solution like below:

// bootstrap.js
// Note that you have to load this code as soon as possible in your entire code

// Retain global Vue instance and remove it from `window` temporally
const GlobalVue = window.Vue
window.Vue = null

// Load Vuex code
require('vuex')

// Restore the global Vue instance
window.Vue = GlobalVue
// main.js (Your project's entry point)
import './bootstrap'

import Vue from 'vue'
// ...

new Vue({
  el: '#app',
  // ...
})

@f1729
Copy link
Author

f1729 commented Apr 11, 2017

Working just:

window.Vue = null

on main.js

@ktsn
Copy link
Member

ktsn commented Apr 11, 2017

But if you do not restore the global Vue, it would affect the merchant app, doesn't it?

@f1729
Copy link
Author

f1729 commented Apr 11, 2017

You're absolutely right, but that way I keep getting the error message.

This working for me:

setTimeout(() => {
    window.Vue = GlobalVue;    
}, 500);

@kinanson
Copy link

kinanson commented Jun 6, 2017

I also get this error message "[vuex] already installed. Vue.use(Vuex) should be called only once."
I want to warp my vue project to plugin.I use vuex to mangage satet.
I new a project to demo what happen.that will easy to understand.

Hello.vue

<template>
  <div class="hello">
    <button @click="addCount">add</button>
    {{count}}
  </div>
</template>

<script>
import { mapActions, mapGetters } from 'vuex'
export default {
  name: 'hello',
  methods: {
    ...mapActions([
      'addCount'
    ])
  },
  computed: {
    ...mapGetters([
      'count'
    ])
  }
}

</script>

stores/index.js

import Vue from 'vue'
import Vuex from 'vuex'

const state = {
  count: 1
}

const actions = {
  addCount({ commit }) {
    commit('addCount', 1)
  }
}

const mutations = {
  addCount(state, num) {
    state.count += num
  }
}

const getters = {
  count: state => state.count
}

Vue.use(Vuex)

export default new Vuex.Store({
  actions,
  state,
  mutations,
  getters,
  strict: process.env.NODE_ENV !== 'production'
})

main.js

import Hello from './components/Hello.vue'
import store from './stores'

const MyPlugin = {
  install(Vue, options) {
    Vue.component(Hello.name, Hello)
    Vue.prototype.$store = store //If i don't use this line.that will occur error can't get getters undefind
  }
}
if (typeof window !== 'undefined' && window.Vue) {
  window.Vue.use(MyPlugin)
}
export default MyPlugin

that is the all.I can success to use this plugin in the my project.But I get error message like below.I feel not comfortable for that.someone can help me how to resolve this issus?

1

@anthonywebster
Copy link

If you are charging through CDN, then you do not need to do Vue.use (Vuex), because it does it automatically.

captura de pantalla de 2017-08-03 12 08 24

In my case I include vue with CDN and vuex as a module and it works for me very well and don't have errors.

captura de pantalla de 2017-08-03 12 08 04

captura de pantalla de 2017-08-03 12 11 13
captura de pantalla de 2017-08-03 12 10 14

@paulopmx
Copy link

paulopmx commented Aug 4, 2017

I got this working by hacking vuex.esm.js

Just commented out

if (typeof window !== 'undefined' && window.Vue) {
  install(window.Vue);
}

Not the cleanest solution but worked out fine. I think the logic is that if it sees window.Vue, it should make itself available to everyone, but I could be wrong. Maybe Vuex can only really run one instance.

@paulopmx
Copy link

paulopmx commented Aug 4, 2017

@posva The solution by @anthonywebster does not solve my issue. That's why I added my own comment and shared a hack. Please note, that I will be running my app inside another web page that may have included a different version vue js via a script tag.

@paulopmx
Copy link

paulopmx commented Aug 9, 2017

Can't we just remove(or add the option) the install part of the code in the .esm bundle? Would that break anything?

@ktsn
Copy link
Member

ktsn commented Aug 9, 2017

It is technically breaking change. I think it would be compromising point to add force option for Vue.use to suppress the warning message.

@paulopmx
Copy link

paulopmx commented Aug 9, 2017

But what about for use cases like ours where we need to install vuex to the esm Vue instance rather than the window.Vue instance, is our only option is always using a fork version?

@ktsn
Copy link
Member

ktsn commented Aug 9, 2017

Well, I'm suggesting the option to solve your use case. When you install the ESM Vue constructor with force option, the warning would not be appeared. I'm still missing something?

@paulopmx
Copy link

paulopmx commented Aug 9, 2017

Sorry not familiar with that, do you have a reference that I can use to try it out?

@ktsn
Copy link
Member

ktsn commented Aug 9, 2017

Ah, I'm talking about a new option 😄
It would look like the following.

Vue.use(Vuex, { force: true })

And we can add a condition here to skip the assertion.

export function install (_Vue, options) {
  if (!options.force && Vue) {
    if (process.env.NODE_ENV !== 'production') {
      console.error(
        '[vuex] already installed. Vue.use(Vuex) should be called only once.'
      )
    }
    return
  }
  Vue = _Vue
  applyMixin(Vue)
}

@paulopmx
Copy link

paulopmx commented Aug 9, 2017

Ah got it, haha, we seem to have been miscommunicating.

Looks like a great option. My only concern is, would my esm Vuex, would still be installed to the window.Vue, instead of the esm Vue installed with my bundle?

Just trying to avoid the possibility that the host page might be using a different version of both Vue and Vuex, and don't want to introduce any conflicts.

I'm not even sure how my app is working even after I just commented out the whole install script.

@ghost
Copy link

ghost commented Aug 17, 2017

Would it be possible to check if Vuex is already installed so we can only use it if it isn't? Something like:

if (!Vue.vuex_installed_or_something) {
  Vue.use(Vuex)
}

edit to clarify, something along these lines:

export function install (Vue) {
  if (Vue._vuex_installed) {
    if (process.env.NODE_ENV !== 'production') {
      console.error(
        '[vuex] already installed. Vue.use(Vuex) should be called only once.'
      )
    }
    return
  }
  Vue._vuex_installed = true
  applyMixin(Vue)
}

Then we could check:

if (!Vue._vuex_installed) {
  Vue.use(Vuex)
}

@ktsn
Copy link
Member

ktsn commented Aug 18, 2017

Unfortunately, it would not solve the problem.
The auto Vue.use(Vuex) is called before the user calls their own Vue.use(Vuex) because the former is immediately executed when Vuex is imported.

@ghost
Copy link

ghost commented Aug 18, 2017

True, it wouldn't directly solve the problem, but it would let developers be able to check if Vuex is already installed so they can avoid doing it a second time.

However, what about having Vuex do nothing if it's already installed?

Something like changing this to:

export function install (_Vue) {
  if (!_Vue._vuex_installed) {
    applyMixin(_Vue);
    _Vue._vuex_installed = true
  }
  Vue = _Vue
}

That shouldn't have any backwards compatibility issues, and it would allow third party libraries to include Vuex without having to know/check whether or not the host app has included it.

@paulopmx
Copy link

@thatbignerdguy looks like a great idea.

But I really just need an option NOT to install Vuex at all to the global window object and just make it available just for my bundle. My widget is installed on other peoples website, and installing it on the window object could wreak havok on that site's own javascript code.

@ktsn
Copy link
Member

ktsn commented Aug 18, 2017

I came up with the solution of this problem. Already made a PR #914

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
9 participants