Press "Enter" to skip to content

Learn React & Material UI – #16 Optimizing Bundle Size


hey guys welcome back to cold realm my
name is Alex and in this video we’re
gonna be talking about bundle size
optimizations and specifically I’d like
to show you how you can reduce your
bundle size by up to 20 percent without
changing a single line of code in your
source code now before watching this
video recommending you go ahead and
check out the very previous one which is
called destructuring creator react app
with webpack 4 and babel 7 and that one
basically covers switching from a
creative act app into a custom project
setup but for this video we actually
went ahead and created a repo on github
and this one contains all the useful
links to cold sandbox if you’re
interested in those and it also has a
link to a medium article I wrote in the
same topic so check out that one if you
are interested and after that you could
also find some installation instructions
as well as a few development commands to
get you started now I’m gonna leave a
link to this repo in the description but
if you want to check it out yourself
you could go to github.com slash Alex
996 and then you could go to
repositories and it’s gonna be the first
one on the list and what we’re gonna do
for this video like I said is we’re
gonna actually switch to a branch over
here which is called SVA unoptimized and
this one basically contains the code for
this video so we need to do is we need
to click on this button over here we’re
gonna copy the path to the repo I’m
gonna switch to my terminal and I’m
gonna do a git clone of that repo so
let’s go ahead and go to that directory
this one would be react exercises I’m
gonna run the yarn commander to install
all the dependencies and once we’re done
let’s open visual studio code I’m going
to pull it back over here and what I’d
like to do first is I’ll go to source
and XJS and we’re actually gonna convert
this dynamic import of app component
into an actual imports so I’m gonna
switch back over here let’s do an import
of app from components app so this way
we can remove the dynamic import over
here now the reason for doing this is
really first of all simplicity this way
we’re gonna have a single bundle and
it’s just gonna be easier for us to
analyze the size of that module once we
apply optimizations but also the way
we’ve had it before with the dynamic
import that
really a very primitive approach for
coats plating this way of doing things
is more suitable for large dependencies
but in this case the app component
itself is barely even eight kilobytes so
it doesn’t really make sense to do it
that way
so let’s leave it at that for the time
being now in the same node we can
actually go to Babel or C and we can
remove Babel syntax dynamic import
because we won’t be using it over here
so we could also go to package JSON and
we can also uninstall this plugin from
dev dependencies so let’s do exactly
that
yum remove Babel plugins syntax dynamic
import and then after that I’m going to
switch back to package JSON it’s also
very useful to have a remove command so
let’s put it over here I’m going to do
remove – RF of the dist folder and this
way we’re gonna clean up the contents of
that dist folder so let’s save package
JSON and back in the terminal I’m
actually going to open a separate tab
and what I’m gonna do is let’s run yarn
build so this way we’re gonna see the
size of the initial bundle without any
optimizations so let me clear up the log
over here so I’m gonna do and the last
command on the dist folder and let’s
observe the size of the main GS bundle
the size is currently 371 kilobytes and
this is without any optimizations at all
so let’s see what we can do about the
bundle size so the first thing we could
do is we can go to our babel RC file and
we can actually optimize the way that
we’re loading polyfills so if i go to
google chrome let’s actually search for
the environment preset in babel and if
we look for use built-ins we’re gonna
find some documentation about this key
now this use built-ins allows you to
apply a few options over here the first
one is obviously false which is the
default and that means you’re not gonna
be using any polyfills
the one that we are using at the moment
is called entry well entry is gonna take
a look at the environment and it’s gonna
try to import everything that that
environment is gonna need so the imports
in the end are going to be dependent on
the target browser now there’s also
another option and this one is usage so
this experimental option allows you to
not only take into consideration the
target environment so the browser that
they use
is gonna be using but it also takes into
account your source code so it’s gonna
go ahead and analyze your source code
and it’s gonna see what polyfills are
gonna be needed to make your source code
work regardless of the browser so it’s
gonna go ahead and pull in all the
polyfills that are missing for your code
specifically so it’s not gonna import
everything but it’s only gonna import
what your files are using so let’s go
ahead and try that option instead so the
only thing I’m going to change is this
single word over here so instead of
entry we’re gonna be using usage so
let’s go ahead and do a build command so
in a separate tab I’ll do yarn build and
once that’s now let’s switch back to
this one let’s see the size once again
and now you can see that the size went
down quite a bit so it went down from
371 to 359 so it’s about 11 or 12 K
difference and again the only thing we
had to change is the option for
polyfills now if you inspect the output
actually what it tells us is it tells us
to remove the import command of Babel
polyfill and let’s actually go ahead and
do exactly that so we no longer need to
import Babel polyfill at the top over at
refile and on the same note we can
actually also remove it so let’s do a
yarn remove Babel polyfill we can go
ahead and do a yarn build once again and
just to be perfectly sure we can check
the size once again it went down even
further so when using the usage option
for use built-ins make sure that you
also remove any imports of babel
polyfill now the next thing I’d like us
to look at is actually the import
statements of material UI now if you
take a look over here what we’re doing
is we’re actually importing the
components from achill UI using named
imports now if you look at the
documentation the documentation actually
uses direct imports across the board so
instead of importing CSS baseline for
example instead of doing a named import
in a documentation you would typically
see something like this so import CSS
baseline from a chili wise slash core
slash CSS baseline now this is doable if
you have only single imports and if you
start having multiple direct imports
it’s going to affect readability of your
code so imagine instead of importing all
of these components in a dialog
component we
to go ahead and import them directly in
the separate import statement it’s kind
of efficient and it kind of blows your
code so it’s very very useful to use
named imports but unfortunately they
come at the price and that’s because by
using this type of import you’re not
only importing a button as well as the
dollar component but you’re actually
importing everything else from a tool UI
so it’s gonna try to pull in every
single component oh you might be asking
why is that happening that’s happening
because material UI by default uses the
common gia syntax for imports and the
common gia syntax looks something like
this it would look something like
required from my chill UI / core and you
could import let’s say a button right so
you could import a button now in this
case this wouldn’t be a named import
this would actually import everything
from actual UI core but it’s going to
destructure the button from that object
now we can go ahead and look at node
modules let’s go to module UI core and
let’s go ahead and look for a
package.json file the package.json file
will have a main key this one is going
to point to a file where all of our
components are going to be exported from
and this index.js file as you can see
well that has a bunch of object defined
property and these ones define all of
the different exports that the library
provides and if you take a look at them
closely you’re gonna see that this file
basically exports everything from the
library and this is exactly the problem
talking about so if you import let’s say
a button like I said it’s not only going
to import the button it’s gonna import
all the components from the library now
there is a way to mitigate that instead
of importing a button in this fashion
let’s say using a name import you could
of course import the button directly
right so you could do a button from
material UI / core / button and the same
thing you would have to do for your
dialogue so you’re gonna need an
important for your dialogue and you can
see how quickly it becomes tedious you
have to import every single component
directly now instead of doing that you
can still keep your named imports just
like you had before but you could
install a special plugin that is going
to transform all the name them ports
into direct imports so what we can do
over here is we can switch to Google
Chrome
let’s go ahead and look for that plug in
this one is called Babel import plugin I
think Babel plug-in import now luckily
for us this plugin actually has an
example for material UI so let’s look
for it so you need to do is you
basically need to provide some
configuration to your Babel or C and
this way it’s going to transform all of
the import statements for your target
library so let’s do exactly that let’s
go back to our terminal
and in the second tab I’ll do a yarn add
of a development dependency Babel
plug-in import and now because we’re
gonna have multiple transformations
we’re going to need to use this syntax
over here where you basically provide an
array the first argument will be the
name of the plugin in this case it’s
babel plugin import or just import for
short the second option is the object
which defines the configuration in this
case they’re transforming and D library
or ant design and they have a few other
options and the last one is sort of like
an alias for that specific transform so
let’s go ahead and copy this pattern
over here in our case we’re gonna be
targeting material UI so I’ll paste it
in
let me quickly adjust the spacing and
once we do we’re gonna be targeting
material UI / cor we can leave library
directory empty for now but we also need
to provide one last option and this one
is actually camel – – component name we
have to set it to false because it’s
true by default in this case the names
of material UI components are already
camel cased so we don’t need to apply
any transformation here so this option
over here is only going to target
material UI core so any component that
you import let’s say once again in a
dialog example by importing the button
this is actually gonna be transformed to
the direct import of that button
component and the same is going to
happen for every single named import now
this is actually going to only target
module UI core but if you look closely
we also have a few other imports as well
so for example we’ll have an import of
colors over here and index.js file and
this import targets module uija core
colors there’s also another one which
imports the mui provider and korea
Mui theme and in this case actually made
a mistake this one has to also have
slash styles at the end so we’re going
to need to make sure we target these
styles directory but you can find this
pattern elsewhere as well let’s say in
layout header we import with styles from
material UI / core / styles so that’s
another directory we’ll have to target
and the last one would be the icons the
icons in this case is a separate library
so we also have to add it to the list of
transformations over here so let’s add
another entry over here it’s gonna be
the same plugin but it’s gonna target a
different directory let’s target styles
and in this case let’s also adjust the
elías let’s call it mature UI / core for
the first one we can give a similar name
to the second one let’s just put slash
styles at the end now let’s copy two
more so in this case we’re targeting
styles the last one would be for icons
so let’s put in the colors once again
this one would be for the two colors we
are importing from over here once again
the reason we have to do this even
though we’re only importing red and
amber this is actually pulling in all
the colors from my Chile UI / core /
colors so we have to make sure that we
transform these two imports to direct
imports of red and amber so four colors
I’m going to adjust the alias and lastly
for icons will have matul UI / core /
icons like this and lastly let’s adjust
the alias so we could save it so let’s
go ahead and recreate the bundle and see
if this affects the size
don’t forget the current size is 355
let’s see if anything changes I’m gonna
do yarn build once again so let’s switch
back and let’s see so the size went down
from 355 to 341 kilobytes once again the
reason for that is because by using
named imports we’re actually importing
everything from the library so in order
to avoid that we’re going to be using a
special plugin which allows us to
transform named imports into direct
imports so once again to make this clear
I’m gonna change this example so in this
case instead of importing rad from a
chili UI / core / colors
we’re gonna be importing red from
material UI / core / colors / red and
the same thing is gonna happen for ember
like this by the difference in this case
we don’t have to do this manually the
plug-in is gonna do this for us all we
have to do is just basically put these
named imports and they’re gonna be
transformed for us now another
optimization we could do is we can pull
in another plugin so this wouldn’t be
transform runtime now why is this plugin
useful well when you transfer your code
with bable bable is going to import
helper functions into every single file
that it’s transpiling now to actually
reduce the duplication of those helper
functions what we can do is we can apply
this babel transform runtime plugin and
this one is going to hoist all the
helper functions at the top so this way
it’s can avoid duplication so let’s go
ahead and install that plug-in as well
so in the terminal I’ll switch to the
second tab let’s do yarn add a dev
dependency of that plug-in while it’s
installing I can go ahead and add it to
the list of plugins so under the plugins
key we can go ahead and add another one
so this one would be plug-in transform
runtime we could also remove the plug-in
prefix once again because we are already
under plugins so it’s gonna be implied
by default so once it finishes
downloading let’s do yarn build let’s
check out the size once again it doesn’t
change that much it changed my about one
kilobyte again it’s not such a big
improvement but hey if we can save one
kilobyte for free why not do that and
now the other thing I’d like to mention
is for our targets we can actually
instead of providing an array we could
simplify it a bit we can pass in a
string so let’s say in this case we are
targeting more than 1% of browsers in
the markets not ie 11 and also not Opera
Mini Oprah underscore mini so instead of
passing in array we could just pass in a
string now this will not affect the
bundle size this is really just for
readability and now the other thing we
gives you as an optimization it’s not
going to improve the bundle size but
it’s actually going to be useful down
the road this one is going to tell you
how to set up
the hash for your filenames and this
will be useful for browsers because the
browser’s do is they by default cache
all of the assets now this is useful
because it speeds up the load time of
your application but it’s not really
that useful once the application has
changed or once you had pushed new code
to production so in order to bust the
cache so to speak we can go ahead and
also pass in a special hash to the file
name in this case if we’re using content
hash the house shall be based on the
contents of the file so it’s gonna
change every time your bundle also
changes so let’s go ahead and add this
conflict to your web pack configuration
so in here we can pass in an output
option we can rely on a default file
name so bypassing the file name key I’ll
pass in the string this one is gonna be
main GS but the differences will also
gonna apply content hash you need to
close up the square bracket over here
and then finally the extension will be
JavaScript obviously so let’s go ahead
and build a new bundle to see the
difference yarn build and if you look at
the contents of the dist folder you can
see that the size of the bundle is
pretty much the same it didn’t change
that much but the difference is now we
have a hash added to the filename so
every time the contents of the bundle
change the hash is also going to be
regenerated and this way the browser
will know to pull in the new version of
the bundle instead of using the old and
cached one once again this doesn’t
affect the bundle size this is really
just an optimization for cache busting
now last I’d like to show you a few
tools that you can use in order to
monitor these size of your bundle so one
tool you can install is web pack bundle
analyzer now what this one does is it
basically allows you to visualize the
dependency graph in your application so
it’s gonna show you the size of every
single dependency and it basically
allows you to find out which dependency
takes up how much space so all we have
to do to install this plugin is we need
to run the install command so copy its
name let’s go back to our terminal I’m
gonna do a yarn add dash D and now to
use this plug-in one way would be to new
it
in the webpack configuration but I kind
of find it useful to actually do it from
the command line so the first thing we
need to do is we need to create a
special stats JSON file and this one is
being created by web pack and it
basically contains a map to your node
modules dependencies so let’s grab that
one
let’s go back to our project and inside
our package JSON I’m gonna go ahead and
create a new command let’s call it stats
you could call it anything but in this
case let’s call it stats and the first
thing we’ll do is well generate these
stats JSON file now the second thing we
need to do is we need to run web pack
bundle analyzer and there’s two options
you can pass the first option would be
this stats file and then the second one
will be the bundle directory so let’s go
ahead and do exactly that so I’ll copy
the command I’m gonna chain it at the
end of this script so we’re gonna pass
in the stats JSON file and we’re also
gonna be monitoring the dist directory
as well so let’s go ahead and run that
command so this would be yarn stats once
it generates this stats JSON file it’s
gonna go ahead and open up a new tab in
the browser and this way we’re gonna be
able to see all of the dependencies in
our application so on the far right
you’re gonna see the source folder it’s
actually very slim and it’s about 7.48
kilobytes and this is all the source
code in our source directory and
everything else is all the dependencies
and node modules so you could see on the
Left we have module UI which takes up
quite a bit of space we have react Dom
in the middle we have JSS at the bottom
over here
and then there’s quite a few other ones
as well so you could go ahead and play
with it and see which dependencies are
taking up space and the last tool I’m
going to point you to is actually web
pack visualizer so to use this tool you
can drag the stats JSON file and once
you upload it it’s gonna show you the
graph of your dependencies this way you
could see that my show you why for
example is taking up almost 60% of our
node modules once again our source code
is only 1.8 percent you can see other
dependencies as well a kuryak tom you
could see JSS once again and all the
other ones the useful thing is you could
also inspect the exact modules in every
dependencies of
example in module UI you can see that
the core takes up most of it and then
there’s a small chunk over here for the
icons which is only in 0.4% and then
there’s also the stylus directory which
takes up about 9% and there’s all these
other components so you could go ahead
and inspect it yourself to find out more
now let’s go ahead and add another
dependency as well this one is going to
be useful for serving the dist directory
we can go ahead and install a dab
dependency this one is called serve and
this one actually comes from now so
let’s go ahead and create a new script
we can call it serve and this way we’re
gonna invoke the serve package on the
dist folder so once we save it we can go
ahead and run it so let’s do yarn serve
and this is gonna serve the district
tree on localhost 5,000 but basically we
can see that it loads successfully we
can still navigate our application we
can add at exercises I believe without
errors you can see that this one moved
to back we can create a new exercise
this way you could just test out the
dist bundle to make sure that no errors
have been introduced and this can be
done on the local environment so at this
point you can actually stop the video
and you’re actually good to go and if
you check out the size of the bundle so
we went from 371 to 341 so it’s about
30k difference so it’s already pretty
good now if you want to go further stick
around because I’m gonna show you
another way you can optimize your bundle
size and this one is actually gonna chop
away quite a bit of code from our
dependencies now remember we talked
about common GS and how much real UI
exports everything in the format of
common GS this is actually the core of
the problem we could reduce the bundle
quite a bit by actually using what’s
known as tree shaking now tree shaking
is pretty much impossible for common GS
because command GS exports objects and
those objects contain specific keys like
let’s say the components that the
library exports or specific functions
that we want to use like let’s say with
styles the problem with common GS is
that those functions are those
components are being exported as part of
a single object
which is typically module exports so if
we could somehow use es6 modules in
mature UI this could potentially reduce
the bundle size quite a bit so luckily
for us module UI actually exports a
directory which is called es and it’s
kind of hidden it’s not very easy to
find in the documentation but if you
look for mature UI bundle size this one
actually has a very small section at the
end of the documentation page and this
one actually points you to the es folder
which essentially contains all the
exports of components in the es6 modules
format and why is that module syntax
useful for us well because if we use es6
modules we’re going to be able to take
advantage of tree-shaking now let’s
actually look at that es6 folder over
here if we look at the app component
let’s look at the index.js file this one
exports the default from app bar and the
app bar is basically an ESX module you
could see all of the imports in the es6
format now this is quite different from
the regular app bar so this one has the
index.js file using common GS this is
actually what we are using right now and
it also has the app bar which contains
common GS exports and imports as well
and if you’re curious about this
gibberish over here it’s actually being
transformed by babel by default babel
transforms es6 modules to common GS
imports and this is just so that the
library is not only useful for those who
use bundler let’s say web pack to bundle
everything for browsers but it’s
actually also usable in no GS
environments which presently support
comma G is out of the box without any
flags now to take advantage of es6
modules all we have to do is we need to
set up an alias to this es6 folder over
here in this way everything that we
import from material UI is actually
going to be importing es6 modules
instead of common GS and it’s you
accomplish that task is not that
difficult we can go to a web pack config
over here so we’ll go ahead and add a
resolve key we’ll set up an alias and
this alias will target material UI / cor
and it’s gonna point everything into
material.you eyes / cor / es so this way
we’re gonna be relying not on the common
J’s code but we’re gonna be pulling in
es6 modules and when we pull those
modules we’re gonna rely on web pack to
transform all of that code into a single
bundle so I’m actually going to go to
Babel or C and as it turns out we’re not
going to need any of these imports over
here so we can actually delete all of
them except one we’re gonna have to
leave out the last one now we can also
remove the alias because we only have
one transformation here we’re only
targeting mature white icons now your
question might be well why don’t we do
the es6 transform for icons as well well
the answer to that is because the Mattel
UI icons library currently does not
provide es6 modules it actually used to
but they ran into problems with timeouts
because the library actually grew in
size and people had trouble installing
that dependency so as a short term
solution they actually went ahead and
dropped es6 modules now in the future
this might as well change so they might
actually create a separate library with
es6 exports and this way you could take
advantage of tree shaking but for the
time being there’s only the kanji has
syntax available to us so we have to
deal with what we have so I’m going to
actually leave this transform over here
for now so let’s go ahead and save this
babel or c file and now over here i’m
gonna do a yarn build and let’s actually
see what happens to our bundle size if
we look at this size right now you guys
see a walloping difference we went from
341 kilobytes to just 293 and once again
it really doesn’t change the behavior of
the application and in fact to prove
that we can actually serve a wrap from
this folder let’s see if we have any
breaking changes we can still delete the
exercises we can switch between
different tabs we can still create a new
exercise as well and we don’t get any
errors and in console so nothing really
changed app wise but we’ve been able to
reduce the bundle size and once again
that thanks to tree shaking so what’s
happening right now is instead of using
common GS exports from actual UI we are
relying
on es6 modules that are conveniently
available for us now you actually have
to be mindful of this approach so
actually if I go back to module UI let’s
look at core I go back to this es6
folder if only I can find it you have to
be mindful of this approach because what
is folder exports is actually es6 syntax
so this way you’re gonna have arrow
functions you’re gonna have cons
keywords in your bundle and this is
actually not suitable for something like
IE 11 and in fact if you look at the
documentation they actually mentioned
that this approach is really only
suitable for evergreen browsers now as
it turns out you actually can change
that code so you can’t transfer it to es
5 you’re gonna need to play with the
babel water configuration so you’re
gonna need to target module UI and
you’re gonna need to make sure that you
transpile all those these six modules so
that that code is gonna be suitable for
something like I 11 and also remember
that this alias is only going to work if
you don’t have any third-party libraries
because what the documentation suggests
is that any library that’s using machole
UI should import components in the
direct fashion so instead of using named
imports as we are using right now the
creators of third-party libraries are
encouraged to do direct imports and the
alias in a wrapper configuration is not
actually going to account for that so if
you use it with a third-party library
you’re gonna end up with duplication in
your bundle now to avoid that you can
take advantage of normal module
replacement plugin and I’m gonna weave
out a link to a github issue where you
can basically find out how to set up
that plugin for yourself but once again
you could still use the alias if you’re
not relying on any third-party libraries
they use Mitchell UI so this is
perfectly fine as long as you’re not
using any of those libraries if you do
intend to use them you are probably
better off by setting up that
replacement plugin like I said so to
close this off let’s actually look at
the size one last time so once again we
went down from 371 kilobytes to a
walloping to 93 so that’s quite a bit of
an improvement that’s about 20% in total
now once I get like I said you could
still be using comma GS if you want to
it’s
more of a safer option for now and it’s
actually easier if you’re also
supporting something like IE 11 for
example and once again you could still
transpile material UI es6 exports for
yourself in this way you’re going to be
able to use your application on older
browsers as well so there’s arguably
quite a few other improvements you can
do so you could also look into how to
mitigate this warning in size so right
now we’re having quite a sizable bundle
I think it’s still shippable to
production it’s not actually that big
but you could also go ahead and try to
play with code splitting and see how you
can optimize your bundle and split into
multiple chunks you could also look into
compressing your bundle into a gossiped
file to save up on the size of your
bundle a lossy could also look into
deploying your application into a CDN or
a content delivery network this way
you’re gonna be able to cache all your
static assets and this is also gonna
speed up the delivery of those assets
now even though it doesn’t affect the
bundle size it’s gonna affect the load
time for your application so once again
there’s quite a few other options out
there as well but these ones are the
basic ones and once again all we have to
do to optimize the bundle size was to
simply adjust some of the configuration
options we really didn’t have to change
a single line of code in our code base
make sure to go ahead and check out the
repo on github once again and you could
also pull it in from SBA on optimized
branch like I said make sure it’s you go
ahead and also check out some of the
useful links I included and they read me
but for the time being this is it for
this video so I hope you found it useful
and I’m gonna see you in the next one take care

Please follow and like us: