Vite and Module Federation Makes Micro-Frontends EASY!

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
A new plug-in is out for Vite and Rollup applications that allows them to support Module Federation. Which is super cool because it allows them to share code dynamically between two deployed applications. So what are we talking about here? Well, there's two ways to share code between applications, and mostly I'm talking about maybe shared stories or shared components. Imagine that you have two applications like the home page and the product detail page, and they want to be able to share a header between those two applications. Now, with build time sharing, which is what we're kind of all familiar with, you have a library where you've got the header in it and then both of those applications import that library and in order to update that header, then you would need to deploy both of those applications. That's build time sharing. Now, runtime sharing allows for a different model. The home page, for example, can have a Header in it. It could then expose that header via Module Federation and then the product detail page can now consume that header dynamically at runtime and that means that the home page, when it publishes and puts out a new version of the Header, that product detail page will get it dynamically without any redeployment. And this is a really big architectural win in the kind of Fortune 500 space. You've got those big companies that have multiple teams managing multiple applications, and you want to be able to have a consistent user experience, but you want to be able to have shared components. So in this case, when you want the header to be updated, it's really great if all of those teams can get the header simultaneously and runtime sharing allows for that. So what I'm gonna do this video is upgrade your skills to the architecture level by showing you how to do Module Federation between two Vite applications. Then I’m gonna show you how to do shared stores between two applications. Pretty cool stuff. Then we're going to bring in Webpack and see if it's compatible because Webpack is where all this module federation started and you want to make sure that your older applications that have Model Federation are now compatible with your new vite or rollup applications that have module federation. And finally, I'll show you how to do this and a monorepo so that you can get a sense of the advantages and disadvantages of a build timesharing system versus a runtime sharing system. This is very cool stuff and I can't wait to bring it to you. Let's get right into it. So let's start off with our plug-in federation. This is a plug in for Vite. It uses the classic micro front end image here to show you the advantages of a micro frontend system. And then it talks about how to install it and how to configure it. So really good documentation here. It also has some really great examples. If you scroll down here into the example projects that show you kind of all of the different configurations and combinations of using different frameworks, using framework, different frameworks together. I mean, potentially you can use using this system, you can bring in dynamically a Vue component into a React component or vice versa. It's really cool stuff. So what we're going to do in this one is mainly show off, React, just React talking to other React applications, but you got a whole bunch of different examples that you can play with here. All right. I'm going to go into the terminal and set up my project. I'll create a project called Vite Mod Fed and then into there, and then I'll bring up VS code. So to start off with, we need two applications because we want to share code between those two applications. One of those applications can be named remote because the remote app is the one that shares the component out. And we're going to have another application called Host, and that's going to be the one that consumes that component. Now, an interesting little side note here is that in module federation, an application can be both a host and a remote. We're not going to do that here, but that is one architectural option for you. All right. Let's start off with our remote application. So I'm going to use pnpm create vite. Name it remote and use the React template. All right, now in remote. I'm going to go install the plug in for Federation. I'll do that. And development mode. Now, let's go take a look at what we've got here. Got a basic app, src/app, all that good stuff. We've got a package.json. Really nothing exciting in here. It's got all the regular React stuff, but it does have this new plugin federation that new just added. Awesome. Now, one of the important aspects of module federation is you're going to be using a URL to point to where the code is in the remote application and you want that URL to be fixed effectively. So in this case, every time we launch this application, we're going to get a different port number, maybe 5176 or 5177 or 5178. That's not great. What we really want to do is fix the port number down so that when the host application references that remote application, it has a consistent port number. So we're going to go make some changes to the package scripts here to allow for that. So all we're doing here is we're saying that the port number is 5001, and if you don't get 5001, then then don't do anything at all. So there you go. That's what strict port does. So now we need a component to share with our host application, once we create that. Now I'm just going to use a custom button here because this is more of a tech demo. I just want to show how these connections are made. I'm not trying to actually show off like a real world scenario. That's up to you. Okay, so let's go create our button now into this button that jsx while I'm going to paste in an implementation, all of this code is available to you for free on GitHub and a link in the description right down below. So you don't really have to type it in as we go. And all this button is doing is it just has a little state on it is a counter goes up by one. Nothing too exciting. It's important that it does have a hook in it though, because you can get into a scenario with module federation where it looks like it's working, but when you bring in a hook, it blows up. So it's important to have in your test some local state. Any kind of hook will do. Just to make sure that the React is configured properly. What's going to happen if it isn't configured properly, is that you'll get a error in the console about how there are multiple react instances loaded. And that's because both of the applications are competing for which react instances is loaded at runtime. All right. We save that out. Okay. And just to check out what we've got, let's go over to app that app.jsx and bring in our button. Now, clean this up a little bit. Oh, instantiate our button. But for convenience sake, I do want to be able to identify which application we're looking at in the browser. So I'm going to put in remote application up here and let's give it a try. So we got the counter that is part of the application itself that's implemented here inside of App. So that's the local app counter. And then this is the button counter says Click me and then it's got that 12 in it. Now what we want to do is we want to create a host application and share this button component with it. So let's go build that. So I’m going to create another terminal and then into there I'm going to do the same pnpm create, but this time I'm going to call it host. Then I go into host and again I'll go and add our plugin in. So you need this plugin on both sides because of remote needs to use that plug in to expose that button, which we haven't done yet. And then the host application needs the plugin to go and then reference that remote. So it's going to add it here. So the only thing I'm going to do in host at the moment is I'm going to go and make some changes to the app.jsx, just to kind of trim it down the way that we did before. Okay, Looks pretty good. Now I'm going to go run that. Now, let's notice that host in this case is on port 5176, not a problem. It doesn't matter where host is. All that matters is where that remote is because remote is going to have the manifest of all of the components that are sharing. And it has to be at a specific URL. All right. Let's see how this looks cool. We've got our host application sitting side by side with our remote application. So now we want to go and share the button out of the remote application. So let's go configure the remote for sharing. All right. First thing I need to do is bring in our plug-in, the federation plug in, and then we need to configure that federation plug in. So let's start at the top here. We've got our name in this case, that's remote app. You can name whatever you want. Then we have the file name for the manifest of all of the modules that are exposed by this application. And the name of that file is going to be remoteEntry. That's pretty standard in the Module Federation world. And then we're going to say, what are we exposing? And in this case, we're going to be exposing button and we need to give it the path to that file. So in this case, src/button, then we need to specify which libraries going to share. And now you need to expose any libraries that your modules depend on. In this case, button depends on react and react-dom. Therefore, we're sharing, react and react-dom. And then finally, I'm gonna make some small tweaks to the build configuration. So now we need to go over into our remote terminal and this is where things start to diverge a little bit from a Webpack style demo. To get this to work with. Vite, you actually need to build the application and then serve it. So let's do that. So we do yarn build and then have yarn build a successful we do a yarn serve, looks like yarn build is successful and looks like yarn serve is successful. Now one last trick that we should do is take a look to see if we have a remote entry. So go and create a new tab and I’m going to look for where that is supposed to go. And that's supposed to go in assets/remoteEntry.js. Now remoteEntry is a manifest. It tells the consuming application in this case. Host Here's what I've got and in this case has got the libraries react and react-dom as shared libraries and then it's also got our button that has our button implementation in it. So if you don't see this file, then nothing is going to work at this point. And if you just run pnpm dev, it won't generate this file. So that's why it's important to do that build, and then that serve because this build that creates the remoteEntry file. And I also want to emphasize the location of this file. This is going into the assets directory and that's because this remoteEntry file and all of the bundles that are references are just JavaScript files and they should be treated and deployed like assets. One of the questions I get most often about module federation is what happens to the sharing between two applications if the application that's sharing that component that I rely on goes down. Well, nothing, because what you should do is you should go and deploy your assets, in this case, all your CSS, your JavaScript, your images, all of it should be deployed to a static asset store, and that includes all the these modules. So yes, your application could go down, but that's just the server side of your application. The static assets should never go down because those should always be deployed on something like S3, which is never going to go down. And if S3 does go down, well, it's a holiday for everybody, so don't worry about that. All right. Sorry about the little excursion there. I just get asked that question so often that I kind of want to address it right here. Okay, let's go and configure host so we can consume this remoteEntry. Then I'll go into host Vite config, I'll bring in that federation plugin and then I'll define federation. So in this case, this is going to be our app and we are going to have a remote and that remote is remoteApp and then we give it that remoteEntry as well. Now this remoteApp key is what you use in your import. So when you want to import button you say remoteApp slash button. You can use any name for that you want, but it's often good to keep it with what it is. So in this case, it's remote app. And then of course any libraries that we want to share with our remote. So in this case we want to share, react and react-dom to make sure that everybody's using the same react and react-dom And we don't end up downloading multiple copies because that would just be bad for both performance and also for the stability of the app. So this allows for module federation to arbitrate between the host and the remote and say, Hey, I've got React. Great, you've got React great. Cool. There it is. Okay, cool. And then finally, some build tweaks and looks like we're good on the configuration side. Let's go now and try and import that button. So important, we're going to use a standard import statement. We're going to say that we have a button on a remote app slash button and then we're just going to use it. Okay, let's try this again. So I'm going to hit Ctrl-C. And now in host, I'm going to build, but I'm going to do preview instead of serve, because if we look at the package.json, we can see that we have just the build and preview. I haven't actually made any changes to this because we didn't need to go make that port strict. So let's go and do a build and then a preview. All right, that's putting me now on port 4173. Let's go take a look. Here we go. Port 4173. We have our host application and we have exactly the same component share between those two things. And this is actually really important to understand as well. Notice that I'm over here on 5001. My Click Me is now at seven. Let's say my account is at four, or it's entirely different from the host application where Click Me is now at 17 and count is it eight. This is another common question I get about module federation, which is why am I seeing different state between two deployed applications when you're using the same component between those applications? And the answer is because it's sharing the code and not the state you wanted to share the state, you'd have to use some kind of state sharing approach like, say, Firebase or something like that. There's no magic here. All we're doing is just sharing code. So let's actually see the code change between these two applications dynamically. So what we want to do is we want to be able to redeploy remote and see the changes in the host without redeploying the host application. So let's see if that actually works. So to do that, I'm going to change the CSS on button, so I'll go back over to our remote and into src. I'll create a new file called Button dot CSS us and I'll put it in our CSS. Now we've got a shared button class. Just so happens that the class name here is Shared Button. Who would have thunk it? So now I'm going to bring in our CSS. So we just defined and then rebuild and redeploy just the remote. All right, let's take a look. So if I refresh 5001, then we get this kind of dark black click me looks pretty good. And we should see if I refresh the host application over here. We should see that same dark black button. And we do. Cool. How cool is that? I did not go and redeploy 4173. Right. It automatically got that change between those two applications because host application is looking dynamically at the code from remote application. Not state just code, but still wicked cool. So what module federation is most often used for is micro front end or micro-fes. And one of the things you want to do with a micro front end is share a state between the host application and the micro frontend, for example, the user ID. Now the way that I strongly recommend doing that is with an atomic state manager, either Recoil or Jotai. Both of those work well. I prefer Jotai, so we're going to use Jotai. So what are we going to do? We're going to have a shared state store here that's going to hold the value for click me and then we're going to attach to that in the application itself so that when I click here, count up to eight and there'll be shared between those two. It's pretty rudimentary, but it will show sharing between the host application, in this case remote and its own button. And then over here we'll do the same thing in host. So we'll see how to share state, and subscribe to shared state. So first we need to create some shared state and we need to install Jotai. Let's go do that. Then over in remote I'm going to go and add Jotai and then I'm going to create our store. I'll call it store.js. and I'll paste in our implementation. We'll take a look at it. So we are going to define an atom called count, and then we will have a custom hook called use count. We'll return a use atom that's going to give us back a value for it as well as a setter in a tuple pretty much like useState. So that will be the default output of this store, will be this useCount and that we can use for our count. All right, let's go bring this in a button and then all we need to do is just replace that useState with useCount and that all about do it now. Let's go over here to our app and also bring that in and replace this useState with that useCount. And so theoretically, when I click on either of those buttons, because the state is shared between those two, it will update in both places. So let's go to try now, of course, to see any changes. I want to do that, build and serve and if I click, yeah, there it is, I click in either place now that button goes up. But here I don't get that same interaction. It doesn't fail, which is good, but I don't see that same interaction. So what do I need to do? I need to share that store between the remote application and the host application. So let's go do that. So we'll go into my Vite config again. Now I'm going to go and expose that store by just changing button to store here and now I need specify that we want to share Jotai because Jotai is essential to the implementation of store. All right, let's build in the play again. Now let's go over in the host and try and use that store. Same kind of deal here. We're going to bring in useStore from the store and use it. Let's see. All right. Again, I want to do that, build and preview. Okay. Looks happy. Let's go back over here or hit refresh. Perfect! So imagine what you can do with this. You can have your user ID, you can have your cart, you can have all this all on shared pieces of atomic state. Now, why do I recommend using something like Jotai shared atoms for sharing state between a host application and its micro frontends as opposed to something is, say, more conventional like a Redux or Zustand? Well, what are we trying to accomplish with shared state? We're trying to accomplish a coupling between the host application and the microphone ends. The more state that you share between those two things, the tighter that coupling becomes. If you have something just like the user ID and then the contents of the cart, that's a pretty small surface area to exchange between those two. The great thing about atomic state management like this is it to extend the surface area that you want to share between those two applications, you need to go and add new atoms. Whereas with Redux or Zustand, you can just extend the size of the store. So there's no friction to adding new data that's sharing between the application and the micro-frontend. And that's not great because the more data that you share, the tighter the coupling becomes. Now, Module Federation came out with Webpack five, I guess almost two years ago now. So are Webpack five applications compatible with the module federation being done here with Rollup and Vite? Let's see! To test that, I'm going to go create a new Webpack five application that imports this same button. We'll see how it works. Let's create yet another terminal and into there I'm going to do my own npx create-mf-app, MF meaning Module Federation, and we'll call this wp-host for Webpack host We'll say that as an application, we'll put it on 8080 and we'll use React and we use JavaScript. Keep the standard CSS and we'll go into that directory. Now let's go take a look at we got we've got a source directory that has an app.jsx file. Cool. And we've got our package JSON. Now we do need to make a few upgrades here because this one has a react of 17.0 and the applications have 18.2. Definitely want to upgrade that. And the Webpack CLI also needs an upgrade to version 4.10. Now over in our app.jsx file, we're using the old school reactDom renderer, so it's going upgrade that to the create root as well as get the react dom now from react dom client. Cool and by default this is a light mode application and since everybody is telling me don't do light mode, we'll go make this a dark mode application by just bringing in the CSS from the Vite side. Okay. I think we're good. Let's give it a try. So now I'll do my pnpm install and I'll start and here we go. Localhost 8080. Well that's beautiful. Okay, so now let's go see if we can bring in that button. So over and over and config we are going to go and specify that we have our remote. This is in the model generation plug in. We're going to say that we have a remote. It's going to be the same exact remote as we had before. Localhost 5001. Cool. And let's go see if we can bring it into our app.jsx, bring in our button and let's just do a console.log now to see what we actually have. So I'm going to go and do a console.log and then I'll restart and that's not good. So what I think is happening here is that Vite is working on Ecmascript modules, so we need to specify a different way of building out our application here in Webpack using Ecmascript modules. So let's bring up the Webpack config and we'll specify here that we are using a module. Let's see if that helps. Oh, okay. So now we're getting library type modules only allowed when we have experiment output module enabled. Okay, let's go do that. So let's say that we want to target EcmaScript 2020 and then also specify our experiment module that it complained about. Let's give it a try. Okay, so these didn't blow up, but it's still not working. So to complete the migration of modules, we need to have an index that has a script of type module in it. So let's go and create a new index. Now we're going to bring in all of our plug in files via the type module. Cool. So now let's go back over to our Webpack config. We'll change this to the index.ejs and then we'll say that we don't want to inject anything and let's give it a try. Okay? It looks like it's not blowing up. That's a good sign. Let's bring up our console. And now that we see that, Oh, we got a button. Awesome. We have a module button there. Cool. So now let's go and try that out. Let's go over to our app.jsx. So this is a little different. We're going to bring in a button module and then from that we are going to dereference button by saying that we want default from button. So that should give us button. Let's see how we do. All right. It looks like this time we actually have our component function, so let's give it a try. So we’ll go back down in our app, instantiate button, and there we go. Now we have our button all the way from our meet roll up applications all the way into our Webpack five application. So they are compatible between the Module Federation from Vite and Rollup and Webpack. Now the last thing I want to do is show you a monorepo version of this. I'm going to bring the monorepo code into our project so when they get have repo linked in the description right down below you'll see a new folder called monorepo-version. This is a turborepo monorepo that has two applications in it, app one and app two. I didn't name them host and remote because those concepts do not make sense in this case. You just have two applications. You're not actually doing that kind of runtime sharing. There's no remote, there is no host. They're both sharing the components that are in UI. So in this case button, the same button that we had before. They're also sharing a shared store project. Now each one of these is a conventional NPM project. In the case of this store package, the name is Store, and then over here in app one in the package JSON, we reference store by saying that we want the store out of the workspace. That's how a monorepo works. We also get the UI from the workspace and then down here in the tsx file we bring in use count from our store, we bring in button from our UI, and it's as simple as that. It is the buildtime sharing that we've all known and loved for a long time. The reason that I'm including it in this project is so that you can have a look at the differences between buildtime sharing and runtime sharing and think about the pros and the cons of each for buildtime sharing the pros are that TypeScript works really easily, unit testing works really easily, and when you build and deploy, the application is 100% coherent. It is a single package. There are no external runtime dependencies. The big con is that you don't get that runtime sharing that you do on the runtime side. On the runtime sharing side, the pro is that you have runtime sharing. You deploy one application once and then anything that relies on remote from that application get updated seamlessly. The cons of runtime sharing are pretty significant. TypeScript sharing becomes more problematic because when you go from TypeScript to JavaScript, you lose the typing. Unit testing can be a problem because those modules aren’t available at the time of the unit test. And then there's the big one, which is the runtime risk. Another application that gives you a component that you rely on at runtime could make changes to that component that break your application, and you wouldn't know about it until you got a runtime issue as opposed to a build time issue with build time sharing. Well I hope this video helps you understand more about how module penetration works and what it allows you to do now with Vite and Rollup applications as well, which is super cool, but I hope it helps you understand also the pros and the cons of each one of these architectures that you can make a choice for your application, which you're going to use. And in the meantime, of course, if you like this video, hit that like button. And if you really like the video, hit the subscribe button and click on that bell and be notified the next time a new blue collar coder comes out.
Info
Channel: Jack Herrington
Views: 68,339
Rating: undefined out of 5
Keywords: vite, module federation, micro frontend, micro frontened architecture, state sharing, vite module federation, module federation vite, module federation react, webpack, webpack 5, federated modules, micro-fe, javascript, micro frontends, federated modules in webpack 5, webpack 5 tutorial, module federation crash course, webpack-5, module-federation, module federation tutorial, module-federation course, learn module federation, share code with module federation
Id: t-nchkL9yIg
Channel Id: undefined
Length: 27min 36sec (1656 seconds)
Published: Mon Apr 03 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.