Simplifying Imports with Path Aliases & Global Components

In this lesson, we'll learn how to add a path alias with Webpack Encore to eliminate relative paths from our client-side imports. We'll also learn how to register global Vue 3 components.

Published
Sep 18, 22
Duration
8m 31s

Developer, dog lover, and burrito eater. Currently teaching AdonisJS, a fully featured NodeJS framework, and running Adocasts where I post new lessons weekly. Professionally, I work with JavaScript, .Net C#, and SQL Server.

Adocasts

Burlington, KY

In this lesson, we’ll learn about two different things. First, we’ll learn how to register a global component within our Vue 3 Inertia application. Since links are fairly common in any application we’ll be taking the Inertia Link component and registering it globally.

Then, we’ll learn how we can simplify our imports using Webpack Encore and path aliases. This will allow us to use a single reference point when importing client-side files inside our Vue 3 application instead of relying on relative paths.

Global Components

In order to register a global component, we’ll want to jump inside our client-side app.js file at resources/js/app.js. At the top of the file let’s add our import, in this case we’ll import the Inertia Link component as shown below.

import '../css/app.css'
import { createApp, h } from 'vue'
import { createInertiaApp } from '@inertiajs/inertia-vue3'
import DefaultLayout from './Layouts/Default.vue'
import { Link } from '@inertiajs/inertia-vue3' // 👈 import the Link component

createInertiaApp({ ... })

If you recall back to a few lessons ago, you’ll remember createApp and the methods chained off of it are the portions of the below actually creating our Vue 3 application, everything else is Inertia-based.

So, all we need to do to register a global Vue 3 component is chain off the createApp function the component method. Just note, you’ll want to call component before mount is called.

import { Link } from '@inertiajs/inertia-vue3'

createInertiaApp({
  resolve: name => { ... },
  setup({ el, App, props, plugin }) {
    createApp({ render: () => h(App, props) })
      .use(plugin)
      .component('inertia-link', Link) // 👈
      .mount(el)
  },
})

The first argument of the component method is the name the component will be registered to. The second argument is for the component itself. Once that’s set, the component is now globally registered and we can now replace all our links with our globally registered inertia-link component.

First, let’s jump into our home page and update the Link component there to our globally registered inertia-link.

<template>
  <!-- resources/js/Pages/Home.vue -->
  <div>
    <!-- <Link href="/login">Login</Link> -->
    <inertia-link href="/login">Login</inertia-link>
    <h1 class="text-red-500">Testing, {{ testing }}</h1>
    <n-button>Testing</n-button>
    <n-input :value="testing"></n-input>
  </div>
</template>

<script>
  // import { Link } from '@inertiajs/inertia-vue3'
  export default {
    props: {
      testing: String
    },
    // components: {
    //   Link,
    // }
  }
</script>

As you can see above, all we need to do is remove our Link import and component registration from our component. Then swap it out within the template with our inertia-link component.

Let’s do the same on our Login page.

<template>
  <!-- resources/js/Pages/Auth/Login.vue -->
  <div>
    <!-- <Link href="/home">Home</Link> -->
    <inertia-link href="/home">Home</inertia-link>
    <label>Login</label>
    <a href="/home">Anchor Home</a>
  </div>
</template>

<script>
  import AuthLayout from '../../Layouts/Auth.vue'
  export default {
    layout: AuthLayout
  }
</script>

<!-- 
  <script setup>
    import { Link } from '@inertiajs/inertia-vue3'
  </script>
>

In this case, since we’re using a setup script with nothing else going on, we can just remove the setup script and swap out the component in the template.

Simplifying Imports with Path Aliases

Next, let’s get our path alias set up so that we don’t need to rely on relative paths to import components, but instead can reference our alias.

For example an import like this:

import AuthLayout from '../../Layouts/Auth.vue'

Can be simplified to the below using a path alias.

import AuthLayout from '@/Layouts/Auth.vue'

This has a couple of benefits.

  1. First, when we go to import a file, we won’t need to stop and think about the depth level we’re currently in nor the depth level the component we need is in.

  2. Second, if we were to copy/paste the import using the relative path we’d need to update the path if our nested level changed at all in comparison to the imported file. Using a path alias we can copy/paste without needing to update our path regardless of where we paste the import too.

Add the Path Alias

First things first, if you have your server running, go ahead and stop it as we’ll need to update our webpack.config.js to add the alias. Then, go ahead and open your webpack.config.js file.

At the top, go ahead and add an import for resolve from the path package.

const { join, resolve } = require('path') // 👈 add import for resolve
const Encore = require('@symfony/webpack-encore')
const Components = require('unplugin-vue-components/webpack')
const { NaiveUiResolver } = require('unplugin-vue-components/resolvers')

Then, scroll on down to where we have our Vue Loader config, around line 186. Here we’ll add our alias using the Encore.addAliases method.

Encore.enableVueLoader(() => {}, {
  version: 3,
  runtimeCompilerBuild: false,
  useJsx: false
})

Encore.addPlugin(Components({
  resolvers: [NaiveUiResolver()]
}))

// 👇
Encore.addAliases({
  '@': resolve(__dirname, 'resources/js')
})

This method accepts an object. The key of the object is the aliased character, which we’ll use '@' here. The second is the path we want this alias to reference. The best place for this will be our resources/js directory as this will allow us to reach for our pages, layouts, and components easily within our Vue application.

At this point, you can go ahead and restart your server if you wish.

Using Our Alias

Lastly, let’s go ahead and make use of our new alias! So, jump back into your Login page, and let’s replace the AuthLayout import from ../../Layouts/Auth.vue to @/Layouts/Auth.vue.

<template>
  <!-- resources/js/Pages/Auth/Login.vue -->
  <div>
    <inertia-link href="/home">Home</inertia-link>
    <label>Login</label>
    <a href="/home">Anchor Home</a>
  </div>
</template>

<script>
  import AuthLayout from '@/Layouts/Auth.vue' // 👈
  export default {
    layout: AuthLayout
  }
</script>

Cleaning Up Our Project

Before we end this project, let’s go ahead and do some cleanup within our application to get ready for the next set of lessons. Most of what we have in our project so far is testing to ensure everything is set up correctly, so let’s go ahead and clean all that out.

Let’s start by creating a header so we can easily navigate from page to page.

Creating Our Header

Let’s create it at resources/js/components/Header.vue

<template>
	<!-- resources/js/components/Header.vue -->
  <div class="bg-slate-100 py-3 px-6 flex justify-between items-center mb-6">
    <div class="flex items-center space-x-6">
      <h3 class="font-bold">AdonisJS InertiaJS Example</h3>
      <nav class="flex items-center text-sm">
        <inertia-link href="/app">Home</inertia-link>
      </nav>
    </div>

    <div class="flex items-center space-x-6 text-sm">
      <inertia-link href="/app/login">Login</inertia-link>
      <inertia-link href="/app/register">Register</inertia-link>
    </div>
  </div>
</template>

Here we have three inertia-link usages one for our home page, one for our login page, and a last for our register page that we’ll create a little later on. You may also notice all three href values are prefixed with /app.

Updating Our Routes

We’ll keep everything Inertia-based under the /app path prefix and everything Edge-based outside of /app. So, let’s jump into our routes and update it to the below.

// start/routes.ts

Route.get('/', async ({ view }) => {
  return view.render('welcome')
})

Route.group(() => {

  Route.get('/', async ({ inertia }) => {
    return inertia.render('App', { 
      testing: 'this is a test'
    })
  })
  
  Route.get('/login', async ({ inertia }) => {
    return inertia.render('Auth/Login')
  })

  Route.get('/register', async ({ inertia }) => {
    return inertia.render('Auth/Register')
  })

}).prefix('app')

We’re renaming home to app. You’ll also want to update the Home.vue filename to App.vue as well. Then, we’ll wrap our app, login, and register routes inside a group that’s prefixed with app. Lastly, we’re adding a route for our register route. We’ll make some changes to our login page in a moment, then copy/paste that file to create our register page.

Applying Our Header To Our Layouts

In addition to applying our header to our layouts, we’ll also replace the placeholder text we have stating what layout we’re in. Let’s also wrap our slotted content in a px-6 to match the padding within our header.

<template>
  <!-- resources/js/Layouts/Default.vue -->
  <div>
    <Header />
    <main class="px-6">
      <slot></slot>
    </main>
  </div>
</template>

<script setup>
  import Header from '@/components/Header.vue'
</script>

And the same for our AuthLayout. At this point our AuthLayout will match our DefaultLayout, but we’ll change that in one of the upcoming lessons so we’ll keep them separated.

<template>
  <!-- resources/js/Layouts/Auth.vue -->
  <div>
    <Header />
    <main class="px-6">
      <slot></slot>
    </main>
  </div>
</template>

<script setup>
  import Header from '@/components/Header.vue'
</script>

Cleaning Our Pages

Next, let’s clean out our pages of the “test” elements and components we’re using.

First our App page.

<template>
  <!-- resources/js/Pages/App.vue -->
  <div>
    <h1>Welcome</h1>
  </div>
</template>

<script>
  export default {
    props: {
      testing: String
    },
  }
</script>

Then, our Login page.

<template>
  <!-- resources/js/Pages/Auth/Login.vue -->
  <div>
    <h1>Login</h1>
  </div>
</template>

<script>
  import AuthLayout from '@/Layouts/Auth.vue'
  export default {
    layout: AuthLayout
  }
</script>

Lastly, let’s copy/paste our Login page and rename the pasted version to Register.vue to serve as our registration page. Then, update the h1 for that page to Register.

<template>
  <!-- resources/js/Pages/Auth/Register.vue -->
  <div>
    <h1>Register</h1>
  </div>
</template>

<script>
  import AuthLayout from '@/Layouts/Auth.vue'
  export default {
    layout: AuthLayout
  }
</script>

Next Up

That should do it for now! In the next lesson, we’ll build off the clean-up we just did by learning about Inertia and how forms work within it by working on our registration form.

Join The Discussion! (6 Comments)

Please sign in or sign up for free to join in on the dicussion.

  1. Commented 1 year ago

    Hi Tom,
    Thank you for sharing this awesome guide with us.

    What would be solution for rendering assets (images) inside Vue component using
    AdonisJS assets-manager, similar to asset helper?

    Thanks.

    1

    Please sign in or sign up for free to reply

    1. Commented 1 year ago

      Hi fearless!
      Thanks for watching/reading!

      You should be able to just plop the images you need inside the public directory, something like /public/images/yourawesomeimage.jpg, then just drop /public to use it inside your Vue components.

      <img src="/images/yourawesomeimage.jpg" />
      0

      Please sign in or sign up for free to reply

  2. Commented 1 year ago

    Hi.

    Do you know how to get rid of the following warning with Volar and the <script> tag in vue files : `Virtual script not found, may missing <script lang="ts"> / "allowJs": true / jsconfig.json.`.

    If I want to use TypeScript by adding `lang="ts"` I have a compile error.

    I noticed you also had this warning in previous videos and now you don't.

    Great content as always sir.

    1

    Please sign in or sign up for free to reply

    1. Commented 1 year ago

      Hi Jean! I unfortunately am not sure. I believe I'm still using Vetur instead of Volar for Vue in VSCode. Maybe Vetur updated and patched this?

      0

      Please sign in or sign up for free to reply

      1. Commented 1 year ago

        Vetur is still being updated?

        I don't have autocompletion either. It's frustrating.

        If I put "allowJs: true" in tsconfig.json like suggested, it works but I feel like this is not the best solution.

        Edit : I found another solution. Add an empty jsconfig.json file inside js directory. What do you think ?

        0

        Please sign in or sign up for free to reply

        1. Commented 1 year ago

          I'm honestly not sure whether Vetur is still being supported. I know the consensus from the core team was that Volar is the preference going forward, but I just haven't switched yet as Vetur is working fine for me.

          Yeah - if adding an empty jsconfig.json is working okay for you that may be a valid solution. I know there was an instance where I needed to add a separate tsconfig.json to get things working as the Adonis namespaces conflicted with the local front-end TypeScript.

          0

          Please sign in or sign up for free to reply