Reduce the size of Vue.js application

When I build my app Vue.js CLI shows result:

File                                 Size               Gzipped

dist/js/chunk-vendors.ead599c9.js    1910.45 kb         603.09 kb
dist/js/index.4ca2cadd.js            201.02 kb          39.30 kb
dist/css/index.7cd5b102.css          39.22 kb           5.96 kb

And it says:

asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
This can impact web performance.
Assets: 
  js/chunk-vendors.ead599c9.js (1.87 MiB)

How can I make it better?

Report

vue-cli-service build has a option --report. With this option CLI will create a chart to analyze chunks. Do

npm run build -- --report

And look for report.html in dist next to index.html.

Lazy load

Vue.js shows clue:

You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.

So I should try.

//import CategoriesPie from "./CategoriesPie.vue";
const CategoriesPie = () => import("./CategoriesPie.vue");

I made a lazy loading for my component with pie chart. This is only component that uses d3.js. This component was moved to separate chunk with d3.js.

 dist/js/chunk-vendors.f939fa5f.js     1779.47 kb         559.93 kb
 dist/js/index.65bb4e5c.js             198.29 kb          38.84 kb
 dist/js/chunk-2d212bb5.d805fcb1.js    134.49 kb          44.36 kb

I made all charts lazy, echarts was placed to own chunk. So chunk-vendors.js become smaller.

  dist/js/chunk-vendors.734a58a9.js     1019.23 kb         279.97 kb
  dist/js/chunk-66af4800.7362425e.js    760.38 kb          262.38 kb
  dist/js/index.875f9c98.js             167.21 kb          36.46 kb
  dist/js/chunk-2d212bb5.d805fcb1.js    134.49 kb          44.36 kb
  dist/js/chunk-2d0d63b4.0506405e.js    6.31 kb            2.32 kb
  dist/js/chunk-2d0e13c4.f023a52f.js    6.31 kb            2.32 kb
  dist/js/chunk-2d21d469.ab31c3ce.js    4.78 kb            2.03 kb
  dist/js/chunk-2d0ac42d.51d69f23.js    4.68 kb            2.00 kb
  dist/js/chunk-2d0c19b3.493276cb.js    4.63 kb            1.86 kb
  dist/js/chunk-2d225bc4.b29c0792.js    4.17 kb            1.93 kb
  dist/js/chunk-2d0e57c2.f8d489f3.js    3.21 kb            1.60 kb

The easiest way to reduce index.js size is lazy routes' components. Instead of

import LoginPageContent from "./components/registration/LoginPageContent.vue"
//...
routes: [
    {
      path: "/login",
      component: LoginPageContent,
    },

I can do

routes: [
    {
      path: "/login",
      component: () => import("@/components/registration/LoginPageContent.vue"),
    },

Lazy routes gave me that:

  dist/js/chunk-5084c06d.6223e348.js      763.40 kb         263.57 kb
  dist/js/chunk-vendors.c5bb7606.js       733.20 kb         184.75 kb
  dist/js/chunk-3fbc4487.78bd1736.js      232.42 kb         70.34 kb
  dist/js/chunk-7a13a3f8.77166654.js      134.55 kb         44.39 kb
  dist/js/chunk-3e474084.c58c7fc5.js      77.68 kb          19.61 kb
  dist/js/index.dada1dc3.js               32.73 kb          8.70 kb
  ... more small components

index.js shrinked from 167 to 32 kb.

Now I have 4 big chunks:

  • echarts
  • vue.js + elementUI
  • moment.js
  • CategoriesPie + D3.js

Don't import whole library

ElementUI

For development I added all components from ElemetUI.

import ElementUI from "element-ui";
Vue.use(ElementUI);

But I use just Dialog and Notification. So I should import these two.

import { Dialog, Notification } from "element-ui";
Vue.component(Dialog.name, Dialog);
Vue.component(Notification.name, Notification);

babel-plugin-component is needed.

npm install -D babel-plugin-component

Add this plugin to babel config.

babel.config.jsmodule.exports = {
  // ...
  plugins: [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]
  // ...
}

chunk-vendors.js decreased from 733 to 141 kb.

Moment.js

Moment.js contains a lot of locales. I don't use them. Moment.js site has documentation how to remain only necessary locales.

npm install -D moment-locales-webpack-plugin

Add plugin to vue config.

vue.config.jsconst MomentLocalesPlugin = require('moment-locales-webpack-plugin');
//...
module.exports = {
  configureWebpack: {
    plugins: [
      new MomentLocalesPlugin({
          localesToKeep: []
      }),
    ]
  }
}

Moment.js is 77kb now. Was 232kb.

Echarts

This library documentation has instruction too.

echarts.custom.jsexport * from 'echarts/src/echarts';

import 'echarts/src/component/dataset';

import 'echarts/src/chart/bar';
import 'echarts/src/chart/treemap';

import 'echarts/src/component/grid';
import 'echarts/src/component/tooltip';
import 'echarts/src/component/dataZoom';
rollup.echarts.config.jsimport node from "rollup-plugin-node-resolve";
export default {
  input: "echarts.custom.js",
  plugins: [node()],
  output: {
  	name: "echarts",
    file: "./lib/echarts.custom.js",
    format: "es"
  }
};
.eslintignorelib/

I should build my custom echarts before my application.

rollup --config rollup.echarts.config.js

And I will import the built result from lib folder.

vue.config.jsconst path = require('path');
module.exports = {
  configureWebpack: {
    resolve: {
      alias: {
        lib: path.resolve(__dirname, 'lib')
      }
    },
  }
}
//import echarts from "echarts";
import * as echarts from "lib/echarts.custom.js";

Echarts decreased from 763kb to 345kb. But it is still alot.

D3

I used the same approach that was used for Echarts.

d3.custom.jsexport {
 scaleLinear,
 scaleOrdinal,
 scaleSqrt
} from "d3-scale";
export { select } from "d3-selection";
export { arc } from "d3-shape";
export { transition } from "d3-transition";
export { hierarchy, partition } from "d3-hierarchy";
export { formatLocale } from "d3-format";
export { lab } from "d3-color";
export { interpolate } from "d3-interpolate";
//import * as d3 from "d3";
import * as d3 from "lib/d3.custom.js";

Chart chunk now is 73kb (was 134kb).

Conclusion

I reduced the full size from 2.1 to 0.9 MB. Problems now are echarts, moment.js and imask.
The main page is 33KB instead of 201KB. It is good.