Grgur's Javascript Corner

Optimize ES6 Output Size & Performance

Transpiled ES6 code can get sizeable, which doesn't benefit limited bandwidth users, such as those using your mobile web site. Every byte matters, especially as web applications grow in size.

With a special emphasis on React JS application development, I'll to show a few approaches to writing performant code that also results in a smaller footprint when it comes out of your build process.

Goes without saying that UglifyJS or a similar minification plugin is an absolute must-have for transpiled code. Developing code with minification in mind is very important. You should know how variables will be changed or how to write clean code that will be properly reduced in size in production build.

Recreating Objects for Immutability

Redux users, in particular, are used to recreating objects to advertise a change in state.

let newState = Object.assign({}, state);
// 38 bytes minified

Object.assign is wonderful and comes with ES2015. Object spread is an alternative that is still a proposal for future versions of ECMAscript, but it's readily available for use via Babel (and other transpilers).

let newState = { ...state };
// 33 bytes minified (13% improvement)

Object spread operator made our code even easier to read and a bit smaller. Chances are you will use spread a lot, whether in Redux, JSX properties, or generaly within you application so it will add up.

It's important to know that Babel will create a small _extends function, which is basically a polyfill for Object.assign. This adds 175 bytes to your codebase, but is used for functionalities other than object spread.

Our first example was easy and yielded a solid improvement in the minified code size. Next we'll see how to squeeze out even more juice in common operations with JSX.

Conditional Statements in React Components

Conditional rendering is a frequent task in many applications, whether we submit properties conditionally or choose proper component for the task.

Conditional Properties

When your React components require properties based on conditional criteria, you should calculate thos props before writing JSX. This simplified example illustrates that:

So why do you think the bottom code results in 30% less code? The key is visualizing the transpiled code. The JSX will become a call to React.createElement function. That leads to a lot of code duplication, which we generally want to avoid.

The second version has a single JSX block and manages variables to passed to React.createElement as arguments. The transpiled code would look something like:

Conditional Components

A common use case for conditional components is choosing the right component to serve as a wrapper. For example, if the data object contains a URL then wrap in an <a> anchor. Otherwise use a <div> layer.

If you read the previous section, you'll know that this block results in a number of function calls. It cetainly looks readable (especially with syntax highlighting), but can we do better? I wouldn’t be bringing this up if I couldn't offer a better way, right?

This is a huge win for both code readability and application size. Of course, it requires the Menu component to support this approach with a loop similar to above, which could add around 100 bytes, depending on the implementation. But the benefits are clear.

Arrow functions

We all love arrow functions. But there's a use case for them. Don't replace regular functions with arrow functions just because it's a cool new thing.

In this example I'll create a noop function. These are useful as default callbacks or placeholders so we could easily reuse throughout an app.

function noop() {};
// 17 bytes minified

Arrow function counterpart could like like this:

let noop = () => {};
// 22 bytes minified (29% more bytes)

Ther are a number of reasons why I would not recomment using arrow functions for this purpose.

It's not easier to read

Produces more code

It's also a trap. Is {} going to return an empty object or undefined?

Answer: It will return undefined. let noop = () => ({}); return an empty object. It's a exception on the ugly side of JavaScript. See more here.

Destructuring With Care

I'm a huge fan of destructuring. But when I saw what it does to the transpiled code, I became a lot more careful about what I use it for.

Many developers will dislike repeating this in a function. It can't be minified and it can get misleading when switching context a lot.

The improvement is huge, staggering 48%! However, it comes at the expense of cluttered code. Especially if several arguments with default values are needed.

Performance vs Maintainable Code

ES2015 is much about syntactic sugar. This is a great thing because it makes it much easier to write and maintain great code. In the current era of web development this comes at an expense of application size and often processing cycles.

So where is the line between performance and maintainable code? This is a crucial decision for a software architect to make, and there's no rule of thumb. Customer facing apps will likely have to be as optimizes as possible, where as itnernal, enterprise products could allow for better code quality. Large apps would benefit from more maintainable code, but framework-grade projects will want to squeeze out any performance.

Please take this data with you, experiment, and share your findings. I would appreciate it if you liked this post and shared with your network.

Grgur is a software architect at Modus Create, specializing in JavaScript performance, React JS, and Sencha frameworks. If this post was helpful, maybe his 16 years of experience with clients ranging from Fortune 100 to governments and startups could help your project too.

Latest posts

Grgur is a mobile and desktop web application architect and an expert in JavaScript, React, Sencha Ext JS, Angular JS and Webpack integration. One of Grgur's key focus points is performance in his never ending research to find new ways of squeezing out every millisecond out of rendering engines.