how to force clearing cache in chrome when release new Vue app version - vue.js

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" />

Related

Embed script only on production with Vue CLI 3

My main goal is to inject a tag into my index.html only in production (it's a New Relic monitoring code snippet).
My Vue.js is built and served as a static resource, so using {% %} tags to surround the script block with a condition doesn't seem to work in this use case.
So I tried to add the New Relic code snippet on my Vue.js app using html-webpack-plugin, since I found a simple Webpack plugin using on html-webpack-plugin. It's a pretty simple plugin, it just create the node and pushes it in the page body : https://github.com/robrap/html-webpack-new-relic-plugin/blob/master/src/index.js#L25
I register the plugin by setting my vue.config.js this way (I first tried to add the script no matter the environment) :
var HtmlWebpackPlugin = require('html-webpack-plugin');
var HtmlWebpackNewRelicPlugin = require('#yodatech/html-webpack-new-relic-plugin');
module.exports = {
configureWebpack: {
plugins: [
new HtmlWebpackPlugin(),
new HtmlWebpackNewRelicPlugin({the plugin options})
]
}
}
The plugin actually does its job well (the code snippet is injected), but its execution messes up with Vue CLI default configuration.
Some stylesheets and scripts aren't referenced anymore in the final index.html file, the <div id=app></div> is not there anymore, the app is broken.
I don't know if using HtmlWebpackPlugin is a dead end, but I currently don't know any other way to reach my goal.
Has anyone an idea on how I could make this work ?
Thanks a lot to anyone passing by.
EDIT : The plugin I was trying to use seemed to be flawed, I had to modify it to make it work with Vue CLI. My main problem has been solved by the selected answer.
vue.config.js option configureWebpack just merges the options you provide to a webpack config provided by Vue CLI. So by using your code, you are running 2 distinct HtmlWebpackPlugins (one from your config and one default from Vue CLI)
Try this instead:
var HtmlWebpackNewRelicPlugin = require('#yodatech/html-webpack-new-relic-plugin');
module.exports = {
configureWebpack: {
plugins: [
new HtmlWebpackNewRelicPlugin({the plugin options})
]
}
}

GWT Bootstrap3 Openlayers offline

I am using GWT with Bootstrap3 and Openlayers Map. I have implemented my own OSM Map server.
My application does not start without internet connection. I need guidance.
I followed the instructions in boostrap3 V1.0.2 for offline applications.
However I only got a blank screen.
Starting with the Firefox debugger I got the following message in the console:
Uncaught ReferenceError: OpenLayers is not defined
<anonymous> http://www.openstreetmap.org/openlayers/OpenStreetMap.js:7
Starting with Google Chrome I get the following warning
[Deprecation] Application Cache API manifest selection is deprecated and will be removed in M85, around August 2020. See https://www.chromestatus.com/features/6192449487634432 for more details.
followed by
GET http://www.openlayers.org/api/OpenLayers.js net::ERR_INTERNET_DISCONNECTED
and
localhost/:1 Application Cache Error event: Invalid or missing manifest origin trial token: http://localhost:8090/simaso/simasoweb/appcache.manifest
Here is my basic setup
SiMaSoWeb.gwt.xml:
...
<inherits name='com.google.gwt.json.JSON'/>
<inherits name="com.google.web.bindery.autobean.AutoBean"/>
<inherits name="org.gwtbootstrap3.extras.cachemanifest.Offline"/>
...
<add-linker name="offline" />
SiMaSoWeb.html:
<!doctype html>
<html manifest="simasoweb/appcache.manifest">
<head>
<title>Sirene</title>
<script type="text/javascript" language="javascript" src="simasoweb/simasoweb.nocache.js"></script>
<script src="http://www.openlayers.org/api/OpenLayers.js"></script>
<script src="http://www.openstreetmap.org/openlayers/OpenStreetMap.js"></script>
<link type="text/css" rel="stylesheet" href="SiMaSoWeb.css">
....
</html>
In ...\simasoweb\appcache.manifest I find:
CACHE MANIFEST
# Version: 1599380329409.0.6297069797290025
CACHE:
AF4477772D0DB53A10ABCF74A5AE0C4D.cache.js
fonts/fontawesome-webfont.woff
clear.cache.gif
fonts/FontAwesome.otf
css/bootstrap-notify-custom.min.cache.css
7192594CA2F468C2F793523022719FA0.cache.js
...
css/font-awesome-4.7.0.min.cache.css
NETWORK:
*
Finally
I compile all this . Resources seem to be included in the war file ..
Needless to say that with internet connection, only in the first 1-2 seconds of starting, all is running fine ..
As per the Google Chrome warning you included, App Cache is a deprecated standard and is being removed. It has already been removed from non-secure contexts.
You should be using Service Workers instead to cache resources for offline use. You may have to write your own linker or maybe you can use gwt-serviceworker-linker.
Thanks to ELEVATE I managed to move from AppCache to ServiceWorker. However the Openlayers couldnt be fixed this way. So here is what solved the issues:
ServiceWorker
I am still using Java 8
I upgraded to GWT 2.9
I added to .gwt.xml
<inherits name="org.realityforge.gwt.serviceworker.Linker"/>
<inherits name="elemental2.dom.Dom"/>
<inherits name="elemental2.promise.Promise"/>
<inherits name="jsinterop.base.Base"/>
...
and
<add-linker name="serviceworker"/>
<extend-configuration-property name="serviceworker_static_files" value="./"/>
<extend-configuration-property name="serviceworker_static_files" value="../SiMaSoWeb.html"/>
In my entry JAVA-routine right at the start of my onModuleLod I added
import static elemental2.dom.DomGlobal.*;
import elemental2.dom.DomGlobal;
public void onModuleLoad() {
...
initStatic();
...
and later in that module
public void initStatic() {
if ( null != navigator.serviceWorker )
{
navigator.serviceWorker.register("simasoweb/"+ GWT.getModuleName() + "-sw.js" ).then( registration -> {
console.log( "ServiceWorker registration successful with scope: " + registration.getScope() );
// Every minute attempt to update the serviceWorker. If it does update
// then the "controllerchange" event will fire.
DomGlobal.setInterval( v -> registration.update(), 60000 );
return null;
}, error -> {
console.log( "ServiceWorker registration failed: ", error );
return null;
} );
navigator.serviceWorker.addEventListener( "controllerchange", e -> {
// This fires when the service worker controlling this page
// changes, eg a new worker has skipped waiting and become
// the new active worker.
console.log( "ServiceWorker updated ", e );
} );
}
}
}
I had issues with accesing the right files in my gwt - war directory. Therefore I updated the navigator.serviceWorker.register... command.
Very useful was the google inherent debugger with CTRL+SHIFT+I. In the 'console'-tab you find the messages - red means bad - solve it!
As external jar libraries I had to include
elemental2-core
elemental2-dom
elemental2-promise
base
Now the openlayers issues... Needless to say that there might be a much more elegant way and further more, you need an offline-map, which I have rendered myself and available (150GB for Germany!).
In the HTML file
Note that I opened the openlayers and openstreetmap .js files in a browser copied them in a file and copied them into my war directory in the subdirectory src. Again the browser debugger can help find directory issues.
<script type="text/javascript" language="javascript" src="simasoweb/simasoweb.nocache.js"></script>
<script src="src/OpenLayers.js"></script>
<script src="src/OpenStreetMap.js"></script>
Copy hard
I downloaded the gwt-openlayers demo project GWT-OpenLayers-master.zip
and copied all files in GWT-OpenLayers-master\gwt-openlayers-showcase\src\main\resources\org\gwtopenmaps\demo\openlayers\public\openlayers into my ...war\src\ directory where my openlayers.js files lie.
Finally I am not sure if the service-worker point 1-3 really helped.

Vuelayers vl-style-icon syntax

I've been looking through the vuelayers documentation and have found little info on to use the vl-style-icon module, which is quite important if you want to create icons on your vuelayer map.
I'm pretty sure I have proper syntax when it comes to using it but marker.png won't load in through it. I've tried accessing it as just a normal image and it works fine so it is to my assumption that it's something with my syntax.
Here is my code:
<template>
<vl-map :load-tiles-while-animating="true" :load-tiles-while-interacting="true" style="height: 400px">
<vl-view :zoom.sync="zoom" :center.sync="center" :rotation.sync="rotation" projection="EPSG:4326"></vl-view>
<vl-feature v-for="crime in crimePoints" :key="crime.id">
<vl-geom-point :coordinates="crime.coords"></vl-geom-point>
<vl-style-box>
<vl-style-icon src="./marker.png" :scale="0.4" :anchor="[0.5, 1]"></vl-style-icon>
</vl-style-box>
</vl-feature>
<vl-layer-tile>
<vl-source-osm></vl-source-osm>
</vl-layer-tile>
</vl-map>
</template>
vl-style-box and vl-style-icon are the main points here. I have also checked to see if the points come up without vl-style-box and they do. What could be wrong with my code?
You can try like this:
<vl-style-icon :src="require('./marker.png')" :scale="0.4" :anchor="[0.5, 1]"></vl-style-icon>
</vl-style-box>
If you used Vue CLI to create your vue project include this in your vue.config.js file. First section tells webpack to parse url attribute on custom tags other than what is already configured (Source).
module.exports = {
chainWebpack: config => {
config.module.rule('vue').use('vue-loader').tap(options => {
options.transformAssetUrls = {
'vl-style-icon': 'src',
...options.transformAssetUrls,
};
return options;
});
}
}
Run the following command to verify the correct vue-loader configuration is there
Source
vue inspect > output.js

vuejs history mode with github/gitlab pages

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~

sails.js less livereload with grunt watch not working

I got my less files compiled in css perfectly by grunt and I see result in .tmp/public/styles
So now livereload with grunt-contrib-watch should be made naturally in sails generated project ?
Or do I have to make a special configuration ?
I found that in tasks/pipeline.js file but not sure of what to do.
// CSS files to inject in order
//
// (if you're using LESS with the built-in default config, you'll want
// to change `assets/styles/importer.less` instead.)
var cssFilesToInject = [
'styles/**/*.css'
];
I saw in the file tasks/README.md :
###### `sails lift`
Runs the `default` task (`tasks/register/default.js`).
And in the file default.js we got :
module.exports = function (grunt) {
grunt.registerTask('default', ['compileAssets', 'linkAssets', 'watch']);
};
But watch.js file is missing in the folder...
What should it be ?
Watch does only looking for files that have changed and execute less, sass, injection and so on - but it doesn't make a reload.
You can add this in task/config/watch.js