In my Vue 3 project my icon component dynamically imports icons. But this causes way too many files in the final build. How can I optimize that?

Viewed 26

I'm using CoreUI for Vue and their icons in my project. And I created a new icon component wrapper around their CIcon component that allows me to import any desired icon dynamically... instead of statically, which is how the component is normally used.

I do this, because I have a navigation sidebar that changes based on what role the authenticated user has.

So my sidebarMenus.ts looks a little like this:

export default {
  admin: [
    // list of menu items
  ],
  moderator: [
    // list of menu items
  ]
  user: [
    // list of menu items
  ]
}

And a menu-item type looks something like this:

type MenuItem = {
  name: string
  route?: string // If route is not present then this item behaves more like a header
  icon?: string // the name of the icon that will be dynamically imported later
}

And in my LazyCIcon.vue I use these lines of code:

onMounted(async () => {
  if (!icon) return
  
  // this line is just to be sure that the iconName is in camelCase,
  // just in case I had written the icon property in kebab-case.
  const iconName = icon.replace(/-(\w)/g, match => match[1].toUpperCase())

  // This line does the exact opposite.
  // The filename has to be in kebab-case and here I ensure that,
  // just in case I had written the icon property in camelCase.
  const fileName = icon.replace(
    /([a-z])([A-Z])/g,
    match => `${match[0]}-${match[1].toLowerCase()}`
  )

  // Vite / Rollup only accept dynamic imports that start with ./ or ../
  i = (await import(`../../node_modules/@coreui/icons/js/free/${fileName}.js`))[
    iconName
  ]
})

So my problem is that when I run npm run build, it will include every single javascript file in the node_modules/@coreui/icons/js/free/ folder, which is 556 javascript files! And that's understandable of course, and would usually not even be a problem. But in my case it is a problem. Because I've also created a service worker (with WorkBox) that automatically pre-caches all assets. So it downloads alllll those files and it makes the first page load for a brand new visitor excruciatingly slow.

While actually I only really need a few icons. Maybe 20 at most, I'm not sure, I didn't count them. So I'm wondering, how can I optimize this?

I'm a bit afraid that the only optimization I could do will involve hard-coding somewhere which icons I'm using in my project. And that would suck a bit, because then if I want to change an icon in the sidebar (or anywhere else in my project), then I'd also need to remember to change that hard-coded list as well.

Maybe I could write a vite / rollup plugin that generates that list automatically at build-time, by just scanning every file for strings that start with cil (since all those icons start like that). But I've never written a vite and/or rollup plugin before, so I may need some help with that. I mean I'm sure I'll be able to figure out how to compose a list of all icons used in my project. But then I have no idea how to tell rollup to only include the icons from my list instead of every icon in that folder.

Finally, I wonder if it would be possible to tell rollup to combine the icons into a single file. Because all the icon javascript files are made in such a way that they would still work perfectly fine if you'd bundle multiple files into one, without even needing to do any kind transformation. Here's the bluetooth icon file for example:

export const cilBluetooth = ["512 512","<path fill='var(--ci-primary-color, currentColor)' d='M150.627,376,232,294.627V496h38.627l120-120-120-120,120-120-120-120H232V217.373L150.627,136H105.373l120,120-120,120ZM264,54.627,345.373,136,264,217.373Zm0,240L345.373,376,264,457.373Z' class='ci-primary'/>"]

Okay, well if you already have an idea of how I could optimize this then I would really appreciate it.

0 Answers
Related