Incorporating a Vue application into a server-side framework application, encountering issues with Vue Public Path

Summary - Incorporating a Vue app into a Phoenix app. Constructing the Vue app with vue cli and ensuring the files are placed correctly within the Phoenix project directories. After building, there is an issue where the asset file paths do not align with the publicPath setting.

I have developed my vue app to be located at

../../lib/exampleapp_web/templates/page
. To accommodate this design, I am using a custom html template to eliminate Vue's default <html>,<head>, and <body> tags, as the Phoenix application already has a layout template that includes these.

The configuration in my vue.config.js file is as follows:

vue.config.js

module.exports = {
  outputDir: '../../lib/exampleapp_web/templates/page',
  assetsDir: '../../../../assets/static',
  indexPath: 'index.html.eex',
  publicPath: './', // adjusting the asset routes to be relative
  chainWebpack: config => {
    config.plugin('html').tap(args => {
      return [{
        template: "./public/index.html", // utilizing a custom Vue HTML template
        minify: false, // for easy comprehension
        inject: false // avoids injecting HTML elements like head, body, etc.
      }]
    })
  }
}

However, upon building the app, the rendered asset paths appear incorrect due to mismatched URLs. For example:

<script src="../../../../assets/static/js/chunk-vendors.74d8847d.js"></script>

But what I actually need is:

<script src="./js/chunk-vendors.74d8847d.js"></script>

To address this issue, I am performing a string replacement within the Vue HTML template:

public/index.html

<!-- https://github.com/jaketrent/html-webpack-template/blob/86f285d5c790a6c15263f5cc50fd666d51f974fd/index.html -->

    <% for (var css in htmlWebpackPlugin.files.css) { %>
        <link href="<%= htmlWebpackPlugin.files.css[css].replace('./../../../assets/static/','/') %>" rel="stylesheet">
        <% } %>
        <div id="app"></div>
        <% for (var chunk in htmlWebpackPlugin.files.chunks) { %>
        <script src="<%= htmlWebpackPlugin.files.chunks[chunk].entry.replace('./../../../assets/static/','/') %>"></script>
        <% } %>

This adjustment results in the correct rendering of asset paths:

    <link href="./css/chunk-vendors.257c6d34.css" rel="stylesheet">

    <link href="./css/app.d5864d1f.css" rel="stylesheet">

    <div id="app"></div>

    <script src="./js/chunk-vendors.74d8847d.js"></script>

    <script src="./js/app.b25a73d8.js"></script>

Although this solution works, it feels cumbersome to manually edit the template each time to replace the paths. Is there a more efficient approach?

I initially thought the publicPath option would resolve this (https://cli.vuejs.org/config/#publicpath), but unfortunately, it does not seem to be effective in my case.

By default, Vue CLI assumes your app will be deployed at the root of a domain, e.g. https://www.my-app.com/. If your app is deployed at a sub-path, you will need to specify that sub-path using this option. For example, if your app is deployed at https://www.foobar.com/my-app/, set publicPath to '/my-app/'

Answer №1

Discover a more efficient solution by disabling the generation of HTML and turning it into a static file template within the backend app. Specify the asset file names for JS and CSS, ensuring they are output to your designated folder. Although most of this configuration was inspired by a blog post, unfortunately, the source is now forgotten...

vue.config.js

const assetsDir = './../../../assets/static'
// Customized config to disable HTML and generate static assets
// All static assets will be stored in assetsDir.
// Remember to add assetsDir to each filename since it does not cascade down
// Each file is assigned a name, which may affect cachebusting if applied
// A static HTML file is created to load our paths
module.exports = {
  assetsDir,
  configureWebpack: {
    output: {
      filename: assetsDir + "/js/my-file.js",
      chunkFilename: assetsDir + "/js/my-file-chunk.js"
    }
  },

  chainWebpack: config => {
    if (config.plugins.has("extract-css")) {
      const extractCSSPlugin = config.plugin("extract-css");
      extractCSSPlugin &&
      extractCSSPlugin.tap(() => [
        {
          filename: assetsDir + "/css/my-file.css",
          chunkFilename: assetsDir + "/css/my-file-chunk.css"
        }
      ]);
    }

    config.plugins
      .delete("html")
      .delete("prefetch")
      .delete("preload");
  }
}

The advantage of this method is that during the build process, your files are automatically placed in the correct directory without the need to manually create HTML templates. Simply focus on building your server-side template as usual. This approach is applicable across various integrations, not limited to just Phoenix.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is the function failing to return a value?

I'm facing an issue with a certain piece of code that doesn't seem to be working as expected. Essentially, I have an array containing the accepted file types for a specific component. The code is designed to iterate over this array and check if t ...

Rendering HTML strings instead of HTML in ReactJS

When I try to display HTML, all I see is strings var ref = firebase.database().ref('raffle/'); ref.on('value', (snapshot) => { var content = ``; var IDwrapper = document.getElementById('raffleFeed'); snapshot.forEac ...

What is the inner workings of stream.Transform in Node.js?

Recently, I stumbled upon a code snippet on a blog showcasing the usage of the stream Transform class to modify data streams and display the altered output. However, there are certain aspects of this code that leave me puzzled. var stream = require(&apos ...

A guide to replicating HTML using AngularJS

I am attempting to replicate HTML content using AngularJS. While I was successful using jQuery, it caused conflicts with Angular. Therefore, I aim to achieve the same result using AngularJS. Here is the code I have written: function printContent(el){ ...

Calculating the number of days between two given dates, including both the start and end dates

I need to calculate the number of days between two dates, including both of the dates. My current method for this task is as follows: numDaysBetweenDates(startDate, endDate) { let millisecondsPerDay = 24 * 60 * 60 * 1000; return (endDate - startDate) ...

How come the transition does not take effect when removing and adding the class to the same element with the removeClass() and addClass() methods?

Two images are present, with the first one having the class "opacityOne". When a button is clicked, based on the variable index, I want the current image to fade in while the other fades out. It works well when I remove the "opacityOne" class from one ima ...

"Integrating Laravel 5.4 Backend with Angular 5 Frontend: A Step-by-Step

Currently, I am immersed in a project that involves creating a frontend using Angular 5 and backend business logic using Laravel 5.4 with MySQL Database. As someone new to this technology stack, I find myself grappling with establishing the data flow conne ...

Add a class by utilizing the :class attribute

I reviewed the documentation, but I am still struggling to implement this in my project. Initially, I simply want to display a specific class using vue.js that I can later modify dynamically. I just need to establish the default behavior of that class, so ...

Retrieving information from Node.js Serialport

I am interested in reading the data received after sending an ascii command to my lock controller. Here is the code that sends the command to the lock controller: var express = require('express'); var router = express.Router(); var SerialPort = ...

Tips for successfully sending an interface to a generic function

Is there a way to pass an interface as a type to a generic function? I anticipate that the generic function will be expanded in the future. Perhaps the current code is not suitable for my problem? This piece of code is being duplicated across multiple fil ...

In angular, concealing the pagination bar can be achieved when the quantity of pages is lower than the items per page count. Let's delve into

I am facing an issue where I need to hide the pagination bar if the number of pages being rendered is less than the items per page. I attempted to use ng-show but it was not successful. <tr ng-repeat="row in allItems"> ...

Smoother transitions with spline curves in Three.js

My current project involves drawing a CubicBezierCurve3 curve in three js. However, I want to enhance the drawing process by visualizing it as a moving rocket leaving behind a gas trail. Rather than having the entire curve drawn at once, I aim to draw it p ...

Why is it that servlets are unable to send custom JSON strings, and why is it that Ajax is unable to receive them?

I have developed a servlet that responds with a simple JSON list: public void addCategory(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { logger.log(Level.INFO, "Adding the category"); ObjectifyS ...

After selecting a subitem, the side menu will automatically close

I designed a side menu that expands to reveal submenus. However, when I click on a subitem within a submenu, the entire list closes. The HTML, CSS, and JavaScript code all appear to be correct as the template is functioning properly. But as soon as I ins ...

Is there a clash between jquery_ujs and Kaminari AJAX functionality in Rails 4?

After some investigation, it seems that there is a conflict between jquery_ujs and Kaminari's AJAX support in my Rails 4 application. Within my application.js file, I have included the following: //= require jquery //= require jquery_ujs //= require ...

Encountering a Babel error while attempting to compile my front-end assets using Laravel Mix. The module build process fails, causing

I've been struggling to fix this persistent error, trying various solutions from different sources including Git, but nothing seems to work. Error: Module build failed: this.setDynamic is not a function In my package.json file, I have the following ...

Exploring the implementation of window.addEventListener within an Angular project

I am currently working on testing a method in Angular using Jasmine. However, I am running into an issue with triggering the mouse event (specifically when the browser back button is clicked). Below is the code snippet I'm working with: navigate() { ...

Tips for adding a bounding box to an image received from the server

I've got a python server that is returning a collection of bounding boxes post OCR processing (using Tesseract or similar). "bbox": [{ "x1": 223, "y1": 426, "x2": 1550, &q ...

Encountering a CORS issue while attempting to retrieve a token from Keycloak using Vue.js and Axios

When trying to access a Keycloak instance with Axios in my Vue.js app, I encounter a CORS error. Could someone please assist me with this issue? (Interestingly, when I send a POST request from POSTMAN to my Keycloak, it works fine). Here is the code I am ...

Animate an element when switching routes

Is there a way to smoothly transition an SVG element across a page when the route changes in Vue.js? I've attempted to set up a watcher that triggers an animation based on the route path conditions. Although the transitionName updates correctly, the ...