vue icon indicating copy to clipboard operation
vue copied to clipboard

Imported components used with `<component :is="">` ignored in production

Open arpadgabor opened this issue 4 years ago • 8 comments

I'm using a custom component to render all my icons:

<template>
  <component :is="name" :weight="weight" />
</template>

<script lang="ts">
import { defineComponent } from 'vue'

import {
  PhCaretUp as Up,
} from 'phosphor-vue'

export default defineComponent({
  name: 'Icon',
  props: {
    name: { type: String, required: true },
    weight: { default: 'regular' },
  },
  components: {
    Up,
  },
})
</script>

It's all fine in development, but the production bundle ignores all icons. I've just switched from another icon pack that does not cause this (but the bundle size is huge) and right now this problem is pretty annoying.

I've also tried registering the global component in main.ts, to no avail.

Any suggestions are welcome.

arpadgabor avatar Dec 18 '20 12:12 arpadgabor

I'm noticing that even regular use of icons with v4.1.3 are not rendering in production builds. Gonna take a look this afternoon. @brlodi maybe you can weigh in on this?

rektdeckard avatar Dec 18 '20 17:12 rektdeckard

I’ll take a look this afternoon.

brlodi avatar Dec 19 '20 16:12 brlodi

The gist of the problem seems to be that the way the SFCs are being built by Rollup, the template is added as a side-effect, e.g. (the same structure exists in the bundled files, but it's easier to read the ESM version):

// dist/esm/components/PhActivity.vue.js
import component from './PhActivity.vue_vue&type=script&lang.js';
export { default } from './PhActivity.vue_vue&type=script&lang.js';
import { render } from './PhActivity.vue_vue&type=template&id=7b9995bc&lang.js';

component.render = render;

The problem is the library is marked as entirely side-effect free (that was my mistake) and so Vue CLI/Webpack is stripping out everything except the pass-through re-export when the consumer builds the project for production. The templates-turned-render-functions are just being entirely omitted, and trying to render a Vue component with no render function defined creates the empty HTML comment nodes you can see in the browser.

I'll need to do some testing to figure out the right combo of Rollup settings and sideEffects to keep everything tree-shaking without over-shaking. In the meantime, simply removing the "sideEffects": false in the Phosphor-Vue package.json will fix the missing icon problem, but it will also totally disable any meaningful tree-shaking. It's up to you @rektdeckard on whether to use that nuclear option for now.

Side-note, this appears to be a change in the Vue SFC compiler in Vue 3, and something I missed in testing. That's on me.

brlodi avatar Dec 19 '20 20:12 brlodi

Upon further digging, I believe that removing the erroneous "sideEffects": false is all that we need to do. Unfortunately I can't then reintroduce tree-shaking the "right" way, because based on vuejs/rollup-plugin-vue/issues/401 it seems like it might be currently impossible to publish a tree-shakable Vue 3 component library—considering the author of that issue report is as far as I can tell the preeminent authority on Vue library bundling.

brlodi avatar Dec 19 '20 22:12 brlodi

Using Vue3 and having the same issue -- icons are missing from production build. I tried removing the "sideEffects": false from the phosphor-vue package.json as mentioned above, however no change. I am implementing as <ph-instagram-logo />.

RyanPrentiss avatar Jan 25 '21 22:01 RyanPrentiss

Just wanted to check in and say I'm still watching this, but a proper fix with working tree-shaking is blocked upstream (actually in the Vue 3 core rather than the Rollup plugin, see https://github.com/vuejs/vue-next/issues/2860).

It's frustrating that Vue 3 launched with full tree-shaking advertised as a first-class feature, but seven months in it's still half-broken when it comes to anything more complex than the sample apps 😞

brlodi avatar Apr 15 '21 22:04 brlodi

@brlodi I've found that if you build a library with the preserveModules option set to true in Rollup, vue is able to treeshake the components. So it seems vue has trouble treeshaking as soon as we bundle all components into one dist file. This is implemented for the next major version release in the next branch.

dnlsndr avatar Feb 04 '23 11:02 dnlsndr

I have the same issue even in dev mode using nuxt 3.9. To fix this, I use an array with the components themselves instead of their names. And it's work fine.

<template>
  <div v-for="icon in icons">
    <component :is="icon" size="36"></component>
  </div>
</template>

<script setup>
import { PhFloppyDisk, PhMoonStars, PhListPlus } from "@phosphor-icons/vue";

let icons = [
  PhFloppyDisk,
  PhListPlus,
  PhMoonStars,
];
</script>

Delaylaph avatar Feb 03 '24 13:02 Delaylaph