[vue-compat] globalProperty with function as value is always bound to instance proxy
Version
3.2.4
Reproduction link
https://github.com/Fiona-SUN/vue3-minimal-reproduction
Steps to reproduce
- update vue to 3.2.4, install @vue/compat of the same version
- install UI Components Vant 3.2.0
- Global Register Toast (eg: https://youzan.github.io/vant/v3/#/en-US/quickstart#cdn)
- use this.$toast.loading in .vue file
What is expected?
The project should run on Vue 3 with Vant.
What is actually happening?
Uncaught(in promise) TypeError: this.$toast.loading is not a function.project broken
PublicInstanceProxyHandlers function use Function.prototype.bind() cause Toast lost some properties. if there have any other methos to resolve the problem instead of Function.prototype.bind().
I felt like looking into this even though the repo wasn't really minimal with the added dependency.
It's an edge-case issue with the compat behaviour for app.config.globalProperties
:
- in plain Vue 3, whatever we provide down, we leave untouched
- in
@vue/compat
, if the value of a globalProperty is a function, we bind it to the instance proxy, in order to mimic the bound behavior ofVue.prototype.someGlobalProperty = function () { ... }
The repo is running in compat MODE: 3
- maybe we should disable this behaviour then? or provide a separate feature flag?
We should discuss it
Thanks for helping me to explain this problem clearer.
I think that globalProperty with function as value should be a common approach in dependency libraries.such as ElementUI
,Vant
and so on.
Now most of us would like to migrate our project from Vue2 to Vue3(Migration Build),but the dependency package can not supported fully which makes me confused.Losts of code need to be rewrited.I don't think this is a good performance in version migration.
(Forgive me for my poor English expression skills~
I think this behavior should be fixed, because the official versions of vue2 and vue3 do not have this problem, which will lead to differences in the perception of migrated users
It is a bug, but it's an edge case, really. it only affects you if you pass a function that also has additional properties on it (which is not something people do quite often).
A plain function without additional properties on it will work just fine.
@LinusBorg Thanks for you helping!
I agree with this. Indeedly, a function has additional properties should be function extension. - but it is undeniable that convenient methods are more popular with people.
I just ran into this same issue and it took me a couple hours to figure out that this is an unsupported edge case; I only figured it out after putting it together from this and some other GitHub issues.
I want to suggest updating the documentation to include a reference to this unsupported edge case, especially since the example in the Plugins documentation gives an example of passing data to the $translate function. While it does use Provide/Inject to finish the example, it's not explained that this is necessary when using a function that passes a variable.
I would also suggest mentioning this in the Api documentation for globalProperties as well:
For those who run into this same situation, I have created a repository to show the scope/combinations of globalProperties and Provide/Inject and where they do and don't work when using the Composition API:
I would be happy to submit pull requests to both of the documentation pages I linked to above, adding notes at the bottom about the scope and usage limitations of globalProperties. Let me know if that's something you'd like for me to do, and if you have any suggestions/requirements for how I word/structure anything, etc.
Until #4873 is merged, a quick workaround is to convert the function property into an object.
For Vant's Toast
plugin that sets up a global $toast
property (as in the @Fiona-SUN's case here):
app.use(Toast)
const toast = app.config.globalProperties.$toast
app.config.globalProperties.$toast = { ...toast } 👈
For Axios:
const axiosInstance = axios.create({ withCredentials: true })
app.config.globalProperties.$axios = { ...axiosInstance } 👈