Vaadin JS Bundle too large to load for first time users

Hi

The JS file for users using the application for the first time or users who are doing a hard refresh ctrl+shift+R the JS file downloaded from Vaadin server(deployed on tomcat) is around 15+MB.

I think Vaadin bundle the complete JS into 1 single file and serves it in 1 shot if its modified. Is there a way to modularize this JS file or if I am doing something wrong that a JS file this large is generated?

Please refer the attachment.
18266297.png

15 MB for a bundle sounds like you’re using development mode that optimizes for quick turnaround and including debugging information in the bundle.

A production build bundle reduces size in several ways:

  • Debugging information (main sourcemaps) is not included
  • There’s an additional build phase that analyzes Java classes to determine which components are actually used, and then the JS implementation is included only for used components
  • The script content is minified
  • The bundle is gzipped

If you look at e.g. https://bakery-flow.demo.vaadin.com/ which is built for production mode, the JS bundle consists of the main 400KB chunk with all component implementations and another one around 40KB for Flow’s own generic framework logic.

Hi Nikhil,

I think you’re using Vaadin 14. When you’re building the application in production Mode, you probably have a warning message:

WARNING in asset size limit: The following asset(s) exceed the recommended size limit (2 MiB).
This can impact web performance.
Assets: 
  build/vaadin-bundle-be1d37f0f9d0456d4f37.cache.js (X.XX MiB)
  build/vaadin-bundle.es5-cfa4cd9e4dc48934c89b.cache.js (X.XX MiB)

WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (2 MiB). This can impact web performance.

In this case, to have more details for the bundle it takes 3 steps in a V14+ npm project:

  1. install an npm dependency: npm install --save-dev webpack-bundle-analyzer
  2. modify webpack.config.json:2.
const merge = require('webpack-merge');
const flowDefaults = require('./webpack.generated.js');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = merge(flowDefaults, {
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: 'static',
openAnalyzer: true
})
]
});
  1. build frontend (either in the dev mode with e.g. mvn jetty:run or in the prod mode with npm package -Pproduction). The bundle size report will open automatically.

There is a ticket to split the bundle into different files here: https://github.com/vaadin/flow/issues/5623
But for now, I think you should check the list of assets and remove the useless ones (if it’s possible).

Leif Åstrand:
15 MB for a bundle sounds like you’re using development mode that optimizes for quick turnaround and including debugging information in the bundle.

A production build bundle reduces size in several ways:

  • Debugging information (main sourcemaps) is not included
  • There’s an additional build phase that analyzes Java classes to determine which components are actually used, and then the JS implementation is included only for used components
  • The script content is minified
  • The bundle is gzipped

If you look at e.g. https://bakery-flow.demo.vaadin.com/ which is built for production mode, the JS bundle consists of the main 400KB chunk with all component implementations and another one around 40KB for Flow’s own generic framework logic.

When I run the application in production mode many of the components of my application behave unexpectedly. I am attaching the screenshot of actual component vs component when I run my application in production mode.

18267074.png
18267077.png
18267080.png
18267083.png

Looks like imports for e.g. select and tabs are missing in the optimized build, while everything is included in dev mode.

Which version of Vaadin are you using? Are you using templates or Java APIs?

My bet right now is that you have templates in use and import statements like import '@vaadin/vaadin-combo-box/src/vaadin-combo-box.js'; are missing, which means that they are left out of the optimized bundle.

Jens Jansson:
Looks like imports for e.g. select and tabs are missing in the optimized build, while everything is included in dev mode.

Which version of Vaadin are you using? Are you using templates or Java APIs?

My bet right now is that you have templates in use and import statements like import '@vaadin/vaadin-combo-box/src/vaadin-combo-box.js'; are missing, which means that they are left out of the optimized bundle.

I am using Vaadin 14.1.1

My MainLayout Class looks like this. I am importing each CSS I am using in this class.

package cool.fountain.manager.layout;

import com.vaadin.flow.component.applayout.AppLayout;
import com.vaadin.flow.component.dependency.CssImport;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.html.Image;
import com.vaadin.flow.component.html.Label;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.page.Viewport;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;

import cool.fountain.manager.constants.FountainManagerConstants;

@Route("")
/*
 * @PWA(name = "Project Base for Vaadin Flow with Spring", shortName = "Project Base")
 */

@Viewport("width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes, viewport-fit=cover")

@CssImport("./styles/fonts.css")
@CssImport("./styles/fountain-manager.css")
@CssImport("./styles/fm-navigation-tree.css")
@CssImport("./styles/layout.css")
@CssImport("./styles/vaadin-grid.css")
@CssImport("./styles/colors.css")
// @CssImport("./styles/specific/colorstwo.css")

@CssImport("./styles/vaadin-text-field-styles.css")
@CssImport(value = "./styles/component/vaadin-checkbox.css",
    themeFor = "vaadin-checkbox")
@CssImport(value = "./styles/component/vaadin-combo-box.css",
    themeFor = "vaadin-combo-box")
@CssImport(value = "./styles/vaadin-text-field-styles.css",
    themeFor = "vaadin-text-field")
@CssImport(value = "./styles/component/vaadin-grid-toggle.css",
    themeFor = "vaadin-grid")
@CssImport(value = "./styles/component/vaadin-tree-toggle.css",
    themeFor = "vaadin-grid-tree-toggle")
@CssImport(value = "./styles/drawer.css",
    themeFor = "vaadin-app-layout")
@JsModule("./styles/shared-styles.js")
@CssImport("./styles/specific/store-menu-list.css")
@CssImport(value = "./styles/component/vaadin-radio-button.css",
    themeFor = "vaadin-radio-button")
@CssImport(value = "./styles/component/vaadin-tab.css",
    themeFor = "vaadin-tab")
@CssImport(value = "./styles/component/vaadin-button.css",
    themeFor = "vaadin-button")
@CssImport("./styles/specific/storefunction.css")
@CssImport(value = "./styles/specific/vaadin-date-picker.css",
    themeFor = "vaadin-date-picker")
@CssImport("./styles/specific/staff.css")

@CssImport("./styles/specific/category.css")

@CssImport("./styles/specific/ordermanagementtab.css")
@CssImport(value = "./styles/context-dialog-overlay.css",
    themeFor = "vaadin-context-menu-overlay")
@CssImport("./styles/specific/CreateOrderCC.css")
@CssImport("./styles/specific/filter-by.css")
@PageTitle(FountainManagerConstants.FOUNTAIN_HUB_PAGE_TITLE)
public class MainLayout extends AbstractFmRouterLayout {

  public MainLayout() {
    // TODO: Change logo based on Login
    Image img = new Image("assets/chaipointLogo@3x.png", "Chaipoint Logo");
    img.addClassName("fm-logo");

    // TODO: Get this from external CSS
    // img.setHeight("44px");
    setPrimarySection(AppLayout.Section.DRAWER);

    addToDrawer(img);
    // addToNavbar(new DrawerToggle()); // TODO: Create header layout class
    NavigationLayout navigationLayout = new NavigationLayout();

    addToDrawer(navigationLayout.getLayout());

    // Footer
    HorizontalLayout footerLayout = new HorizontalLayout();
    footerLayout.addClassName("grid-footer");
    Label poweredBy = new Label("Powered By");
    Image fountainImg = new Image("assets/fountainLogo@2x.png", "Fountain Logo");
    fountainImg.addClassName("fm-footer-logo");
    footerLayout.add(poweredBy);
    footerLayout.add(fountainImg);
    addToDrawer(footerLayout);


    HeaderLayout headerLayout = new HeaderLayout();
    headerLayout.addClassName("main-header");
    addToNavbar(headerLayout);
  }

}

Below is the custom AppLayout extending Vaadin AppLayout.

package cool.fountain.manager.layout;

import com.vaadin.flow.component.applayout.AppLayout;


public abstract class AbstractFmRouterLayout extends AppLayout {

}

Apart from this I have lot of node_modules created under node_modules folder(Refer Attachments). What am I missing to include in my code.

18267290.png
18267293.png
18267296.png
18267299.png

Jean-Christophe Gueriaud:
Hi Nikhil,

I think you’re using Vaadin 14. When you’re building the application in production Mode, you probably have a warning message:

WARNING in asset size limit: The following asset(s) exceed the recommended size limit (2 MiB).
This can impact web performance.
Assets: 
  build/vaadin-bundle-be1d37f0f9d0456d4f37.cache.js (X.XX MiB)
  build/vaadin-bundle.es5-cfa4cd9e4dc48934c89b.cache.js (X.XX MiB)

WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (2 MiB). This can impact web performance.

In this case, to have more details for the bundle it takes 3 steps in a V14+ npm project:

  1. install an npm dependency: npm install --save-dev webpack-bundle-analyzer
  2. modify webpack.config.json:2.
const merge = require('webpack-merge');
const flowDefaults = require('./webpack.generated.js');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = merge(flowDefaults, {
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: 'static',
openAnalyzer: true
})
]
});
  1. build frontend (either in the dev mode with e.g. mvn jetty:run or in the prod mode with npm package -Pproduction). The bundle size report will open automatically.

There is a ticket to split the bundle into different files here: https://github.com/vaadin/flow/issues/5623
But for now, I think you should check the list of assets and remove the useless ones (if it’s possible).

Thank you for recommending this.
I used this. I have report’s screenshot attached. But still not sure what to exclude and what to keep.

18267929.png
18267932.png

You should try to update your vaadin version to the latest 14.1.x (14.1.28 I think).

In production mode, it seems some Css rules don’t applied.
Perhaps some css rules has not been imported or in a different order.

For the bundle size, in production mode the size should be compressed and optimized. I don’t see anything wrong in your bundle that could lead to a 15Mb bundle in production mode.

Either CSS rules not applied like Jean-Christophe says, or then the components have not been upgraded correctly (It would just render whatever is in the components as plain text in that moment. One thing to test is that you remove all the @CssImport, build prod and test it. That will rule out if it is your styles that mess with the component’s built in styles.

Jens Jansson:

My bet right now is that you have templates in use and import statements like import '@vaadin/vaadin-combo-box/src/vaadin-combo-box.js'; are missing, which means that they are left out of the optimized bundle.

Is that because it’s using /src? A number of my templates import /src/. I seem to remember that it was Vaadin Designer that created those imports.