How to set vue 3 with router from subfolder - vue.js

I installed fresh vue 3 app with cli, containing also vue router.
On my web I have put builded files into www/dist sub folder. Then in my php index file I linked all js/css files to /dist folder manually.
I have an image in component
<img class="discord-icon" src="#/assets/discord.png">
When I set publicHtml = '/dist' , everything works even image is shown, but for some reason my homepage gets redirected from original "www.page.com" to "www.page.com/dist"
I would like to have default "www.page.com" at homepage but also with working image, how can I achieve that ?
Edit :
My router config (default from Manual cli install with router)
const routes = [
{
path: "/",
name: "Home",
component: Home
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
});
UPDATE :
On vue discord I got adviced to create .htaccess for this, sadly I am not skilled enough to create one, or even know to which folder store it. Also I had another one already in there since I am using php framework (Nette)
This is how I got it working for now :
"publicPath: '/dist'" and "history:
createWebHistory(process.env.BASE_URL)" where project is in "/dist"
folder, so page goes www.page.com/dist. All work but. I go to
"www.page.com" it redirects to "/dist" but when look in html it does
not generate "/dist/css/chunk-vendors.css" so all imported css is
missing. But when I hit refresh (going directly to www.page.com/dist)
chunk-vendors.css is loaded. By default vendors.css is not in
generated index.html so I guess its done by js. So i got evil thought
of just paste that vendors.css link manually to my production
index.html aand it works, althought when going to /dist url directly I
end up with loading this style 2 times lol. Then I got even more evil
thought and changed to 'history: createWebHistory("/")' , rebuilded
for production and now i have nice www.page.com and even all imported
styles are working (with that style link pasted in manually), just
have some feeling I will burn in hell.

Here is a little summary of how a vue-cli project usually gets built and pushed to a server. and what kind of config is important and what 's their purpose. hope it gives you an idea of how to setup your project.
Building with Vue Cli
This is how it works in a regular project to build with vue-cli:
vue.config.js
module.exports = {
publicPath: process.env.NODE_ENV === 'production' ? '/final-sub-folder' : '',
// ...
}
The above config sets a different site URL for dev and prod. The prod /final-sub-folder means that on the final URL in your distant server this sub-folder will be present (E.g. https://example.com/final-sub-folder).
when building with Vue Cli npm run build the output goes into /dist folder (by default) and the build is set for prod by default (so process.env.NODE_ENV = 'production').
Now, if you try to go in that dist folder in your localhost (on Apache for instance), the vue-router using the same publicPath will fail because on your localhost your-proj/dist is a different base URL than /final-sub-folder as set in the config. So it will only work in the final server where the base url can be matched.
Setting up vue-router
Your vue-router should always be set to (this config is for vue-router#next - for Vue 3):
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
So that it follows the base URL you set in a single place, in your vue config.
(notes: the createWebHistory removes the # in the URLs)
Adding a .htaccess
Regarding the .htaccess, it is a common practice to set up redirections in it so that on your final server, accessing https://example.com/final-sub-folder/a-sub-page directly will be redirected to your index.html that can load the vue-router which will handle the route matching of what you entered in the URL.
https://router.vuejs.org/guide/essentials/history-mode.html#example-server-configurations
Without this .htaccess, the Vue router cannot be reached from a sub-folder since it does not exist on the server (it's a virtual route using rewriting engine), it would result in a not found URL.
Here is a simple example of .htaccess:
Options +FollowSymlinks
Options -Indexes
RewriteEngine On
RewriteRule \.(js|css|map|jpe?g|svg|gif|png|eot|ttf|woff2?)(\?.*)?$ - [NC,QSA,L]
RewriteRule .* index.html [NC,QSA,L]
What is important in that is the last line redirecting anything to index.html (which is generated from your vue-cli build).
The previous line allows any asset file to not be redirected (no asset file should be handled by vue-router).
Hope it helps you understand! ;)

Related

NextJS doesn't create index.html for subfolders in static export

I made a fully static website using NextJS, exported it and I'm hosting it on S3 using static website hosting. I can click around and successfully view all the pages, including example.com/blog. However if in the browser I click refresh, or enter example.com/blog directly, I get a 404 Not Found error.
When viewing the exported files, I see that /blog/ has no index.html file, even though there should be (in my opinion) since in the original source files I have a /blog/index.ts file, and when in dev mode I can refresh localhost/blog or enter it directly and it works as expected.
In summary, I believe NextJS should create a /blog/index.html file but it doesn't. Is there any way to force this? Am I doing something wrong? Thank you!
To generate an index.html file when exporting to static HTML, enable the trailingSlash setting in your next.config.js:
module.exports = {
trailingSlash: true,
}
./out/blog.html should now become ./out/blog/index.html after the export.

Using NuxtJS for dynamic routes without server target

I always thought that frontend should not be over bloated in size, usually by "frontend" I imagined a set of HTML, CSS and JS files, which are kind of small, especially when minified and compressed. So you can use whatever framework or library you love, your dev node_modules could be enormous in size, but after the compilation you get something lightweight to be served e.g by Nginx. Yeah, I just described an SPA-like setup, not an SSR when there's a server process running.
I had an experience building a website with NuxtJS, and it has only runtime logic, so no backend was required. I just did yarn generate and served all the resulted static with Nginx.
Now I'm building an application which requires a backend (it's a separate Python process), with dynamic pages like /users/john and /users/jane. Nuxt documentation says I can't use generate option anymore, cause such routing is dynamic. (technically I can write a set of fetch functions to load users from API during build time and generate corresponding pages, but it doesn't work well for runtime data). The only option is to use server target of NuxtJs.
There's even a page describing how to serve Nuxt application with Nginx. It assumes you should use yarn start command, which starts a Node process. It works fine, dynamic content is routed, caching is performed by Nginx, but.. it doesn't fit in a model that "frontend is lightweight". I use docker, and it means that now I need to bring huge node_modules with me. nuxt package itself is about 200 MB, which is kinda big for a small frontend app. I can run yarn install --production to save some space, but it still doesn't solve an issue that resulted image is huge.
Previously, when I wrote some apps in React, they resulted in a single index.html which I served by Nginx. It means, such dynamic routing was handled by frontend, using react-router or similar stuff.
To better understand my concerns, here's some rough comparison:
My old React apps: ~5 MB of disk space, 0 RAM, 0 CPU, routing is done by index.html file
My previous site with Nuxt static option: ~5 MB of disk space, 0 RAM, 0 CPU, routing is done by file system (/page1/index.html, /page2/index.html)
My current site with Nuxt server option: ~ 400 MB or even more disk space for a docker image, RAM, CPU, routing is done by Nuxt runtime
I don't really want to overcomplicate things. Allocating a lot of resources for a simple web app is too much, especially when you can solve the task with a help of a few static files.
The questions are:
Am I missing some option in NuxtJS to solve my issue?
Am I just misusing NuxtJS, and it's better to get plain VueJS, some vue-router, and develop the app as I described in "previously with react" section?
I think you are making a mistake here about SPA mode.
Assume that you have a page named users in your Nuxt pages, your folder structure is like this:
[pages]
[users]
[_name]
index.vue
When you requesting /users/john you can take the john from params and making an axios call to your server.
After that, you can use the nuxt generate command to create your dist folder and after that serve the dist folder with Nginx. Everything will work fine.
Check this simple routing approach in the browser
const routes = {
'/': Home,
'/users': Users
}
new Vue({
el: '#app',
data: {
currentRoute: window.location.pathname
},
computed: {
ViewComponent () {
return routes[this.currentRoute] || NotFound
}
},
render (h) { return h(this.ViewComponent) }
})
In the Users component integrate with your python backend.
You can use SPA mode in NuxtJS (mode: 'spa', or ssr: false, in latest versions), then run yarn generate or nuxt generate, it will generate a full bundle in dist folder that can be served with any HTTP server.
This works fine with all dynamic routes, I tested it with simple php -S localhost:8000 that's just serves a folder via HTTP.
This works due to a trick with 200.html https://dev.to/adnanbabakan/deploy-a-single-page-application-with-200-html-2p0f
For my project it generated all needed data and folder size is just 13mb (with all images, fonts, etc...).
You can read more about how static routing is done in Nuxt docs: https://router.vuejs.org/guide/essentials/history-mode.html#example-server-configurations

Not getting query string if trailing slash is not present when deployed

I've to read the query parameters from an URL in my Gridsome app
. I have 2 cases of URL, in one of which my query key is coming empty object {} when accessing using this.$route.query in my app.
case 1: https://app.com/forms/ads/?param1=one&param2=two - There is a / slash just before query params starts.
case 2: https://app.com/forms/ads?param1=one&param2=two - No / slash just before query params.
When I'm access this.$route.query on my localhost both of these cases giving me the value of param1 and param2.
My problem is when I'm pushing it to production, case 2 is returning an empty object {}.
storeUtmData() {
const utmSource = this.$route.query.utm_source;
const utmMedium = this.$route.query.utm_medium;
console.log(this.$route.query); // return {}
...
},
case1 is requesting in an "index.html" in the folder "https://app.com/forms/ads/" while for case2 no resource will exist on static served webpage when you have build your project with gridsome for production (if you don't have any really special infrastructure).
As the resource doesn't exist my guess is that vue-router which you access with "this.$route" contains only an empty object with this case2.
Why does it work locally ?
I can imagine locally in the dev environment express treats the resource "ads" as a route while on live there is only a folder containing and index.html which is served by a webserver.
The difference between a route and a folder which is served statically by a webserver is that a route from an nodejs express application is actually triggering directly application logic to be executed, while the webserver is mainly transferring the static files in the folder. By convention a folder is accessed with a slash at the end in a URL.
For further information please read the following sections:
https://en.wikipedia.org/wiki/Clean_URL#Structure
https://en.wikipedia.org/wiki/Clean_URL#Implementation
I hope that answers your question.
Why do you need case2 to work in production ?
It should be under your control how to link to certain URL at least within your application.
Cheers,
ewatch

is it possible to set multiple base url in vue.config.js?

My vue.config.js code is as below
module.exports = {
baseUrl: process.env.NODE_ENV === 'production' ? '/prodserver1/' : ''
}
and it's working perfectly fine by hitting URL: abc.com/prodserver1/index.html (hostname + pathname)
But I have multiple production servers where I wanted to deploy the same application, let's say, I have one more production server named 'prodserver2'
How to pass multiple production server strings in base URL such that I can run app either on abc.com/prodserver1/index.html or abc.com/prodserver2/index.html?
Maintaining multiple applications for each server is not feasible as every minor change needs to updated in each time to each app.
The simple answer is no. You can't supply a path to a file or resource which references multiple possible locations. However using a relative path instead should work.
From the vue documentation baseUrl it first of all suggests to use publicpath instead. In the publicPath description :
The value can also be set to an empty string ('') or a relative path
(./) so that all assets are linked using relative paths. This allows
the built bundle to be deployed under any public path, or used in a
file system based environment like a Cordova hybrid app.
I suggest you use the option of a relative path, so you can then serve your app from any path.

How to customize polymer serve with default welcome file

Various webservers allow the configuration of welcome files, so that when an URL points to a directory and the directory contains an index.html file, that file is being served.
Can I configure polymer serve so that it also serves index.html files when a directory URL is requested?
Internally, polymer-cli is based on polyserve, which is based on Express, having middleware abilities that might help. Do I really have to go so deep?
Unfortunatly polymer-cli only passes on to express a set of pre defined arguments. see: https://github.com/Polymer/polymer-cli/blob/c5f8db4c3db39fdb90a089739a5c48961210417a/src/commands/serve.ts#L63
But it looks like the ability to add middleware is in the works https://github.com/Polymer/polyserve/issues/250