DEV Community

Cover image for How I optimized Carousel for EditorJS 2x in size.
Igor Golodnitsky
Igor Golodnitsky

Posted on

How I optimized Carousel for EditorJS 2x in size.

Why are we doing this?

I am supporting a project where lawyers, advisors can publish articles or news, to get more attention and clients. Initially it was made through TinyMCE, but they wanted not just HTML, but also a way of showing many pictures with interactions.
I was seeking UI editor like Medium, and I guess best what I found was EditorJS and during checking its Awesome List there were several carousel plugins and I stopped on this.

carousel look like this

  • The pros: it had already generated bundle with it, which I could easily add to my project
  • The cons: there were no demo page with it, it was only a screenshot, which later happens already outdated 😬

In current version there were buttons to move order, but for some reason instead of "delete" ❎ button as on screenshot it was blank, and also inside toolbox it was also blank.

initial view

No other way: I need to fork, clone it, πŸ”§ bugs, build myself. About fixing part maybe will write separate post, but I would like to share process of optimizing bundle, because it looked a bit heavy for such kind of code.

What with bundle size?

Let's see initial size of build we have 35.4k.

    Asset      Size
bundle.js  35.4 KiB
Enter fullscreen mode Exit fullscreen mode

After fixes size become even smaller:

    Asset      Size
bundle.js  35.1 KiB
Enter fullscreen mode Exit fullscreen mode

Even without tools it looked a bit weird, all files in source without minification costs us 27k, its something not needed inside, and if you unpack it with something like https://beautifier.io/ you will see:

strange stuff inside

What the hell process.chdir doing inside browser bundle?

What bundle consists of?

So as always we add webpack-bundle-analyzer:

"webpack": "^4.29.5",
"webpack-bundle-analyzer": "^4.10.1",
"webpack-cli": "^3.2.3"
Enter fullscreen mode Exit fullscreen mode

Adding --profile flag to build command:

"build": "webpack --mode production --profile",
Enter fullscreen mode Exit fullscreen mode

Bundle inside looks like this:
Initial build size 35.11k

What we have here, lets show it in form of json:

{
  "src": {
    "index.js + uploader.js": "8.64k",
    "index.css": "3.64k",
    "button-icon.svg": "439b",

    "all": "13kb",
  },
  "styles": {
    "style-loader": "4.67k",
    "css-loader": "808b"
  },
  "babel": "1.42k",
  "ajax": "14kb"
}
Enter fullscreen mode Exit fullscreen mode
  • src is fine, its meat we need to deliver.
  • babel hard to say, why it was used. There is no target or browserlist left. Not sure if author wanted to support IE11. Even when this component was created in 2021, es6 was on 97% adoption through browsers.
  • styles is interesting, we have index.css of 3.64k and delivery mechanism that costs 5.4k, which more than css itself 😺.
  • ajax is a blackbox for us for now, its another UMD bundle, need to build it separately, and check what inside.

Remove babel and styles

Remove babel from webpack.config.js
remove babel

Remove styles is trickier. In fact what we could do, is just remove it from bundle, to separate file. We could put it as it is, but author decided to use a bit of scss-stuff, and we might want to leave it. For webpack4 I didn't find a solution for this, so I updated it to webpack5.

Packages now looks like this:

"mini-css-extract-plugin": "^2.8.1",
"svg-inline-loader": "^0.8.0",
"webpack": "^5.91.0",
"webpack-bundle-analyzer": "^4.10.1",
"webpack-cli": "^3.2.3"
Enter fullscreen mode Exit fullscreen mode

Adding MiniCssExtractPlugin:

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
...
module.exports = {
  plugins: [
    ...
    new MiniCssExtractPlugin({
      filename: 'bundle.css',
    })
  ],

...

  {
    test: /\.css$/,
    use: [
      MiniCssExtractPlugin.loader,
      'css-loader',
      {
        loader: 'postcss-loader',
        options: {
          plugins: [
            require('postcss-nested-ancestors'),
            require('postcss-nested')
          ]
        }
      }
    ]
  },
Enter fullscreen mode Exit fullscreen mode

Let's build it, and see what we get:

    Asset      Size 
bundle.js  22.8 KiB 
bundle.css  3.1 KiB 
Enter fullscreen mode Exit fullscreen mode

As you see bundle.css is even smaller than css inside bundle, this is because to be in js, css need a bit of escaping and module instantiation. After we remove utils for loading, now instead of 9k we have 3.1k.

Intermediate result: 25.9k vs 35.1k

Checking ajax library

Library used here: https://github.com/codex-team/ajax. As stated in readme it is Just another AJAX requests helper. Main reason why it was used, I guess a simple helper for submitFiles(). Lets see what dist have inside:

ajax dist initially

What we really need is in red border.

  Asset      Size  
main.js  14.3 KiB  
Enter fullscreen mode Exit fullscreen mode
{
  "src": {
    "index.js": "3.74kb",
    "utils.js": "1.74kb",

    "all": "5.5kb",
  },
  "http-build-query": "569b",
  "promise-polyfill": "7kb"
}
Enter fullscreen mode Exit fullscreen mode

Again this library is from 2020.

support promise

Support of Promise in major browsers goes from 2014. Yes we supported IE11 for many years, and I guess in 2018 many companies let it go. Problem here that polyfill always here, npm distribute this bundle, and no way to tell, if you need it for your build or not.

After removing promise-polyfill:

  Asset      Size 
main.js  7.09 KiB 
Enter fullscreen mode Exit fullscreen mode

After removing babel for es6 classes emulation πŸ‘½

  Asset      Size 
main.js  6.09 KiB 
Enter fullscreen mode Exit fullscreen mode

If we copy this main.js to our carousel project as ajax.js, and build it, we will get:

    Asset      Size 
bundle.js  14.7 KiB 
bundle.css  3.1 KiB 
Enter fullscreen mode Exit fullscreen mode

It's already much better, but what I don't like about this, is that ajax.js is still UMD style webpack bundle, that means array of modules inside connected by something called (webpack)/buildin/global.js which takes 472b.

(webpack)/buildin/global.js

Why use it, if we have such a great thing like ESM πŸ˜ƒ.

Enabling ESM for ajax project

I didn't follow long story of dealing with ESM inside webpack during last 5 years, but looks like now it works good, and in fact my build from webpack was even more compact, than I could make from esbuild.

To enable it you just need to:

  • upgrade webpack to latest 5, for me its ^5.91.0
  • change "type": "module" in package.json
  • rewrite fully webpack.config.js

initial vs module

Result is nice πŸ”₯ and one more useless kilobyte gone.

  Asset      Size 
main.js  5.06 KiB 
Enter fullscreen mode Exit fullscreen mode

Lets see final result πŸŽ‰: 16.9k vs 35.1k

    Asset      Size 
bundle.js  13.8 KiB 
bundle.css  3.1 KiB 
Enter fullscreen mode Exit fullscreen mode

Resume

  • Don't treat bundles as binary, it is JavaScript
  • JavaScript is a high-level language
  • It is easily readable πŸ“š, even when minified
  • Can be formatted πŸ“ by vscode
  • It is a pleasure to feel full control of it

Thanks for reading. 🀘
Open to collaboration. πŸ‘‹
If you have full-time or part-time projects.
Usually work as full-stack: .net + js.
Write me πŸ“«: igor.golodnitsky@gmail.com

Top comments (0)