robcresswell / vue-material-design-icons

Material Design Icons as Vue Single File Components
https://npmjs.com/vue-material-design-icons
MIT License
162 stars 35 forks source link

Support for a single component that receives the icon name as a prop #280

Closed EvandroLucas closed 1 year ago

EvandroLucas commented 2 years ago

It would be nice if I could do something like:

<mdi-icon name="arrow" /> 

And have only this mdi-icon component imported globally. This helps specially if I need to somehow dynamically change the icon.

robcresswell commented 2 years ago

This breaks tree shaking; it means that all components need to be available at runtime, I think

EvandroLucas commented 2 years ago

This breaks tree shaking; it means that all components need to be available at runtime, I think

Well, I don't know, a bunch of other icon plugins have implemented this functionality and it works quite well.

robcresswell commented 2 years ago

I'm curious how build tools can handle that without making their bundles huge? Maybe they're more advanced that I thought. I wouldn't expect they would know how to figure out what icons to drop by arbitrary strings.

EvandroLucas commented 2 years ago

Well, apparently they do load everything on memory, sometimes. I don't know how Vuetify's v-icon or BootstrapVue's b-icon work internally, there's more complexity involved there, but this plugin right here seems to just declare every single icon as an svg in a 30KB file and that's it.

hrishikesh-k commented 2 years ago

I tried creating my own dynamic component in Vite like:

<script
  setup
  lang = "ts">
  import {computed, defineAsyncComponent} from 'vue'
  const iconProps = withDefaults(defineProps<{
    name : string
  }>(), {
    name: null
  })
  const dynamicIcon = computed(() => {
    return defineAsyncComponent(() => {
      return import(`../../node_modules/vue-material-design-icons/${iconProps.name}.vue`)
    })
  })
</script>
<template>
  <component
    v-bind:is = "dynamicIcon"/>
</template>

White it includes all files in a separate folder based on my Vite config:

image

it also includes the "paths" of all these files in the index.js file - which is why the size went to over 1 MB. So bundle size is definitely taking a hit here. Not to mention, my build time rose exponentially too.

hrishikesh-k commented 2 years ago

Note that, this is how it's working even without the library. I tried using @mdi/svg directly using vite-svg-loader. Unless I manually copy the required icons into a separate folder, it will include all the icons in the chunks folder. That part is fine, but the bundle size increased due to:

image

... Vite embedding all paths in the JavaScript file. The bundle size is lower in @mdi/svg than vue-material-design-icons though.

I got 821 KB without GZip as compared to 1313 KB.

robcresswell commented 2 years ago

@EvandroLucas 30kb seems like a pointless hit if you're using a single icon, to me 🤷🏻 That icon library isn't particularly big either; MDI would be hundreds if in a single file I think.

@Hrishikesh-K Yeah, isn't this an argument for single components then?

hrishikesh-k commented 2 years ago

Yup, I think the best way is to manually copy all required SVGs to assets and use a component like I shared above to load the icons that one needs. This is possibly the only way to keep bundle size to the minimum.

robcresswell commented 1 year ago

Closing this. I don't think anything suggested here is beneficial over slightly more import lines. They all add significant build time (or worse, runtime) slowdowns and complexity.

charleston10 commented 1 year ago

It's simple

import * as icons from '@mdi/js'

icons[iconName]
icons['mdiEye']
robcresswell commented 1 year ago

@charleston10 Yeah, but I think in that case you've just imported every single icon into your final build, which will inflate it a lot.