-
-
Notifications
You must be signed in to change notification settings - Fork 6
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
Vuec causing component props to be bound incorrectly. #5
Comments
Hey, thank you very much for reporting and investigating this problem! I'm glad to see you're still using Vuec! |
I'm looking at this in more depth this morning. I tried the service injection as opposed to the parameterized hook injection. Same behaviour, though removing the Although, if parameterized injection doesn't allow for name mangling, and it has a strange interaction with Vue's lifecycle initialisation, it might just be prudent to completely remove it and make the services object the only way. Addition 1: If you add another hook to the mixin call and inspect
Go a step further and remove the
Addition 2: This is shipped as a plugin, which encapsulates a global mixin ( |
There's some really odd stuff going on, because a simple test that I'd expect to fail: it('Should bind on the correct instances.', function () {
var uuid = 0;
new Vue({
el: '#app',
template: '<div id="app2"></div>',
mounted: function () {
uuid = this._uid;
}
});
new Vue({
el: '#app2',
mounted: function () {
assert.notEqual(this._uid, uuid);
}
});
}); which actually passes, meaning that inside their hooks they have the correct ID, but indeed as you mentioned adding a "created" hook inside the mixing will cause it to only ever see uid 0. I'll have to look into this for now, could you create a separate project demonstrating the issue? I'm currently nearing my finals and I'm afraid that I can't spare as much time to set one up as I'd like |
Oops, that close button is easy to click. Sorry. Here's a vue-cli project that displays the problem: Check out the console. Methods and computed properties are bound correctly, as are calls to |
I don't think it's related to properties itself (though they are a side effect of the problem), but rather that for some reason the bound methods only get bound to the first instance that gets bound (so that all hooks get called in the context of the first instance and thus it appears the second instance has the properties of the first). I'll have to dig into the source code of VueJS again to see how they handle the binding of their methods. |
Haha, yeah I was digging around in there too. Specifically these three files: https://github.com/vuejs/vue/blob/dev/src/core/instance/init.js https://github.com/vuejs/vue/blob/dev/src/core/global-api/use.js https://github.com/vuejs/vue/blob/dev/src/core/global-api/mixin.js It's not immediately apparent though. And we know Vue does some magic here and there, proxy objects and performance references, etc. In the interim, I've taken your library as inspiration and have written a simplified solution just to unblock my team. It goes the |
This commit is an attempt to identify and fix an issue regarding the bound instance
Hey, sorry for the delay; I've been crazy busy with finishing my internship and signing a contract with another company in between my exams and all, my apologies for the radio silence. Could you check if 79d957e solves anything for you? |
Congratulations! I will not be able to test this until I return from my vacation in three weeks. I write to you from the airport 😂 Til then! |
I will try to confirm and fix the bug further myself then, thank you for the assistance! Have a great vacation |
@Daekano can you check the patch I wrote for you? |
Hi @dealloc, thanks for this awesome library. I ran into the same problem as @Daekano. I tested The problem is, as you stated in the this thread
The cause of the problem is this line in the file src/Vue.js: const hook = hooks[hooks.length - 1].bind($vm); I found that the Now, for the test you mentioned: it('Should bind on the correct instances.', function () {
var uuid = 0;
new Vue({
el: '#app',
template: '<div id="app2"></div>',
mounted: function () {
uuid = this._uid;
}
});
new Vue({
el: '#app2',
mounted: function () {
assert.notEqual(this._uid, uuid);
}
});
}); This does not cover the problematic scenario. The problem arises only when multiple instances of the same component definition are mounted (either simultaneously or in succession). I have sent you PR to fix this issue. This PR
I would be glad if @Daekano could verify this as well. |
Fix #5: lifecycle hooks must be called on correct instance
@Daekano if you could test if this solves (some of) your problems, that'd be great :) |
@dealloc Okay I'll try to reproduce the issue and apply the fix. I decided to build our own IOC container to remove the dependency since it's not all that complicated anyways, so my problem environment has since disappeared. |
That'd be perfect. And you can always use the Vuec container (it's in a separate file and has no dependencies either ;) ) |
@dealloc This is fixed! Again, apologies for the delay. I'm quite busy these days and haven't formed any open source habits, so it tends to escape my mind. |
No problem, thanks for checking it! Thanks a lot @kashiif for the patch! |
@dealloc Any plan for releasing this fix? |
@kashiif of course, my bad! I've been sick this week and haven't really given it much thought. Expect a new release within a couple hours! |
Belated thanks for this release. Works like a charm. |
Hey guys. I found this project and this issue because I'm building a TypeScript-based DI container with an adapter to Vue.js and ran into a similar problem. I justed wanted to let you know that the proposed solution here does not fully/cleanly resolve this issue. What's happening here is no Vue.js magic, it's just plain JavaScript mechanics. Let me explain: the hooks you pull from $options are located in the prototype of the options, not in the options instance of the component itself. The first time you patch them works OK, but the second time you're patching the already patched hook again. So the second time a component of a type is created, its hook points to the previously patched hook which points to the original one. And so on. Since in your previous code all patched hooks had been bound to the respective component at creation time, this simply meant that the most inner call (the first hook created) won with regards to the used context. That's why you were always only seeing the first component instance.
That's also the reason your current solution is questionable: technically it works because you remove the explicit binding and pass on the outer-most component down the chain. But you still increase the involved call stack by 1 every time a component is created, which surely is no good for performance and will even crash at some point (max call stack depth). Replacing the hook can be a one-time thing (per component type). |
Btw. I haven't looked at your code in much detail, but it seems to me that the way it's implemented now it will also have lifetime issues for the resolved dependencies, i.e. if you ever intend to support transient instances I'm pretty sure they won't resolve correctly with the current approach of patching the hooks. |
Hello again!
I believe I have found another edge case. I am able to reproduce the problem fairly easily, see further below for code.
I have a component. That component has props. I create two instances of this component in the template, each with different values for the same props. The component renders the expected prop values, and the Vue DevTools also display the correct prop values.
However, any reference to these props inside of the lifecycle hooks only returns the values of the props passed to the first component instantiated.
I have narrowed this down to the Vue Container. If I change line 6 of src/Vue.js to the following:
hooks[hooks.length - 1] = hooks[hooks.length - 1]
, this unexpected behavior goes away. So it seems thatcontainer.prepare
is somehow binding to the wrong Vue instance, though this is the part that I can't narrow down. It all seems to be correct. Maybe there is some other type of interaction that I'm not able to see right away.I also noticed that component methods were getting the wrong scope resolution (though computed properties do not). Maybe this indicates an unexpected interaction with lifecycle initialisation, since there is quite a bit of magic done by Vue within that process.
Inspecting
this
during theVue.mixin
call results in all distinct components, so it seems we can rule that out...The text was updated successfully, but these errors were encountered: