DEV Community

Pradeep Kumar
Pradeep Kumar

Posted on β€’ Edited on

Nuxt 3 Performance Improvements

Dynamic Importing:
https://github.com/nuxt/nuxt/discussions/9061#discussioncomment-539483

1) Keep your Pinia Stores lean

Move all your business logic to services or utils or datalayer

Ex:

export const useCartStore = defineStore("cartStore", {
  state: () => ({
    cartItems:[],
  }),
  actions: {
    async addToCart(context, item){
    const { addToCart } = await import('~/services/cartService');
    return await addToCart(context, item);
    }
  },
});
Enter fullscreen mode Exit fullscreen mode

2) Disable component auto import

In your nuxt.config.ts, add:

components: {
    global: true,
    dirs: ['~/components/global'],
  },
Enter fullscreen mode Exit fullscreen mode

Keep your global components in components/global folder

Import component dynamically:

const Example = defineAsyncComponent(() => import('@/components/Example.vue'));

Enter fullscreen mode Exit fullscreen mode

3) Add compression and caching on assets

In your nuxt.config.ts, add:

nitro: {
    compressPublicAssets: true,
    minify: true,
    serveStatic: {
      headers: {
        'Cache-Control': 'public, max-age=31536000, immutable',
      },
    }
  },
Enter fullscreen mode Exit fullscreen mode

4) Add async and defer to scripts

5) Keep an eye on build sizes

You should run npm run build and keep any on build sizes

6) Use transform to optimize payload

Ref: https://www.youtube.com/watch?v=laRJNkG_wls

7) Use ClientOnly

Wrap component under <ClientOnly> tag which are not important for SEO, example:

<ClientOnly>
<NewsLetter />
</ClientOnly>
Enter fullscreen mode Exit fullscreen mode

8) Use Pinia State management effectly

Ref: https://masteringpinia.com/blog/5-best-practices-for-scalable-vuejs-state-management-with-pinia

9) Dynamic loading of services

Note: You should avoid calling your store or nuxt composable in services, instead you must pass an instance of store from page or component.

Store in Page/Component

<script setup>
import { onMounted } from 'vue';

const myStore = useMyStore();

onMounted(async () => {
  const { getItem } = await import('~/services/myService');
  myStore.item = await getItem(myStore)
});
</script>
Enter fullscreen mode Exit fullscreen mode

Store in middleware

export default defineNuxtRouteMiddleware(async (to, from) => {
  // Conditionally determine if the auth store needs to be loaded
  if (to.path.startsWith('/protected')) {
    // Dynamically import the Pinia store
    const { useAuthStore } = await import('~/stores/auth');

    const authStore = useAuthStore();

    // Perform the authentication check using the dynamically loaded store
    if (!authStore.isAuthenticated) {
      return navigateTo('/login');
    }
  }
  // Other paths might not need the auth store and can proceed without loading it
});
Enter fullscreen mode Exit fullscreen mode

10) Use export functions to avoid large bundle size

Smaller bundle size

// utils.ts
export const foo = () => {/* ... */};
export const bar = () => {/* ... */};

// Usage
import { foo, bar } from './utils';
Enter fullscreen mode Exit fullscreen mode

Tree Shaking: When using module bundlers like Webpack or Rollup, it's easier to tree-shake unused functions, potentially resulting in a smaller bundle size.

Large Bundle Size

// MyClass.ts
export class MyClass {
  foo() {/* ... */}
  bar() {/* ... */}
}

// Usage
import { MyClass } from './MyClass';
const myInstance = new MyClass();
myInstance.foo();
Enter fullscreen mode Exit fullscreen mode

11) SSR Calls

const { data } = await useAsyncData(
    'results',
    async () => {
      const {searchResult} = await import("@/services/algoliaService")
      return searchResult()
    }
)

Enter fullscreen mode Exit fullscreen mode

12) Use cachedEventHandler for API routes

ref: https://hub.nuxt.com/docs/features/cache#api-routes-caching
ref: https://www.youtube.com/watch?v=QamuVgRiLVg

13) How to getCachedData

const { data, error, pending } = await useAsyncData(
    'getPopularKeywords',
    async () => {
      const {getPopularKeywords} = await import("@/services-optimized/builderService")
      return await getPopularKeywords(
          runtimeConfig.public.builderApiKey
      );
    },
    {
      immediate: true,
      getCachedData(key) {
        if(!debug)
        {
          const cachedData = nuxtApp.payload.data[key] || nuxtApp.static.data[key];
          if(!cachedData){
            return;
          }
          //if data is too old
          const expirationTime = new Date(cachedData.fetchedAt);
          expirationTime.setTime(expirationTime.getTime() + 1000 * 60 * 60);
          const isExpired = expirationTime.getTime() < new Date().getTime();
          if(isExpired){
            return;
          }
        }
      },
    }
)
Enter fullscreen mode Exit fullscreen mode

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

πŸ‘‹ Kindness is contagious

Please leave a ❀️ or a friendly comment on this post if you found it helpful!

Okay