vuejs / vuex

🗃️ Centralized State Management for Vue.js.

Home Page:https://vuex.vuejs.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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

f1729 opened this issue · comments

commented

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.

I have the same problem

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.

commented

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

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).

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.

I have the same problem :( @LinusBorg @ktsn

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

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

commented

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.

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

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

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 😞

commented

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

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',
  // ...
})
commented

Working just:

window.Vue = null

on main.js

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

commented

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

This working for me:

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

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

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

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.

@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.

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

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

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?

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?

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

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)
}

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.

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)
}

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.

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.

@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.

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