Vuejs: generate an error for non-existing components

234 Views Asked by At

I would like to generate an error, instead of a simple warning like this in the console when we are trying to use a non-existent component:

[Vue warn]: Failed to resolve component: nonexisting-component
If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement. 
  at <MainLayout onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref< undefined > > 
  at <RouterView> 
  at <App>

People sometimes break the flow and they don't notice it because the component just doesn't appear. Is it possible to do anything about it?

2

There are 2 best solutions below

0
On BEST ANSWER

For people struggling with this, here is a solution:

https://vuejs.org/api/application.html#app-config-warnhandler

You can use this handler to notify when a missing component is rendered. Warnings only work during development, so this config is ignored in production mode. In my case, since I use quasar, I made a plugin in src/boot, which receives the Vuejs app object, and added the following code:

  app.config.warnHandler = (msg: string, instance: ComponentPublicInstance | null, trace: string): void => {
    if (msg.includes('Failed to resolve component')) {
      alert(`⚠️ ${msg}\nNothing will be rendered at its place.\n${trace}`)
    }
  }

I don't know how to place something in the DOM instead of the object, but an alert is good enough. Saved plenty of lives since then

2
On

It's quite easy to solve this with a rollup plugin.

The plugin could be written right inside vite.config.js. Or move it to a separate module.

Here you use the rollup hook resolveId. The Vite/Rollup calls this hook when it cannot resolve an import. Than use the load hook to provide a fake component that would issue the error into the console and display it on the page:

enter image description here

import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
    plugins: [
        vue(),
        {
            resolveId(id) {
                if (id.endsWith('.vue')) {
                    return 'MissingPlaceholder.vue?id=' + id;
                }
            },
            load(id) {
                if (id.includes('MissingPlaceholder.vue')) {
                    const missing = id.match(/(?<=\?id=).+$/)[0];
                    return `
                        <script setup>
                            console.error('Component "${missing}" is missing!');
                        </script>
                        <template>
                            <div style="background:yellow;color:red">Component "${missing}" is missing!</div>
                        </template>
                    `;
                    
                }
            }
        }
    ],
    resolve: {
        alias: {
            '@': fileURLToPath(new URL('./src', import.meta.url))
        }
    }
})

App.vue

<script setup>
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
import MissingComponent from './components/MissingComponent.vue'

const src = 'x" onerror="alert(\'busted\')"';

</script>

<template>
    <header>
        <img alt="Vue logo" class="logo" :src="src" width="125" height="125" />

        <div class="wrapper">
            <HelloWorld msg="You did it!" />
            <MissingComponent />
        </div>
    </header>

    <main>
        <TheWelcome />
    </main>
</template>