Let's say I have a website with multiple tabs, built with react.js, and each tab contains a large amount of data. Normally webpack bundles react apps into a single bundle, but this would be a waste of resources if you never visit one of the tabs. Is it possible to break it into separate bundles without having to re-bundle react core & react DOM, and then call these extra static modules upon request?
I'm open to different suggestions - webpack, systemjs etc.
First, check your architecture and make sure you actually need this. Considering the typical scenario in which only the "infrastructure" is part of the bundle and all data is downloaded via Ajax as they are needed, it is kind of unlikely (not impossible) that you will end up with a particularly large bundle when you properly take care of optimization.
Back to your question...
Bear in mind that this might not be a walk in the park. From my experience, when you're trying to do something a little off the dotted line, problems start to pop up down the road. You might have problems with server-rendering, redux or whatever.
Anyway, you got 2 options:
1) Using a multi-page app configuration
Webpack will output multiple "chunks" with you configure it to have multiple entry points like this:
module.exports = {
entry: {
p1: "./page1",
p2: "./page2",
p3: "./page3"
},
output: {
filename: "[name].entry.chunk.js"
}
}
You can even have "common chunks". Take a look here.
2) Using code splitting
There's a Webpack loader called bundle-loader that offers a lazy option that allows modules to be actually downloaded as they are needed. I came across this loader when I was reading the React-Router 4 documentation. They even provide an example which doesn't require the router at all, check it here.
Related
In a bigger project, I have a fairly high amount of used third-party libraries, including firebase, redux, etc and some more specific ones (but only used on several pages) like konvaJS, jimp, ....
I just migrated recently to nextJS to speed up the website & maybe allow SSR. However, after migrating, the Lighthouse Speedtest dropped compared to the Pure React Version. The main problem seems to be the shared JS first Load bundles.
After some optimizing, including lazy loading bigger Components with dynamic() & modules with await import(), I managed to compress the shared first load JS bundles by half, but they are still around 400KB which is way too heavy. I guess heavy modules like firebase are included there as well, because it is needed basically everywhere in the App.
I also tried to analyze the dependencies with #next/bundle-analyzer. But the Visualization is not really easy to interpret. Is it true that it also lists modules that are lazy loaded? And in addition, I have some dependency packed multiple times in different bundles. Last but not least, the bundles visualized by the analyzer do not match the names of the build output.
Any help to reduce or understand the process better is well appreciated. I am using current React & Next.js versions.
Edit: This is the composition of the shared JS bundles after build:
Edit2: I am still confused about the output of bundle-analyzer. The module jspdf for instance, is only used in one page / component and lazy loaded. Is it correct that it is still visible in the analyzer? It does not have any impact on the shared JS bundle size.
Edit3: I managed to lazy load my firebase modules (which are crucial for my App), so I saved over 200KB shared JS size. Still hoping to cut down the remaining. Btw, the bundle analyzers output did not really change.
Are you using FontAwesome?
We were able to reduce our "First Load JS shared by all" from
504kb down to 276kb
by removing FontAwesome dependency and downloading the individual .svg icons
directly.
Before
After
so I'm working on migrating my company's app to a micro-frontend approach. We are following the standard described in https://micro-frontends.org/. While under the hood everything is currently using React, we are wrapping things with Web Components so that we will have the freedom and flexibility to be framework-agnostic in the future. We've got a working architecture up and running and so far it is working beautifully. We even created a fancy compatibility layer on top of the Web Component spec which allows us to pass React-like props to the Web Components, including Objects, Arrays, and even Functions. This allows for much better interaction between them.
The main concern we have right now is duplication of libraries. We're a React shop, so even though we have this framework agnostic approach, everything is using React. While this new approach gives us the ability to individually upgrade pieces of our app to a newer React version (finally), we still don't like the idea of so much duplication of the React library.
To put it in perspective, even Gzipped, React/ReactDOM are over 40kb. That's super tiny individually, but scaled up it starts to take up more and more bandwidth. RAM-wise it's less of an issue, about 130kb for those libraries, and given the RAM capacity of most devices now it's not a huge deal.
But, of course, we want things to be as optimized and streamlined as possible. So I'm hoping someone can suggest a way for the micro-frontend apps (the ones wrapped in a Web Component) can get React and other libraries from the parent app.
You should know that the parent app JavaScript is loaded prior to the micro-frontends. Each micro-frontend is loaded via a <script> tag. Lastly, we are NOT using the Shadow DOM at the moment, a tradeoff we made to benefit how we are migrating our existing code into the new micro-frontend architecture.
The core idea is to tell module bundler on how to package your micro-frontends.
Assuming you are using Webpack to bundle your applications, here are the two things that you need to do.
Step 1:
Declare React as an External dependency like in your Webpack config:
externals: {
'react': 'React',
'react-dom': 'ReactDOM'
},
Step 2:
Before you load your parent application's JS, ensure that you are loading React and ReactDOM from CDN or other equivalent place:
<script crossorigin src="https://unpkg.com/react#16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script>
Put these script in your main index.html which is responsible for bootstrapping your entire SPA.
Explanation
When you declare certain package/library as external, Webpack does not include it as part of the bundle. It assumes that the outer environment will make that particular version available as a global variable. In case of React, it uses React and ReactDOM as global variables.
By doing this and including it via CDN, you will be left with exactly one copy of React and ReactDOM. When a user visits the application for the First time, it will be slower but once cached, it should not be a problem
Further, you can extend this idea and also declare them as external for your parent app or parent shell container.
Possible solution is to prepare library using Import Map but as it does not support IE11+ I recommend you using SystemJS?
https://github.com/systemjs/systemjs
especially this one seems to be close to your case:
https://github.com/systemjs/systemjs-examples/tree/master/loading-code/react-hello-world
At html you do:
<script type="systemjs-importmap">
{
"imports": {
"react": "https://cdn.jsdelivr.net/npm/react/umd/react.production.min.js",
"react-dom": "https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.production.min.js"
}
}
</script>
<script src="https://cdn.jsdelivr.net/npm/systemjs/dist/system.min.js"></script>
Then you could import ReactJS, as browser knows where to take it from.
Potentially there is a possibility to build some kind of library that looks into your micro front end's (MFE's) package.json, to get know which library needs to be in use and dynamically create imports object similar to example above.
We need to keep in mind that there is a need to cover versioning check. Mentioned library could potentially store some kind of map which connects dependency with version with place where it's accessible. Imagine that in case mentioned we need to deal with different react version on each MFE :).
Then while loading another MFE library could check if required dependencies has been already included, if some missing, download it, otherwise use what has been already fetched.
Another thing is use webpack 5, with module federation, which I not recommended yet, as it is not stable enough for production, but there is a possibility to start playing with it. Hard part will be covering version check, so most probably need another abstraction layer to deal with it.
https://indepth.dev/webpack-5-module-federation-a-game-changer-in-javascript-architecture/
Hi I need your help trying to figure it out something.
First a little background, I was used to code in Django, it was fast to code and good, but times change and Node is taking over most of new apps, then I move to Express a couple of years ago, however I still miss a lot of the Django functionality, then I start coding a little library to help me with common tasks, then start growing until the current point where you have a core library and plug-able “apps” to do recurrent tasks, like notifications, auth and more, or at least that’s the plan.
Right now an app works something like this:
./controllers (All renders)
./endpoints (Restfull API endpoints)
./models (Static and DB models)
./public (Public files)
./style (Scss styles with bootstrap injected)
./views (Default templates, distributed with the app as example, loaded by default)
…/…/views (Custom views to rewrite the default ones from the app)
On start, JSloth compile everything for you and run the server (hot reload included):
Now, that works fine using an static multipage environment, but I will love to use Angular for that, changes will be needed but I want you guys to lead me in the right direction.
So far I have 2 ideas:
1- Split Routes/Html apps and Restful/Endpoints, then basically use an standard set up on that kind of apps with webpack and AngularSSR.
The big downside is, you can’t give an out the box frontend implementation for apps.
2- Figure it out a way to provide an Angular app for each JSloth app, in this way if you install/copy the auth app you will be provided of user lists, interfaces to update your profile, etc.
I’m thinking that may be a problem talking about performance because in this way you will have a lot of Angular apps, am I wrong?
I need a easy way to allow the final user to share footers and headers at least, maybe even styles or scss variables for colors, in that way it will not look like a huge change.
Do you have any other option? Any better idea?
Thank you so much for the help, this is the repository: https://github.com/chrissmejia/JSloth
Edit 1: Forgot to add the models folder
People mention requirejs together with marionette, backbonejs and the like.
requirejs seems an asset loader -- executing your rules on when to load what.
I know the first 'page' of my single-page-app already needs most of the files. If I don't mind loading all files in one go, can I simply ignore requirejs?
Technically yes. Only dependencies for marionette-backbone are
jQuery v1.8+
Underscore v1.4.4 - 1.6.0
Backbone v1.0.0 - 1.1.2 are preferred
Backbone.Wreqr (Comes automatically with the bundled build)
Backbone.BabySitter(Comes automatically with the bundled build)
Further require.js can manage use code structure in a manner which give your code much resource efficient code at the end. From my point of view for simple application which you need simple set of views,models and collection with manageable amount of code it ok to proceed without require.js.
But if your application have complex logic and higher number of resources it's good to go require.js. Because it not good to send 15+ like individual resource requests server at very beginning of your application load. Require can make any number of your resource in to one server resource. That's the advantage.
What I prefer is one request of all css, one for all js, one for sprite image for graphic if things are big to handle which allow to create fast performing application.
Take you decision looking at the amount of resources of the project. It's not essential have require.js form the beginning of your application development.
I am using backbone.js in a legacy app to rewrite separate pages into individual bits of backbone work.
I am not using any routing and it is not a total single page application.
Only certain pages are individual backbone.js applicaitons.
At the moment I have all my backbone javasript in one file for each page that uses it which is painful to work on.
Would it be wise to use something like requirejs on a page by page basis or is there something better I could do in order to split the page up in development and serve one page in production?
That depends largely on what your existing codebase looks like.
RequireJS is a great tool...if your existing code is set up to support it, or you have a small enough codebase to be able to convert it without breaking everything. However, not all legacy JS code is, especially if it's part of a larger system (I personally ran into this problem with a Backbone project I'm working on). If you can, then by all means, make use of it. The big advantage, as far as I know, with RequireJS is that it doesn't actually fetch and load the Javascript files until you need them. So you can have one RequireJS call that's in all of your pages, and only download what you need, when you need it.
There are other ways, however, to combine your Javascript code at production time, which, again, depends greatly on your setup. Many content management systems include "minify" scripts that handle it automatically for all of your Javascript files. You can also do it "by hand" with Minify, YUI Compressor, or one of the many other minification tools out there. (You can also do it "really by hand", and develop in multiple files and combine them via copy+paste, but that's really more work than is necessary.)
Regardless of how you go about doing it, I highly recommend breaking your projects into multiple files (not only into a file for different projects, but multiple files within the projects, to hold each view and models if they have significant code). It makes it infinitely easier to maintain.