DEV Community

Yogesh Galav
Yogesh Galav

Posted on

How to optimize Vite app?

Vite

Hello guys!!

Currently as a tech lead I got task of optimizing Vite App which was also using vue and inertia. I read many blogs but they all were either made for webpack or didn't made reasonable output,
After lot of research I had implemented some solutions which you must use.
Vite is awesome tool for bundling which already implements multiple strategies like chunking, versioning, compression for optimizing you app, so we can save some hard work.
Hence, if you are not using Vite you must look forward for new technology.
In my case the project was already big enough so the code written by different developers wasn't implementing ways to decrease load of app.

So for summary here's what I'm going to implement

  1. Common bundle for legacy packages.
  2. API defer.
  3. Third party script async and defer.
  4. Lazy load Modals and charts.
  5. Use ES6 modules.
  6. Vite plugins.

Let's discuss them in detail

1. Common bundle for legacy packages.

  • Vite already separates pages and components into different bundles which you can see in network tab but some of those bundles are still size heavy, hence take much time.
  • Vite also implements versioning so each time you release new version users have to download those heavy bundles again.
  • Also they have to download images again. All this effort can be saved with browser cache.
  • Only thing is you must know what to put in browser cache and what not.
  • For suggestion, you may choose all images to not included in versioning.
  • And for packages which you may choose legacy packages like moment and package you will not update in coming years.
  • Although it's not suggested to use legacy packages but in reality for large or legacy project it's not an easy job.
  • You can also identify big chunks or packages from visualizer plugin of Vite, link given below.
  • Here's the code you can attach in vite.config.js for having more control over versioning.
 build: {
        rollupOptions: {
            output: {
                entryFileNames: `assets/[name]-[hash].js`,
                assetFileNames: (assetInfo) => {
                    // Get the name from the array of names (usually the first/only entry)
                    const fileName = assetInfo.names?.[0] ?? '';

                    if (/\.(png|jpg|jpeg|gif|svg|webp)$/.test(fileName)) {
                        return `images/[name][extname]`;
                    }
                    return `css/[name]-[hash][extname]`;
                },
                chunkFileNames: (chunkInfo) => {
                    const is_manual_chunk = ['core', 'chart', 'quill', 'datepicker', 'moment', 'jsoneditor'].some(el => chunkInfo.name.startsWith(el));
                    if (is_manual_chunk) {
                        return `js/[name].js`;
                    }
                    return `js/[name]-[hash].js`;
                },
                manualChunks: {
                    core: ['vue', '@inertiajs/vue3', '@sentry/vue'],
                    chart: ['@j-t-mcc/vue3-chartjs', 'chart.js'],
                    quill: ['@vueup/vue-quill'],
                    datepicker: ['@vuepic/vue-datepicker'],
                    jsoneditor: ['@json-editor/json-editor'],
                    moment: ['moment', 'moment-timezone', 'moment-duration-format']
                },
            },
        },
    },
Enter fullscreen mode Exit fullscreen mode

2. API defer.

  • This second step looks so easy but brings a great difference to app load. All you have to do load important API's first and non-important API's at last.
  • For the API's which you wanna load first, call them from created hook or setup initial lines in vue3 of layout file.
  • A bad approach would be to call it from entry-file because it blocks app mounting, but you may use it if necessary.
  • The API's which you wanna load in last, keep them in defineAsyncComponent in vue. For react you can use async component via composition but it's difficult so react lover please give some suggestion in comment.
  • The API's you wanna load in between, try to put them in mounted or useEffect hook part not created hook or directly in script part.
  • Try to use less API call, as it will take some time to go through DNS, then server, then firewall, then your code Middlewares and with some extra code dependencies. In my case I combined multiple API into initial-data API.

3. Third party script async and defer.

  • Your project may be using third party scripts in main HTML element like google analytics, chat plugin etc.
  • If you define this scripts in head section then they will block further scripts and load first which is not needed at all. If your user can't interact with app quickly what's the point of all other tools.
  • Try to use these scripts in bottom body part instead of head section.
  • Now we have to decide between defer and acync script for those who don't know defer will call your script after page loads and async will load script in parallel thread non-blocking you main app JS.
  • In my case I used async for loading third party scripts
 <script async src="https://www.googletagmanager.com/gtag/js?id=G-DNJN1PF3CS"></script>
Enter fullscreen mode Exit fullscreen mode
  • and defer for executing those script
<script defer>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());

gtag('config', 'G-XXXXXXX');
</script>
Enter fullscreen mode Exit fullscreen mode

4. Lazy load Modals and charts.

In other blogs this part may be on top but I kept it below because it may not bring significant difference in app load unless you modal is calling some API's or is using large/heavy component

  • Please don't lazy load modal component itself but the component that's used inside it, In most case it would be passed by slot.
  • You can also lazy load chart components and other benefit you may find with it is suspense component till main component is loaded.

5. Use ES6 modules.

For those who don't know there's two type of js module, CommonJs and ES6, there is fight going on between them but most probably ES6 will win, because it's newer and have benefit of tree-shaking which we want to make use of.

  • This step also might not bring significant difference to your app bundle size but it's good to have. For me it was 12kb.
  • For those old packages which are created in CommonJS does not provide tree-shaking and hence increases the bundle size.
  • Most common use case is lodash which is used in many projects, some developers even use it in new projects so the alternative is lodash-es.
  • and you must use it in correct way otherwise it wont make any difference. Even I spent hours on figuring out why it isn't working 😅.
import { flatten as l_flatten, reduce as l_reduce } from "lodash-es";
Enter fullscreen mode Exit fullscreen mode

6. Vite plugins.

Vite provides various other packages to enhance your app. some of the packages I will mention here with link so you can read in detail about them, if you know others please mention them in comment

7. Optimize Inertia, if you use it

Last but not the least tip, if you use Inertia, please don't make HandleInertiaMiddleware heavy, either store and load the data in session if necessary, else keep it lightweight.

Thanks for read and Have a great day. ❤️
Please give your love and support to this post.

Top comments (0)