vuejs history mode with github/gitlab pages - vue.js

Has anyone managed to figure out how to make Vue.js work with history mode with GitHub or GitLab Pages?
It works with hash mode, but I don't want to use hash mode for SEO related reasons.
Reference for router modes: https://router.vuejs.org/en/essentials/history-mode.html

I found a solution that works for me in this article.
To summarize the solution, I created the following 404.html file and added it to the project's root folder.
<!DOCTYPE html>
<html>
<head>
<script>
// ====================================================================================================================
// This text is simply to make sure the 404.html file is bigger than 512 bytes, else, internet explorer will ignore it.
// Thank you internet explorer for requiring such awesome workarounds in order to work properly
// ====================================================================================================================
sessionStorage.redirect = location.href;
</script>
<meta http-equiv="refresh" content="0;URL='/'">
</head>
</html>
I then added this javascript in the index.html:
(function(){
var redirect = sessionStorage.redirect;
delete sessionStorage.redirect;
if (redirect && redirect != location.href) {
history.replaceState(null, null, redirect);
}
})();

Not sure about GitLab Pages, but in GitHub Pages you can serve your whole Vue.js Application through the 404.html file instead of the index.html file. Simply rename the index.html file to 404.html file on deploy.
EDIT:
As pointed out in the comments, this has the side effect of having GitHub/GitLab serve your website with a 404 status code.

Run into same issue, found this question & tried both solution above but no luck. Then tried combine them like this:
Here my 404.html file
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
<script>
// ========================================
// Credits:
// - https://stackoverflow.com/a/50259501
// - https://stackoverflow.com/a/50247140
// ========================================
const segment = 1
sessionStorage.redirect = '/' + location.pathname.slice(1).split('/').slice(segment).join('/')
location.replace(
location.pathname.split('/').slice(0, 1 + segment).join('/')
)
</script>
</head>
</html>
And here's my main.js file
const app = new Vue({
router,
store,
render: h => h(App),
created () {
if (sessionStorage.redirect) {
const redirect = sessionStorage.redirect
delete sessionStorage.redirect
this.$router.push(redirect)
}
}
})
app.$mount('#app')
And it works
https://feryardiant.github.io/static-spa/foo/bar/baz
https://feryardiant.gitlab.io/static-spa/foo/bar/baz

GitLab Answer
For those using GitLab there is now support to redirect to index.html using a _redirects file in your public folder.
Steps:
Create a file named _redirects in the public folder
Add this snippet line to that file
/* /index.html 200
Documentation: https://docs.gitlab.com/ee/user/project/pages/redirects.html#rewrite-all-requests-to-a-root-indexhtml

A little late to the party but I have a method to do this. I am using Vue CLI 3 and GitHub pages.
First of all, I commit all the source file into a source branch, and commit the dist folder (generated by Vue) to the master branch using the following shell command:
# deploy.sh
#!/usr/bin/env sh
# abort on errors
set -e
# build
echo Building. this may take a minute...
npm run build
# navigate into the build output directory
cd dist
# create a copy of index.html
cp index.html 404.html
find . -name ".DS_Store" -delete
# if you are deploying to a custom domain
echo 'custom.com' > CNAME
# remove git and reinitialise
rm -rf .git
echo Deploying..
git init
git add -A
git commit -m 'deploy'
# deploy
git remote add origin https://github.com/User/repo.github.io
git push origin master --force
cd -
rm -rf dist
When GitHub pages can't find the route, it uses 404.html. The deploy program I wrote makes a copy of index.html and names it 404.html. That's why it works.
Edit
Just realised that this wouldn't be good for SEO purposes as it returns a 404 response and Google won't index it.

You could use a 404.html hack https://github.com/rafrex/spa-github-pages/blob/gh-pages/404.html
Or you can try to pre rendering your vue into static html
https://nuxtjs.org/guide#static-generated-pre-rendering-

Based on Fery's solution, I think instead of handling redirect when creating Vue instance, the Navigation Guards could work better.
I basically added a beforeEnter guard for the index route, so that the index page will be skipped and directly go to the target page.
const routes = [
{
path: '/',
component: Index,
beforeEnter: (to, from, next) => {
if (sessionStorage.getItem('redirect') !== null) {
const redirect = sessionStorage.redirect
delete sessionStorage.redirect
next(redirect)
} else {
next()
}
}
},
]
Hope this is helpful.

2021 Solution for vue3 & vue-cli:
Follow this with "Basic instructions":
https://github.com/rafgraph/spa-github-pages#usage-instructions
no need to change var pathSegmentsToKeep = 0; the 404.html.
and then in the vue.config.js:
// do not use "./dist/"
publicPath: "/dist/",
// make the index.html file place at the root of the repo
indexPath: "../index.html",
then the spa is good to go~

Related

how to force clearing cache in chrome when release new Vue app version

I created an app with vue-cli and then I build the dist folder for production.
The app is deployed on IIS with flask backend and works fine.
The problem occurs when I have to make some changes and I have to redo the deployment. After this, users call me because app doesn't work but if I clear the chrome cache, the app works fine again.
How can I fix this problem? Is there a method to clear chrome cache automatically when I release a new application version?
Thanks
my dist folder
deployment: copy and paste folder dist on IIS
if files in dist folder are correct, maybe the problem is in axios cache? i have make some changes also to rest apis
I had the same problem and changing (incrementing) the version number in package.json before running the build command fixed it.
For example by default the version number is set to "0.1.0"
package.json file:
{
"name": "project-name",
"version": "0.1.1",
"private": true,
...
}
If you use vue-cli, then it has built-in webpack configs for building dist. And in fact it adds hash-names to output files.
But if it was removed somehow, you can add it back to webpack config like
output: {
filename: '[name].[hash].bundle.js'
}
And your app will looks like this:
And even more, you do not need to handle how all this stuff will be added to html, coz webpack will figure it out for you.
You need to add a version query to your js file. This is how a browser can know if the file has changed and needs to download the new version.
So something like:
<script src="main.js?v=1.1"></script>
<script src="main.js?v=1.2"></script>
etc...
Assuming this is nothing to do with service worker/PWA, the solution could be implemented by returning the front-end version.
axiosConfig.js
axios.interceptors.response.use(
(resp) => {
let fe_version = resp.headers['fe-version'] || 'default'
if(fe_version !== localStorage.getItem('fe-version') && resp.config.method == 'get'){
localStorage.setItem('fe-version', fe_version)
window.location.reload() // For new version, simply reload on any get
}
return Promise.resolve(resp)
},
)
You can also ensure the fe-version is returned based on any sort of uniqueness, here I have used the commit SHA.
Full Article here: https://blog.francium.tech/vue-js-cache-not-getting-cleared-in-production-on-deploy-656fcc5a85fe
You can't access the browser's cache, that would be huge a security flaw.
To fix it, you must send some headers with your flask responses telling the browser not to cache you app.
This is an example for express.js for you to get the idea:
setHeaders: function (res, path, stat) {
res.set('Cache-Control', 'no-cache, no-store, must-revalidate') // HTTP 1.1
res.set('Pragma', 'no-cache') // HTTP 1.0
res.set('Expires', '0') // Proxies
}
You can read a lot more about caching in here.
This is an older post, but since I could not find the solution for this problem online, ill just post this here in case someone else might find it usefull.
I added the hash to the appllication chunk files via the webpack.mix.js file by adding:
mix.webpackConfig({
output: {
chunkFilename: 'js/[name].js?id=[chunkhash]',
},
})
This adds a fingerprint to the actual chunks and not just the app.js file. You can add a version name to the app.js file aswell by adding version(['public/js/app.js']); at the end of the file, or add filename: '[name].js?[hash]' to the output block.
My complete webpack.mix.js:
const mix = require('laravel-mix');
mix.webpackConfig({
output: {
chunkFilename: 'js/main/[name].js?id=[chunkhash]',
}
}).js('resources/js/app.js', 'public/js').vue()
.postCss('resources/css/app.css', 'public/css', [
//
]).version(['public/js/app.js']);
In my laravel blade file I used
<script src="{{ mix('js/app.js') }}"></script>
to load the app.js file with the correct version fingerprint.
The answer for me was caching at my DNS provider level.
Basically, I'm using Cloudflare DNS proxy and they are caching the website so in development mode I was not getting the code updates.
I had to clear the cache many times to get anything to change. I had to wait a significant period of time before anything update.
Turned it off and it stopped doing that.
the method I want to suggest
<script src="{{ asset('js/app.js?time=') }}{{ time() }}" defer></script>
add below script in publc/index.html
<head>
...
<script type="text/javascript" language="javascript">
var timestamp = (new Date()).getTime();
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "<%= BASE_URL %>sample.js?t=" + timestamp;
document.head.appendChild(script);
</script>
...
</head>
could you try ?
vm.$forceUpdate();
Also it's possible that the component it self needs a unique key :
<my-component :key="unique" />

How to download a PDF in Vue

So, here I've got a locally stored file named "its_me.pdf" in the assets folder.
I'm trying to reference a download to the PDF using an HTML tag
<a href="../assets/its_me.pdf" download>PDF</a>
It is a real PDF file, if I go double click on the file manually I can see it display and it's real. However, when I go to my application on: http://localhost:4200/its_me (name of route in which it lives), and click on the link, I get a "Failed - No File" error.
Based on #AkashBhave answer I was able to get to work this way.
In my script tag:
data () {
return {
publicPath: process.env.BASE_URL
}
}
then in my template.
<a:href="`${publicPath}whatever.pdf`" download="download">PDF</a>
Alternatively with webpack, in your vue.config.js you add this;
chainWebpack: config => {
config.module
.rule("pdf")
.test(/\.pdf$/)
.use("file-loader")
.loader("file-loader");
}
then in the script tag;
data () {
return {
pdfLink: require("#/assets/whatever.pdf"),
}
}
Finally, in the template;
<a :href="pdfLink" download="download">PDF</a>
Relative imports should work by default with Vue. Try putting your PDF file into the /public folder of your application.
You can then reference the file using string interpolation, like so:
<link rel="icon" href="<%= BASE_URL %>its_me.pdf">
More information is available at
https://cli.vuejs.org/guide/html-and-static-assets.html#interpolation
If that doesn't work, something might be wrong with your Webpack or build configuration.

asp.net core 2 static files cache busting default document default.html?

I have a single page web app and I use versioning to cache bust the script and other resources that app needs, i.e. in default.htm I link like this:
<script src="js/index.js?plv=1.1"></script>
This works fine, however the issue I run into is that the default.htm page itself is being cached by the browser so it doesn't read the new default.htm and in turn doesn't trigger the update.
The "home" page is being served by the app.UseDefaultFiles() middleware.
I'm thinking a quick and dirty way to fix this issue, is on every code update I publish, I alternate between index.htm and default.htm as the default document and rename the other one so it doesn't get picked up by UseDefaultFiles middleware.
This is a manual step however and surely I'll forget to do it at some point.
How can I handle this situation?
The solution was obvious in retrospect, the home page is light, no reason to cache it at all:
app.UseStaticFiles(
new StaticFileOptions
{
OnPrepareResponse = context =>
{
if (context.File.Name == "default.htm" ) {
context.Context.Response.Headers.Add("Cache-Control", "no-cache, no-store");
context.Context.Response.Headers.Add("Expires", "-1");
}
}
});
use the
asp-append-version="true"
attribute on the script tag.

include client side js in express.js

I've build a new app with an express-generator so i've got public dir with javascripts i guess for a client side. But i can't get access to this files.
My app.js has for an absolute path
app.use(express.static(path.join(__dirname, 'public')));
But when i put on my index.html page on the bottom of the body like this
<script type="text/javascript" src="/javascripts/main.js">
i've got an error inside a console with a 404 regarding to this script.
you need to install stylus and nib npm modules
var express = require('express')
, stylus = require('stylus')
, nib = require('nib')
function compile(str, path) {
return stylus(str)
.set('filename', path)
.use(nib())
}
app.use(stylus.middleware(
{ src: __dirname + '/public'
, compile: compile
}
))
app.use(express.static(__dirname + '/public'))
I am developing something similar. I have my javascript files inside js folder in public folder. This is my link to javascript and I am not getting any error:
<script src="js/options.js"></script>
I think you needed to remove a common slash in "src" tag or properly close the "script"

Stylus middleware in Express not working?

In app.coffee I have
stylus = require("stylus")
...
app.use stylus.middleware
debug: true
src: __dirname + "/stylus"
dest: __dirname + "/public/css"
compile: (src) ->
console.log(stylus(src))
return stylus(src)
I included the styles in layout.jade like:
link(rel="stylesheet", href="/css/styles.css")
But in Chrome network tab, I see canceled for styles.css why is that?
When I point the browser directly to /css/styles.css, I get
Cannot GET /css/styles.css
Whats wrong? How do I fix this?
Do you have the static middleware properly configured and working and positioned AFTER the stylus middleware in your middleware stack? The stylus middleware is just going to read the .styl file and write the corresponding .css file but it expects the static middleware to then find the .css file and serve it.
Also note that your src and dest file hierarchies should correspond directly. By that I mean even counting all intermediate directories if you list the recursive contents of one directory (ls -R or similar) then the ONLY difference should be src contains .styl files and dest contains exactly corresponding .css files. Don't tack a /css prefix onto one but not the other, for example.
Recently I run into the same issue and as long as #PeterLyons answer is correct I found that adding the extra slash after css directory name also seems to fix the problem.
(without coffee)
var stylus = require('stylus');
app.configure(function() {
app.use(stylus.middleware({
src: __dirname + '/stylus',
dest: __dirname + '/public/css/' // <-- additional slash after "css"
}));
app.use(express.static(__dirname + '/public'));
});
Not sure if this is stylus version-related issue and wasn't/was present before but still it's quite confusing to me.
This has been driving me crazy for a few hours so I thought I'd share :)
I serve my external files from /public
So my stylesheets are in /public/styles. All I had to do was put my .styl files in a folder called /styles in the root of my project.
modules.app.use(modules.stylus.middleware({
debug: true,
src: __dirname + '/',
dest: __dirname + '/public/',
compile: compile
}));
I got around the whacky path requirements as I'm always going to ask for styles in /styles
GET /styles/website.css serves /styles/website.styl from the root / directory of the project
this worked for my
app.use(express.static('public'));
//stylus
function compile(str, path) {
return stylus(str)
.set('filename', path)
}
app.use(stylus.middleware(
{ src:'/public/css'
, compile: compile
}
));
put your file.styl in public.css it will be compiled there too!
the problem must be the src directory, it seems you are pointed to modules/stylus, anywhere I am not an exprert but this way works