-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Reactivity lost on $state from class when said $state is consumed in another project (micro-frontend) #13234
Comments
My guess is that the microfrontends each come with their own Svelte runtime. That means the effects listen to sources being read inside them but never notice the sources from the other runtime, because those set the corresponding variable in runtime A, not B. So the solution is to somehow deduplicate the Svelte runtimes. I'm not sure how to do that though, probably some Vite config Fu required. |
Thanks for dropping by! I tried doing what one would do for React: Import Svelte from a CDN and mark it external in Vite. This of course, requires that the project be compiled and a few other adjustments to the repository I shared. The end result is this: As seen, the class using |
Ok, I think I successfully set the project up for de-duplication of Svelte using unpkg.com. It is the same repository, but in the branch named JP/SvelteExternalized. The project runs fine in serve mode ( Furthermore, even though I think I successfully de-duped Svelte, the problem in reactivity remains. 😢 |
Continuing from here. Even though both instances might be technically importing the same module now, they do not share the reference to the same value because of the isolation between processes on different ports...and thus their reactivity graphs (and everything else!) are separate. |
Hello, @kwangure and thanks for dropping by. It seems that isolation in browsers is based on the context, and since this is all in the same window, same document, there should be no isolation happening. Or am I misunderstanding how isolation works? For example, there's no isolation going on with my module "@mfe/utils". I have verified that the constructor of the class only runs once. Since both MFE's can get a hold of the class instance, this can only mean it is the same instance, therefore the same module, even when "mfe1" is consuming the module in a different port. |
Anyway, by extending the logic, it seems that externalizing the "svelte" module alone is not sufficient to do the trick. At this point I would absolutely loved it if core maintainers could come forward and extern their opinion on what it would take to make this scenario work. |
My uneducated alternatives are, in my mind:
For #2, it would be a requirement to be able to reactively work in any number of MFE's, not just "one or the other", or "home graph + 1". For it to be useful, it should work in an arbitrary number of graphs. That's what I can think about, again, using whatever little knowledge of what's really going on here. Thank you all in advance for the time you lend me reading this. |
Okay. You're right. I assumed that from inspection but I've gone around removing things binary-search style in your repro. Removing
|
Hello again, @kwangure. I hadn't realized that The plug-in For your second point, this is not something I can assert, unfortunately. The condition "satisfactory" in this context can be defined as: Both micro-frontends end up using the same reactive graph. I don't have the skill to assert this condition and is probably the key point where I need help from savvy and experienced developers that know the Svelte code, that know how to assert this condition. What I can say about your second point is that I performed the same process that is done to de-duplicate React and React-DOM. Points 3, 4 and 5 all relate to running the projects in serve mode, which is unimportant as long as the built products cannot function properly, so I guess all those have a pin in them as well. 😭 So, unfortunately and very quickly, I arrived to point 6. SummaryIf people are willing to help with this, what we need is to focus on the built product: Can Vite de-duplicate Svelte when building the micro-frontends? If not, why? Once the reasons are known, I suppose the Svelte core team can decide whether or not a potential fix/change/upgrade/refactor is feasible and whether or not they are willing to pursue. Once the built product works, we can discuss making it work while in serve mode, which probably has a different set of challenges. |
My current thinking is that Vite can't properly handle this during dev mode because it transforms the imports to |
Yes, it would be great if that Vite issue could be implemented for sure for development mode. But that leaves us with the lifecycle_outside_component error when the projects are built and run. This should work, I think, since externalization has been a thing in rollup for a long time. If time permits, can it be explored why the built de-duplicated version throws this error? |
@webJose i was just playing around with something similar (not exactly the same) and i think i kinda found a solution. You should externalise the dependencies also on your main project and rely on import maps. However note that the import maps should not be bundled. <script type="importmap">
{
"imports": {
"svelte": "https://esm.sh/svelte@next?no-bundle",
"svelte/": "https://esm.sh/svelte@next&no-bundle/"
}
}
</script> this is the import map I'm using and with this i'm able to import a component like this import "svelte/internal/disclose-version";
import * as $ from "svelte/internal/client";
var on_click = (_, count) => $.update_prop(count);
var root = $.template(`<h1> </h1> <button> </button>`, 1);
export default function _unknown_($$anchor, $$props) {
let count = $.prop($$props, "count", 7);
var fragment = root();
var h1 = $.first_child(fragment);
var text = $.child(h1);
$.reset(h1);
var button = $.sibling(h1, 2);
button.__click = [on_click, count];
var text_1 = $.child(button);
$.reset(button);
$.template_effect(() => {
$.set_text(text, `Hello ${$$props.name ?? ""}`);
$.set_text(text_1, count());
});
$.append($$anchor, fragment);
}
$.delegate(["click"]); directly in the browser and hydrate an index.html page. Give this a try for your project and let me know. |
Hello, @paoloricciuti and thanks for the note. I'll maybe try a thing or two out, but in all honesty, that looks very scary! Hehe. Even if I could mount a component like that, it is not sustainable. This kind of breakthrough is best consumed by core member teams. Maybe you guys can figure out what needs to be done in Svelte itself so it can be externalized while writing normal code, normal components, normal state stores. |
You can also host your own unbundled version of svelte if that's what scares you but the gist is the same. It needs to be the same module that is imported by both projects. And this is also exactly what you tried here
But with a cdn that supports unbundled libraries. We also opened an issue there to see if they can bundle it correctly in the bundled version. I'll try to see if there's a decent way to externalize svelte but still build it with your main package so that you can use a single instance in your main project. |
Describe the bug
Hello, I describe this in #13224 and in short, a
$state
value doesn't react when it is "foreign" to the project.I made a reproduction repository where one can see that reactivity works within the context of the project that creates the state, while simultaneously not working in the other project.
Reproduction
Example repository
Logs
No response
System Info
Severity
annoyance
The text was updated successfully, but these errors were encountered: