Laravel mix vuejs router history mode - vuejs2

I am using laravel mix and vuejs for my app and everything works fine.
Now I tried to change the VueRouter to history mode:
const router = new VueRouter({
mode: 'history'
})
On the ngix server, I added the catch all rule:
location / {
try_files $uri $uri/ /index.html;
}
Because of that its still working, when accessing or refreshing top level urls like "/pages" or "/contact".
The only problem is with sublevel urls like /pages/1 or /foo/bar/boo. Clicking on a router link still works, but if I try to refresh the page on /pages/1 or if I try to directly access it (enter /pages/1 in browser) its not working, since the browser tries to access the assets from /pages/js/2.js instead of /js/2.js

Ok, I just found it out - I had to set the public path to an absolute path in my webpack.mix.js file:
mix.webpackConfig({
...
output: {
...
publicPath: "/"
}
});

Related

Catch-all route for production build of Vue Router/Vite-based SPA

I have a Vue3-based SPA using Vue Router and Vite as dev server. The app has three valid URL paths:
/
/first
/second
For production, it will be deployed on Apache under a prefix, i.e. the production URL paths will be:
/prefix
/prefix/first
/prefix/second
What I want to achieve is that clients should be redirected to a valid URL path (e.g. /prefix) of the application even when they initially request an invalid URL path, such as /prefix/invalid.
For this purpose, I've defined the following router.js:
import { createWebHistory, createRouter } from "vue-router";
const routes = [
{
path: "/first",
component: () => import("./components/First.vue")
},
{
path: "/second",
component: () => import("./components/Second.vue")
},
{
path: "/:anything+",
redirect: "/"
},
];
const router = createRouter({history: createWebHistory(import.meta.env.BASE_URL), routes});
export default router;
The third route entry defines a catch-all route that will match whenever clients request an invalid URL path. This works fine in development on Vite, i.e. when serving the app via npm run dev -- --base='/prefix/'. Even when the browser initially requests /prefix/invalid, the app's /prefix route is loaded and displayed.
When deploying with npm run build -- --base='/prefix/' for production, however, the catch-all route starts working only after clients have initially requested /prefix or /prefix/index.html and the SPA has been loaded. When they initially request /prefix/invalid or even /prefix/first or /prefix/second, Apache responds with 404 Not Found. This is of course because the whole routing is implemented client-side in JavaScript, so my catch-all route and everything else routing-related will only work once the SPA itself has been loaded and its JavaScript is executing in the browser.
My question is: is there a way to make initial requests for invalid URL paths work in production on Apache like in dev on Vite? I tried adding a file public/.htaccess like this:
ErrorDocument 404 import.meta.env.BASE_URL
But the expression import.meta.env.BASE_URL, which Vite statically replaces with the base config option value in router.js, is not replaced in this file - which is consistent with the documentation of the public directory. Hence this approach doesn't work.
Not sure whether I'm following the right path with .htaccess or whether my business problem has any better -possibly simpler- solution?
The problem can be solved by generating .htaccess dynamically using a simple Vite plugin, which I define inline in vite.config.js for simplicity.
// vite.config.js
import { defineConfig } from 'vite'
import vue from '#vitejs/plugin-vue'
// A private Vite plugin for generating .htaccess dynamically
function generateHtaccess () {
// Resolved Vite configuration, including "base" option
let viteConfig;
return {
name: 'generate-htaccess',
// Rollup Plugin API output generation hook
// See https://rollupjs.org/guide/en/#generatebundle
generateBundle() {
this.emitFile({
type: 'asset',
fileName: '.htaccess',
source: `ErrorDocument 404 ${viteConfig.base}\n`
});
},
// Vite Plugin API specific hook
// See https://vitejs.dev/guide/api-plugin.html#configresolved
configResolved(resolvedConfig) {
viteConfig = resolvedConfig;
},
};
}
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), generateHtaccess()],
});
A non-prefixed production build, such as npx vite build, will hence generate dist/.htaccess with the following content:
ErrorDocument 404 /
while specifying a prefix, e.g. npx vite build --base='/~user/' will generate:
ErrorDocument 404 /~user/
With this .htaccess, clients initially requesting any invalid URL path in production on Apache will be properly redirected to the same catch-all route -the app's root directory- as defined in router.js. This behavior will work consistently for any --base value supplied at build time.

Using connect-history-api-fallback' ,but not sure how to implement in vuejs

When i refresh the page in vuejs, i get nginx error for tht I am using connect-history-api-fallback' ,but not sure how to implement in vuejs , i see that its usage in app.js with middleware server,how do we use in a standalone app in vuejs
I think you could do it in 2 ways.
with just nginx (nginx configuration) (For full nginx config check this link)
location / {
try_files $uri $uri/ /index.html;
}
using express like server (server.js file with connect-history-api-fallback)
const express = require('express');
const history = require('connect-history-api-fallback');const app = express();
app.use(history());
app.use(express.static('src'));app.get('/', (req, res) => {
res.sendFile('src/index.html');
});app.listen(3000, () => console.log('server started'));
--in your router you need to use history mode, follow this vuejs history mode.
Follow this blog for reference https://medium.com/swlh/using-vue-routers-history-mode-6d9395e8122e

redirect default '/' to a specific path nuxt / netlify

Basically I want the root route '/' to be redirected to my '/home' route.
My Nuxt app is hosted on Netlify so I tried to do this is the _redirects file
/ /home
as per their redirect docs - but it's not working.
Now I know that in Vue in the router config you can set up redirects, but how do I achieve the same thing in Nuxt??
any help would be appreciated!
In your main index.vue page you would add:
<script>
export default {
middleware: 'redirect'
}
</script>
Then you would create a middleware to actually redirect to desired page, in middleware > redirect.js:
export default function ({ store, redirect }) {
return redirect('/home')
}

Vue - How to create 404 error page with 404 response code

How can I create 404 error page with 404 response code in Vue? Here is the route for 404.
{
path: "*",
name: "404",
component: load("404"),
alias: "/404"
}
You won't be able to set the HTTP status code in a Single-Page Application - all the routing is done on the same page in the browser so once the page is loaded no more HTTP status codes will be received.
However, if you try to load a non-existent route by directly typing/copying the URL into the address bar of the browser - then you can use nginX (or whatever server software you are using) to signal 404 HTTP status:
server {
error_page 404 /404.html;
location / {
try_files $uri $uri/ =404;
}
}
But this is not a practical/wise approach - basically, you want every non-existent path to be resolved to /index.html so that your SPA is always loaded and once loaded - it will detect that this route does not exist and will render your 404 component.
So your nginX config should look like this:
server {
...
location / {
try_files $uri /index.html$is_args$args;
}
}
The $is_args variable will add ? if $args is not empty while $args will provide the query parameters (if any).
Your route definition looks ok, except that I don't know your load function does, i.e. how it resolves to a Vue component. But in general your code follows the common approach as described in the Vue router docs. Usually you won't need a name or an alias here since this route is not used explicitly. Just put in a component that shows your "not found" content and you should be good to go:
{
path: "*",
component: PageNotFound
}
Since this is very close to the code you provided, please explain what exactly gives you a problem.
Like IVO GELOV wrote, you have to force this on the server. I came across the issue with Apache (IVO GELOV provides help on nginx).
In the vue router (Vue 3):
const routes = [
{
// Your other routes here
// ...
{
path: '/404',
name: '404',
component: Error404,
},
{
path: '/:pathMatch(.*)*',
beforeEnter() { window.location.href = "/404" },
},
]
With the last route item, all non-matched routes will be redirected to /404. I use beforeEnter to avoid creating a component and window.location.href to get out of the Vue application.
In Apache .htaccess:
<IfModule mod_alias.c>
Redirect 404 /404
</IfModule>
This will add a 404 error code to the /404 url.
Then the /404 route from the vue router config will call the Error404 component that you have to define and import like any other Vue component!
This answer is heavily inspired by:
https://stackoverflow.com/a/62651493/14217548

VueJs 2: Unable to render view when using history mode and route params

I am trying to set up SPA routes using history mode as follows:
{
mode: 'history',
routes: [
{
path: '/',
component: Home
},
{
path: '/articles',
component: ArticleList,
children: [
{
path: ':title',
component: ArticleView
}
]
}
]
}
As I am using the history mode routing on vue and on express application I've set up "express-history-api-fallback" as the last middleware in the pipeline:
const app = express();
const root = path.join(__dirname, '../client/dist');
app.use(express.static(root));
/* other middlewares */
app.use(fallback('index.html', {root: root}));
At the moment of a page reload, everything works fine. I.e. loading a url http://application/articles, opens correctly the view, BUT when I try to access the view that takes in a parameter, no matter what, the view does not get loaded and two requests are made to the express.
I.E. a request to http://application/articles/test will resolve into two requests. One to http://application/articles/test and another one to http://application/articles/app.[calculated-hash].js
As far as I understand, the first request fetches the index.html the other request fetches the bundled js script.
Also, on the express app, all routes to api start with 'api' prefix.
Question:
What is wrong with my setup/code using history mode and route params because no routes with parameters are loaded when trying to access them when entering url manually or hitting refresh?
Update:
Using connect-history-api-fallback produces the same results
The problem was with the script tag the webpack injects into the index.html
<script type="text/javascript" src="app.06503cbc260646a996d9.js"></script>
The src attribute value was missing a '/' prefix thus the resolution of files failed.
So to make it work, I've linked the src file like this:
<script type="text/javascript" src="/app.06503cbc260646a996d9.js"></script>
As I am using the webpack to bundle js files, on the webpack config I've added output.publicPath '/'
More info: connect-history-api-fallback bugtracker