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
Related
I am just working through a React course and the current topic is lazy loading.
I was wondering why lazy loading is not the default and taken care of by React without forcing the developer to write repetitive code?
Example:
In the course, we want to load the Posts component lazily, because in this component we only render it when on a certain route. Therefore he replaces
import Posts from './containers/posts'
with
const Posts = React.lazy(() => import('./containers/posts'))
and where it is used he replaces
<Route path='/posts' component={Posts}>
with
<Route
path='/posts'
render={() => (
<Suspense>
<Posts>
</Suspense)}
>
so basically just wrapping the component we want to lazyload in a certain React component.
React is not handling the lazy loading by itself but relies on the functionality of the underlying bundler (Webpack). In particular, the bundler converts the import() statements (which is the proposal for the dynamic import) to something which could be processed by the majority of the browsers. Thus, to enforce the underlying building process to load a specific module lazy you also have to use import().
In general, splitting into multiple chunks (that's what is happening on build when lazy loading is used) might be good (e.g. for mobile users, as mentioned by #Prashant Adhikari) but also leads to additional delays while using the page because the files have to be transferred over the network one by one first. Thus, it's also not an option to have lazy loading everywhere. In fact, this issue might disappear in the future (esp. with some "intelligent" preload mechanism in HTTP/2) but for the majority of applications the best practice over the last years seems to be generating a fat JS file for application-related scripts plus a vendor.js for the dependencies.
However, introduction of lazy loading might be reasonable in order to minimize the page loading time. Esp. for bigger applications (like Stack Overflow) it makes sense to preload the modules require to depict the primary content (e.g. Questions) and lazy load the less frequent pages (e.g. User settings).
React lazy : - It is now fully integrated into core react library itself.
Firstly, bundling involves aligning our code components in progression and putting them in one javascript chunk that it passes to the browser; but as our application grows, we notice that bundle gets very cumbersome in size. This can quickly make using your application very hard and especially slow. With Code splitting, the bundle can be split to smaller chunks where the most important chunk can be loaded first and then every other secondary one lazily loaded.
Also, while building applications we know that as a best practise consideration should be made for users using mobile internet data and others with really slow internet connections. We the developers should always be able to control the user experience even during a suspense period when resources are being loaded to the DOM.
With lazy loading you can customise and load lazily the component which are not needed for the current screen.
The question about having lazy loading natively might be answered by some more experienced guy or you can raise an issue on gitHub.
It's a relatively new feature released in the last year with React 16.6.
If there was a way to enable lazy loading for all existing projects without rewriting code, they would have included it. The fact they did not means it isn't compatible with all existing patterns.
When, we use create-react-app while creating a new react application. It creates multiple files. Out of which index.html is the rendered html application, where multiple jsx elements are placed depending upon the react application App.jsx
I was curious on what is the best way to import google fonts, bootstrap, jquery and other different external plugins?
As I have researched there are two ways of importing external modules. For eg. if we consider bootstrap that is to be imported from cdn: https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js
Then, one way to import is place it in public/index.html:
# Rest of the index.html code
<div id="root"></div>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
# Rest of the index.html code
Other way is to install bootstrap using npm i bootstrap#4.3.1 --save, and then put it as import in src/index.js .
The above thing can be applied to multiple places. Say, we have to import popper.min.js or jquery.js or import css fonts. Which is better? Placing it in index.html or inside index.js?
Also, what is the major difference between placing it at the two different places?
Both approaches will have the similar effect of making that library available to your application.
The HTML approach causes your dependencies to be fetched and executed at run-time by the browser. Much like you'd include imgs on a page, you can include scripts on a page with the source of the script either inline or from another URI. So, let's say you create an HTML page, put a number of script tags around it, and then hit that page in your browser. Your browser will scan the HTML page, identify all script tags and start downloading them. It will also remember in which order the script tags were found, and it will parse and execute them in that order (unless you use async and defer attributes on them).
Ok, so, browser sees HTML page consisting of various elements, including some script tags, download them (probably in parallel), and execute them (sequentially). Now, these scripts may or may not know anything about each other on the page.
Now, let's go to the realm of React and other rich Javascript apps, which depend on various Javascript libraries.
When you do Javascript Module Bundling i.e. import { something } from "package", this will get picked up at compile time by your Javascript compiler, e.g. React via create-react-app when you do npm run build, or Angular's equivalent script, or other compilers such as webpack, etc. These compilers will scan not just a single file, but rather your entire application. They typically start from an entry point, e.g. index.jsx, and then discover the graph of dependencies and recursively go through each file or module that they identify and discover those dependencies and so on and so forth. Once the compiler is done discovering, resolving, building, and bundling your app, you'll typically end up with a single Javascript file (e.g. main.[some hash].js) which is your application AND all dependencies (all those other modules you imported) bundled together in a single file.
So, you see the big distinction is:
HTML scripts are independent resources that are downloaded and processed by the browser at run time.
Javascript modules are discovered at compile time and end up being bundled together with your application code in a single file.
I'm leaving aside concepts such as package splitting and dynamic references, etc. in order to illustrate the larger distinction; you'll end up reading about variations in each approach as you dig further into them.
HTML Script advantages:
They are fetched independently, perhaps from a CDN, may even already be cached in your browser if another website needed them and downloaded them previously. So things like jQuery, loDash, etc. are common packages that may have already been downloaded, and your browser will benefit from its internal cache.
They can be downloaded in parallel; although you can rely on the browser to ensure that they get executed sequentially. So for example if you had your own script that relied on jQuery having already been loaded, all you need to do is to <script src="jquery.min.js" /> first and then <script src="myscript.js"> and that sequence will be respected.
Javascript Module Advantages
Your code may not need the entire jQuery, lodash, or whatever other library that you're referencing. By importing whatever function of your dependencies that you need in your source code, a smart compiler might be able to artfully scalpel only those functions out of the larger library (tree-shake the library), and you'll end up with a smaller overall download payload.
Your entire bundle can be minified, yielding an optimized package.
Your entire bundle will be (/can be) in a single file. One download, and your entire app is loaded and ready to use. No need to worry about downloading various resources from various URLs.
Hybrid Approach is OK
Feel free to use a hybrid solution! If you inspect your compiled React code, you'll see that the create-react-app compiler will inject a <script src="static/js/main.js" /> element at the end of your HTML document. This means that your app can rely on other`s included higher up in your HTML document. So, feel free to load up some libraries in HTML and reference other ones through JS modules. In fact, there are cases that you'd want to do this; for example, including the Google Maps script can be easily done via an HTML script directive, and your React app can still use the GMaps library.
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.
I am building a reactjs application using gulp and Browserify. I have used material-ui components in my application. There are 8 pages in my application using different components.
The build.js created by Browserify is 4mb in size. I want to reduce its size. I have searched and learned a little about the lazy loading design pattern. But I am confused how it should be used to make the size smaller of my application?
I am also using react router in my application so there are 8 routes defined. I have an idea that we can lazy load the files required for each route, but how could that be done in react?
P.S : I would have loved to share some code, but i don't understand what kind of code will be required for such a question.
The problem is most likely that your javascript contains source maps. Source maps can easily increase the size of your code by an order of magnitude. Try taking a look at your compiled JS file and if you see large random strings that look something like this:
dlfheihfgrewifjwe;iofgrewfwejroifnekw.nfoeiquf0eqf;oiwehjfkl;qwejfio;qeo;f;qoihfi;qejhfkjqwehj
then you are including source maps in your compiled Js.
Check your browserify config to make sure that you are not compiling with source maps.
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.