Home » vue » Failed to mount component on new laravel 5.5 project

Failed to mount component on new laravel 5.5 project

Posted by: admin November 26, 2021 Leave a comment

Questions:

me and a colleague have been having problems in new and old laravel projects when it comes to using vue js because we get the following error every time in the browser console

>`[Vue warn]: Failed to mount component: template or render function not defined.
found in
---> <Example>
       <Root>
warn @ app.js:32173
mountComponent @ app.js:34241
Vue$3.$mount @ app.js:39678
Vue$3.$mount @ app.js:41868
init @ app.js:35260
createComponent @ app.js:36909
createElm @ app.js:36852
createChildren @ app.js:36980
createElm @ app.js:36885
patch @ app.js:37394
Vue._update @ app.js:34147
updateComponent @ app.js:34271
get @ app.js:34614
Watcher @ app.js:34603
mountComponent @ app.js:34275
Vue$3.$mount @ app.js:39678
Vue$3.$mount @ app.js:41868
Vue._init @ app.js:36000
Vue$3 @ app.js:36085
(anonymous) @ app.js:802
__webpack_require__ @ app.js:20
(anonymous) @ app.js:775
__webpack_require__ @ app.js:20
(anonymous) @ app.js:63
(anonymous) @ app.js:66`

This happens even in new laravel projects using the default Example.vue

My current code is the following

Example.vue

<template>
    <div class="container">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <div class="panel panel-default">
                    <div class="panel-heading">Example Component</div>

                    <div class="panel-body">
                        I'm an example component!
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        }
    }
</script>

assets/js/app.js

/**
 * First we will load all of this project's JavaScript dependencies which
 * includes Vue and other libraries. It is a great starting point when
 * building robust, powerful web applications using Vue and Laravel.
 */

require('./bootstrap');

window.Vue = require('vue');

/**
 * Next, we will create a fresh Vue application instance and attach it to
 * the page. Then, you may begin adding components to this application
 * or customize the JavaScript scaffolding to fit your unique needs.
 */

Vue.component('example', require('./components/Example.vue'));

const app = new Vue({
    el: '#app'
});

assets/js/bootstrap.js

window._ = require('lodash');

/**
 * We'll load jQuery and the Bootstrap jQuery plugin which provides support
 * for JavaScript based Bootstrap features such as modals and tabs. This
 * code may be modified to fit the specific needs of your application.
 */

try {
    window.$ = window.jQuery = require('jquery');

    require('bootstrap-sass');
} catch (e) {}

/**
 * We'll load the axios HTTP library which allows us to easily issue requests
 * to our Laravel back-end. This library automatically handles sending the
 * CSRF token as a header based on the value of the "XSRF" token cookie.
 */

window.axios = require('axios');

window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

/**
 * Next we will register the CSRF Token as a common header with Axios so that
 * all outgoing HTTP requests automatically have it attached. This is just
 * a simple convenience so we don't have to attach every token manually.
 */

let token = document.head.querySelector('meta[name="csrf-token"]');

if (token) {
    window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
    console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}

/**
 * Echo exposes an expressive API for subscribing to channels and listening
 * for events that are broadcast by Laravel. Echo and event broadcasting
 * allows your team to easily build robust real-time web applications.
 */

// import Echo from 'laravel-echo'

// window.Pusher = require('pusher-js');

// window.Echo = new Echo({
//     broadcaster: 'pusher',
//     key: 'your-pusher-key'
// });

welcome.blade.php

<!doctype html>
<html lang="{{ app()->getLocale() }}">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="csrf-token" value="{{csrf_token()}}">
        <title>Laravel</title>

        <!-- Fonts -->
        <link href="https://fonts.googleapis.com/css?family=Raleway:100,600" rel="stylesheet" type="text/css">
        <link rel="stylesheet" type="text/css" href="/css/app.css">
        <!-- Styles -->

    </head>
    <body>

        <div id="app">
            <example></example>
        </div>
        <script type="text/javascript" src="/js/app.js"></script>
    </body>
</html>

webpack.mix.js

let mix = require('laravel-mix');

/*
 |--------------------------------------------------------------------------
 | Mix Asset Management
 |--------------------------------------------------------------------------
 |
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your Laravel application. By default, we are compiling the Sass
 | file for the application as well as bundling up all the JS files.
 |
 */

mix.js('resources/assets/js/app.js', 'public/js')
   .sass('resources/assets/sass/app.scss', 'public/css');

package.json

{
  "private": true,
  "scripts": {
    "dev": "npm run development",
    "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "watch-poll": "npm run watch -- --watch-poll",
    "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
    "prod": "npm run production",
    "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
  },
  "devDependencies": {
    "axios": "^0.16.2",
    "bootstrap-sass": "^3.3.7",
    "cross-env": "^5.0.1",
    "jquery": "^3.1.1",
    "laravel-mix": "^1.0",
    "lodash": "^4.17.4",
    "vue": "^2.1.10"
  },
  "dependencies": {
    "cross-env": "^5.0.5",
    "vee-validate": "^2.0.0-rc.17"
  }
}
Answers:

If you haven’t changed anything and and have correctly bundled and installed JavaScript then the issue may lie with the default Laravel implementation.

The error message you are receiving means that you are likely importing the runtime only build (vue without the template compiler) in to an app that needs the template compiler.

To better understand this, Vue compiles everything into render functions (essentially a pure JavaScript representation of your webpage). When using single file components you end up with one base component that you mount to your Vue instance, which serves up your all your views, so, we would get something like this:

components/App.vue

<template>
  <div>
    I'm a base component
    <!-- vue-router will mount components here -->
    <router-view></router-view>
  </div>
</template>

app.js

import App from './components/App.vue' 

// "h" is just a standard taken from JSX
new Vue({
  render: h => h(App)
}).$mount("#app");

app.blade.php

<html>
  <head>
    <!-- head stuff -->
  </head>
  <body>
    <div id="app"></div>
    <script src="app.js"></script>
  </body>
</html>

The important thing here is that app.blade.php only acts as a mounting point for your entire app and App.vue serves as the base component, which is turns serves every other view (this would usually be done via vue-router). To get that to work we need to compile our assets into app.js via webpack, which creates all our render functions for us, so we don’t need the compiler because everything has already been compiled. All that’s left to do is create a route in routes/web.php to serve up the index blade file. That’s essentially setting up an SPA.

What Laravel encourages you do, is add Vue components directly in your markup and register components globally, so you would do:

app.js

Vue.component('my-component', require('./components/My-component.vue'));

const app = new Vue({
    el: '#app'
});

index.blade.php

<html>
  <head>
    <!-- head stuff -->
  </head>
  <body>
    <div id="app">
      <my-component></my-component>
    </div>
    <script src="app.js"></script>
  </body>
</html>

Because we’ve added our component to the markup we need the template compiler to compile the bit between our app div tags into a render function at runtime. So, we need to import vue + compiler, which is what Laravel Mix should do for you by aliasing the runtime + compiler version of Vue (you can find details of how to do that in the docs).

To be honest, I’m not a fan of Laravel Mix because it abstracts away crucial implementation details that as a developer you need to know, such as, “is Laravel Mix correctly aliasing the Vue + compiler build”?, in your case it looks like it isn’t.

In the end it’s usually easier to just setup your own webpack config directly so you have total control over your config, you can use Vue’s webpack simple config as a base.

Once you’ve correctly setup webpack, you just then need to add the alias to webpack config and you have your runtime + compiler build in:

 resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    }
  }

I realise that I haven’t really given a soultion to your specific problem. I find it hard to believe that Webpack Mix isn’t correctly aliasing the vue + compiler build, but that is what that message implies. Hopefully though, this gives you enough information to find where the problem lies.