Learn React & Material UI - #16 Optimizing Bundle Size

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
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
Info
Channel: Code Realm
Views: 13,821
Rating: undefined out of 5
Keywords: react, reactjs, material-ui, material ui, ui library, material design, bundle, bundle size, bundling, webpack, es modules, commonjs, optimizations, tree shaking, fast render, fast load
Id: CGgEPHwzCUU
Channel Id: undefined
Length: 30min 28sec (1828 seconds)
Published: Sun Sep 09 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.