Can't register components the way I used to


#1

After updating my dependencies to their latest version I started getting runtime errors. I cannot be sure this is entirely an issue with Vue, as many dependencies were updated.

Here is the problem (runtime error):

[Vue warn]: Failed to mount component: template or render function not defined.

After investigating I tracked the problem to the way I am registering my application’s components. Here is the way I was doing it which was working fine prior to updating dependencies:

Vue.component('sidebar', require('./components/Sidebar'));

And I can fix the error by using either of these methods:

Vue.component('sidebar', require('./components/Sidebar').default);

OR

Import Sidebar from './components/Sidebar';
Vue.component('sidebar, Sidebar);

Can someone explain why this happened and how I can either get back to the old way I was doing it or perhaps show me a better way to register global components?


#2

That happens because of webpack, basically. This behaviour changed With Webpack 2, I believe. Or maybe it was related to Babel 5 vs. Babel 6? I can’t really remember. But here’s why:

To ensure interoperability between ES Modules (export), which can have a default export alongside named exports, and commonjs modules (module.exports), which don’t know/have this concept, ES modules are transpiled from this:

// default export
export default 'the default export'

// named export
export const someOtherThing = 'something else'

into something like this:

{
  __esModule: true, // flag to mark this as an ES module
  default: 'the default export'
  someOtherExport: 'something else'
}

When you import such a module, webpack sees the flag and import Syntax allows you to get access to all exports:

import TheDefault from './yourModule.js'
// => 'the default export'

import { someOtherThing }  from './yourModule.js'
// => 'something else'

However, when you require() such a module, to ensure the require() behaviour matches what commonjs specifies, it just gives you the whole object:

const theWholeThing = require('./yourModule')
/*
{
  __esModule: true, // flag to mark this as an ES module
  default: 'the default export'
  someOtherExport: 'something else'
}
*/

Otherwise there would be no way to get the named export someOtherExport:

// this is not how it works in reality!!
const TheDefault = require('./yourModule.js')
// => 'the default export'

const someOtherThing = ?? how would we get this?

So you have to do this:

const TheDefault = require('./yourModule.js').default

const SomeOtherThing = require('./yourModule.js').someOtherThing

So in short: whenever you require() an ES Module, you have to use require().default to get the default export.


#3

Thanks @LinusBorg. After posting the question I found this thread (weird that it never turned up in my previous searches) https://github.com/JeffreyWay/laravel-mix/issues/1833

I guess I switch to the ES module method, even though it is more verbose than I’d like.

Btw I noticed you’re super active on these forums and always have great support, thanks a lot for taking the time, you’re a stellar dude!