vue.js – Import a Vue js component from Laravel Blade File

To further extend on Salim Example, you can add Vue to the window and directly create the exported Vue Component within the Vue File.



1) Autoload Vue Within Laravel Mix


webpack.mix.js

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

mix.autoload({vue: ['Vue', 'window.Vue']})
   .js(...)
   .css(...)
   .version()

2) Register Vue Components Globally As You Create Them


resources/js/components/profile/profile-image.vue

<template>
    <div class="profile-image" @click='show(user)'>
        <img :src="https://stackoverflow.com/questions/58121640/user.avatar" :alt="`${user.name} profile image`" />
    </div>
</template>

<script>
  /** Note: Global Component Registered Via Vue.component(...) **/

  Vue.component('profile-image', {   
     props: ['user'],

     methods: {
        /** 
         * Show User Profile Page
         */
         show(user) {
            const { location } = window;

            window.location = `${location.origin}/users/${user.id}`;
         }
      }
   });
</script>

3) Instead of requiring each component simply use Laravel Mix

webpack.mix.js

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

mix.autoload({ 
  vue: [
     'Vue', 
     'window.Vue'
  ] 
})
.js([
      /* --------------------------------- 
       |   Card Components
       | ---------------------------------
       |
       | . Card.vue (Original)
       | . IconCard.vue (Topic Contextually Relevant Icon)
       | . DetailCard.vue (Shown On Detail Pages & Used To Summarize Index Tables)
       |
      */
      'resources/js/components/cards/card.vue',
      'resources/js/components/cards/icon-card.vue',
      'resources/js/components/cards/index-card.vue',
      'resources/js/components/cards/detail-card.vue',
      'resources/js/components/cards/organization-card.vue',

      /* --------------------------------- 
       |   Button Components
       | ---------------------------------
       |
       | . Button.vue (Original)
       | . ButtonRipple.vue (Interactive Click Effects)
       | . ButtonFabIcon.vue (Rounded, Material Design Icons)
       |
      */
      'resources/js/components/buttons/button.vue',
      'resources/js/components/buttons/primary.vue',
      'resources/js/components/buttons/success.vue',
      'resources/js/components/buttons/button-ripple.vue',
      'resources/js/components/buttons/primary-ripple.vue',
      'resources/js/components/buttons/success-ripple.vue',
      'resources/js/components/buttons/button-fab-icon.vue',
      'resources/js/components/buttons/primary-fab-icon.vue',
      'resources/js/components/buttons/success-fab-icon.vue',



      /* --------------------------------- 
       |   Fields Components
       | ---------------------------------
       |
       | . Form.vue (Create & Update)
       | . Detail.vue (Show, Edit, & Cards)
       | . Index.vue (Tables Ex: Sort, Search, Filter)
       |
      */
      'resources/js/components/fields/date/form.vue',
      'resources/js/components/fields/date/index.vue',
      'resources/js/components/fields/date/detail.vue',

      /** Then that one component we actually created ;D **/
      'resources/js/components/profile/profile-image.vue',

], 'resources/js/components/bootstrap.js')


.babel([
      /* ------------------------------------------------------------------
       | Mounting Vue & Using "Babel" (Vanilla JS For Every Browsers)  
       | ------------------------------------------------------------------
       |
       | . Our Components are compiled
       | . Our Last File Being Added Will Mount Vue
       | . We'll Use ".babel()" While Adding This File
       | . "Babel" Simply Transforms All Javascript Into Vanilla JS
       |
      */
        'resources/js/components/bootstrap.js', 
        'resources/js/bootstrap/mount-vue.js'

], 'public/js/app.js')


/*------------------------------*/
/* Optimization Minification   
/*------------------------------*/
.minify('public/js/app.js');

/*------------------------------*/
/* Cache Busting Versioning   
/*------------------------------*/
if (mix.inProduction()) {
  mix.version();
}

4) Simplify Further By Extending Laravel Mix

resources/js/mix-extensions/mix-every-vue-component.js

import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'

const requireComponent = require.context(
  // The relative path of the components folder
  './components',
  // Whether or not to look in subfolders
  false,
  // The regular expression used to match base component filenames
  /Base[A-Z]w+.(vue|js)$/
)

requireComponent.keys().forEach(fileName => {
  // Get component config
  const componentConfig = requireComponent(fileName)

  // Get PascalCase name of component
  const componentName = upperFirst(
    camelCase(
      // Gets the file name regardless of folder depth
      fileName
        .split("https://stackoverflow.com/")
        .pop()
        .replace(/.w+$/, '')
    )
  )


  // Register component globally
  Vue.component(
    componentName,
    // Look for the component options on `.default`, which will
    // exist if the component was exported with `export default`,
    // otherwise fall back to module's root.
    componentConfig.default || componentConfig
  )
})

webpack.mix.js

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

class LaravelMixEveryVueComponent
{
    public constructor() {

    }

}
mix.autoload({ 
  vue: [
     'Vue', 
     'window.Vue'
  ] 
})
.js([
      /* --------------------------------- 
       |   Card Components
       | ---------------------------------
       |
       | . Card.vue (Original)
       | . IconCard.vue (Topic Contextually Relevant Icon)
       | . DetailCard.vue (Shown On Detail Pages & Used To Summarize Index Tables)
       |
      */
      'resources/js/components/cards/card.vue',
      'resources/js/components/cards/icon-card.vue',
      'resources/js/components/cards/index-card.vue',
      'resources/js/components/cards/detail-card.vue',
      'resources/js/components/cards/organization-card.vue',

      /* --------------------------------- 
       |   Button Components
       | ---------------------------------
       |
       | . Button.vue (Original)
       | . ButtonRipple.vue (Interactive Click Effects)
       | . ButtonFabIcon.vue (Rounded, Material Design Icons)
       |
      */
      'resources/js/components/buttons/button.vue',
      'resources/js/components/buttons/primary.vue',
      'resources/js/components/buttons/success.vue',
      'resources/js/components/buttons/button-ripple.vue',
      'resources/js/components/buttons/primary-ripple.vue',
      'resources/js/components/buttons/success-ripple.vue',
      'resources/js/components/buttons/button-fab-icon.vue',
      'resources/js/components/buttons/primary-fab-icon.vue',
      'resources/js/components/buttons/success-fab-icon.vue',



      /* --------------------------------- 
       |   Fields Components
       | ---------------------------------
       |
       | . Form.vue (Create & Update)
       | . Detail.vue (Show, Edit, & Cards)
       | . Index.vue (Tables Ex: Sort, Search, Filter)
       |
      */
      'resources/js/components/fields/date/form.vue',
      'resources/js/components/fields/date/index.vue',
      'resources/js/components/fields/date/detail.vue',

      /** Then that one component we actually created ;D **/
      'resources/js/components/profile/profile-image.vue',

], 'resources/js/components/bootstrap.js')


.babel([
      /* ------------------------------------------------------------------
       | Mounting Vue & Using "Babel" (Vanilla JS For Every Browsers)  
       | ------------------------------------------------------------------
       |
       | . Our Components are compiled
       | . Our Last File Being Added Will Mount Vue
       | . We'll Use ".babel()" While Adding This File
       | . "Babel" Simply Transforms All Javascript Into Vanilla JS
       |
      */
        'resources/js/components/bootstrap.js', 
        'resources/js/bootstrap/mount-vue.js'

], 'public/js/app.js')


/*------------------------------*/
/* Optimization Minification   
/*------------------------------*/
.minify('public/js/app.js');

/*------------------------------*/
/* Cache Busting Versioning   
/*------------------------------*/
if (mix.inProduction()) {
  mix.version();
}

4. Extend Laravel Mix Removing All Extra Steps

laravel-mix-autoload-vuejs-extension.js

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


const CollectFiles = (folder, files = []) => {
    const isFolder = to => File(path.resolve(to)).isDirectory();
    const CombineFiles = (Files, Segments = []) => [ ...files, path.join(__dirname, Segments[0], "https://stackoverflow.com/", Segments[1])];

    return fs.readdirSync(folder).reduce((filed, file) =>
            isFolder(`${folder}/${file}`)
                ? CollectFiles(`${folder}/${file}`, files)
                : CombineFiles(files, [folder, file]),
        files
    ).map(string => string.replace(__dirname, ''));
};


class LaravelMixAutoloadVue
{
    constructor()
    {
        this.LoadVueComponents = (to, output) => mix.js(CollectFiles(to), output);

        return mix;
    }

    dependencies()
    {
        return ['fs', 'path'];
    }

    name()
    {
        return ['vuejs'];
    }

    register(to, output)
    {
        if (typeof to === 'undefined') {
            return console.log(`Output is undefined for codesplit path ${to}`);
        }

        this.LoadVueComponents(to, output);
    }

    boot()
    {
        console.log("Booting Example");
    }
}

mix.extend('vuejs', new LaravelMixAutoloadVue());

webpack.mix.js
webpack.mix.js

const mix = require('laravel-mix');
require('./laravel-mix-autoload-vuejs`);

mix.autoload({ 
  vue: [
     'Vue', 
     'window.Vue'
  ] 
})
      /* -------------------------------------------------------------
       |  Laravel Mix Autoload Vue Extensions Handles All Components
       | -------------------------------------------------------------
      */
.vuejs('resources/js/components/', 'resources/js/components/bootstrap.js') 
.babel([
      /* ------------------------------------------------------------------
       | Mounting Vue & Using "Babel" (Vanilla JS For Every Browsers)  
       | ------------------------------------------------------------------
       |
       | . Our Components are compiled
       | . Our Last File Being Added Will Mount Vue
       | . We'll Use ".babel()" While Adding This File
       | . "Babel" Simply Transforms All Javascript Into Vanilla JS
       |
      */
        'resources/js/components/bootstrap.js', 
        'resources/js/bootstrap/mount-vue.js'

], 'public/js/app.js')


/*------------------------------*/
/* Optimization Minification   
/*------------------------------*/
.minify('public/js/app.js');

/*------------------------------*/
/* Cache Busting Versioning   
/*------------------------------*/
if (mix.inProduction()) {
  mix.version();
}

Leave a Comment