When you generate a PWA app using vue ui you can expect the following behavior.
All files you have in your dist folder are precached by browsers.
In other words, when you navigate to your app a browser will quietly download all the files and cache it for further use.
The problem here is that browsers will also download async chunks which could never be used by a user. For example, I have an admin-settings route and a regular user does not need to download it at all.
Now I'm trying to disable this behavior, here's modified vue.config.js file:
module.exports = {
chainWebpack: config => {
config.plugins.delete('prefetch-index')
config.plugins.delete('preload-index')
},
pwa: {
workboxOptions: {
exclude: [/.*/],
runtimeCaching: [{
urlPattern: new RegExp('^https://example.com.*'),
handler: 'CacheFirst',
options: {
cacheableResponse: {
statuses: [200]
}
}
}]
}
},
...
}
Right now everything works as expected and browsers do not prefetch resourses. The problem, however, is that when I upload a new version of a file the app is not updated. Browsers still use the previos version of the file.
I'm stuck, any advice, good sirs?
P.S. Figured it out.
This line caused index.html to be also cached.
urlPattern: new RegExp('^https://example.com.*')
After changing it everything works as expected.
urlPattern: new RegExp('^https://example.com/.+')
Now the problem is that the old version of a cached file is not deleted from cache. The size of cache would grow a lot with each new deployment.
Any advice?
Related
i had created a Vuejs project with PWA support but when i am building its production build it always using cached version of api requests i want to prevent it from using cache for api requests or change its policy from being to cacheFirst to NetworkFirst for api i found a i had changed vue.config.js to prevent cacheing but its not working
pwa: {
workboxOptions: {
exclude: [/.*\/api\//,],
},
},
any help on how i can either prevent cache on api routes or set networkFirst policy
exclude option only works for excluding precaching assets, which is what vue generates at build time, not api requests at run time.
It seems the Cache-Control problem of the api server, but if you do not have access to that server, you can config NetworkFirst policy runtimeCaching:
module.exports = {
pwa: {
workboxOptions: {
runtimeCaching: [{
urlPattern: new RegExp('^https://api.example.com/path'),
handler: 'NetworkFirst',
}]
}
}
};
I'm trying to deploy a VueJS/Express app to Heroku which consists of two App.vue instances using the 'pages' option on vue.config.js. One for the homepage, and then a seperate Vue app for the Saas app itself. Everything works locally in development, but I'm struggling with the server settings in Express for production on Heroku.
When I go to the page 'app' at pat-simplebooks.herokuapp.com/app looking at the sources tab in DevTools the app.js and app.css files returned are both the actual HTML of app.html, hence the app not loading.
The homepage works fine and is calling the 'index' page as expected.
Here is my vue.config.js
module.exports = {
pages: {
index: {
entry: 'src/pages/index/main.js',
template: 'public/index.html',
chunks: ['chunk-vendors', 'chunk-common', 'index']
},
app: {
entry: 'src/pages/app/main.js',
template: 'public/app.html',
chunks: ['chunk-vendors', 'chunk-common', 'app']
}
}
}
And the relevant production settings in Express;
const history = require('connect-history-api-fallback');
if(process.env.NODE_ENV === 'production'){
app.use(history({
rewrites: [{
from: /\/app/,
to: '/app.html'
}]
}));
app.use(express.static(path.join(__dirname, '../../client/dist')))
}
I've tried adding <base href="/ "> to the HTML templates, as well as <base href="/app/" > but to no avail, as suggested in other answers I've found. Also the publicPath webpack option doesn't work for multiple pages as noted in the VueJS docs.
Removing the history redirect setting in Express allows me to navigate to http://pat-simplebooks.herokuapp.com/app.html - which works, however as soon as I refresh the page it redirects back to the 'index' page.
Any help would be great, I've exhausted my Googling skills.
I managed to work it out, incase anyone has the same issue in future.
The connect-history-api-fallback package needed to provided with the htmlAcceptHeaders option to only rewrite the html location, and not the JS/CSS assets.
app.use(history({
rewrites: [{
from: /\/app/,
to: '/app/index.html'
}],
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml']
}));
app.use(express.static(path.join(__dirname, '../../client/dist')))
I'm trying to integrate a vue project that I built with the vue cli into an existing .net app. I'm very new to vue, so I'm trying to follow guides and such, but am left with lots of questions.
While trying to compile this, I found that the vue cli-service node module has the following for setting the main.js file located in it's base.js file.
webpackConfig
.mode('development')
.context(api.service.context)
.entry('app')
.add('./src/main.js')
.end()
.output
.path(api.resolve(options.outputDir))
.filename(isLegacyBundle ? '[name]-legacy.js' : '[name].js')
.publicPath(options.publicPath)
I need to override this since my .net app doesn't have a src directory and the usage of this vue app won't follow that path structure. I'm not seeing a way to do it in my vue.config.js file. I would expect that if I can override it, that would be the spot.
I could overwrite the base.js file where this exists, but when a co-worker runs npm install, they would get the default value rather than what I have. The only option I see there is checking in all the node modules to git which we really don't want to do.
For anyone in a similar situation, I found what worked for me. It's not the ideal solution due to the fact that it forces you to build into a js folder. That resulted in the file being put in Scripts\build\vue\js. Would be nice to be able to just dump it in the vue folder, but at least this works. Code below.
vue.config.js
module.exports = {
publicPath : "/",
outputDir: "Scripts/build/vue", //where to put the files
// Modify Webpack config
// https://cli.vuejs.org/config/#chainwebpack
chainWebpack: config => {
// Not naming bundle 'app'
config.entryPoints.delete('app'); //removes what base.js added
},
// Overriding webpack config
configureWebpack: {
// Naming bundle 'bundleName'
entry: {
quote: './Scripts/Quote/index.js' //where to get the main vue app js file
},
optimization: {
splitChunks: false
}
},
filenameHashing: false,
pages: {
quoteApp: { //by using pages, it allowed me to name the output file quoteApp.js
entry: './Scripts/Quote/index.js',
filename: 'index.html'
}
}
}
I am having hard times wrapping my mind around new CLI and configuration.
In the official documentation, I couldn't really find anything about CSS and how to add it as an entry point and not import it directly into an component or main.js file.
I realized that some JS files are being chunked into separate file, from main.js and the rest gets compiled where supposed to - into the app.js.
I was wondering, how does it know in the background what should be stored as "vendor" for the JS, but when I try to import some "vendor" SASS files into main.js it does not and it merges all within a single app.css file.
Can anyone tell me, how does one create/modify the vue.config.js and tell the bundler that I also want app.scss to be an entry point and vendor.scss to be another entry point.
I am unsure what are best practices for such purpose, but I always did it this way with my own webpack config...
Partial example below:
entry: {
vendor: [
'./styles/vendor.scss',
'./scripts/vendor.js'
],
app: [
'./styles/app.scss',
'./scripts/app.js'
]
}
EDIT #1
I think I got the first one...
"How does it know what should be chunked in "vendor" files?
Whatever gets imported from node_modules, it is being chunked.
What I did not figure out yet is... What if I am having my personal assets/styles/vendor directory where I #import those SASS files from NPM and do some modifications of variables or whatever.
Importing this file to main.js does not get chunked in this case... So there must be a way to tell bundler that I want everything within that directory or everything within vendor.scss file where everything is being imported, to be chunked out.
EDIT #2
I figured I can use WebPack's magical comments to import the main vendor SCSS file, such as:
import(/* webpackChunkName: "vendor" */ './assets/styles/vendor.scss')
I don't have a problem with this, but apparently the bundler does. It generates an empty vendor.[hash].js file as well.
EDIT #3
I did further research and learned that there's a command vue inspect which would output the webpack configuration.
So when making tweaks to vue.config.js, we can look a the output with this command if there's a bug or something is not working as expected.
Further more, I learned that if we specify entry directly in our vue.config.js file, that we will get an error that entry cannot be specified within our configuration file.
The following is forbidden to do so, but it's what I actually want to achieve...
// vue.config.js
module.exports = {
entry: {
app: [
'./src/main.js',
'./src/assets/styles/app.scss'
],
vendor: [
'./src/assets/styles/vendor.scss'
]
}
}
The actual proper way to do this will be an answer to my own question...
The way to achieve this is by using WebPack's Chain API.
However, if I did everything correctly, I still see a problem of generated vendor.[hash].js file with some WebPack module boilerplate. This JS file is also being injected to the index.html template.
Which leads to the same outcome as the attempt of my EDIT #2, except that we're no longer importing our Sass files within main.js
To modify entry points for my purpose of this question, we can do it the following way:
// vue.config.js
module.exports = {
chainWebpack: config => {
config
.entry('app')
.add('./src/assets/styles/app.scss')
.end()
.entry('vendor')
.add('./src/assets/styles/vendor.scss')
.end()
}
}
Note
We're not specifying the app entry JS file, which would be main.js by default, because we're not overriding the current entry point. Instead, we're extending it, so everything works as expected.
UPDATE
Until WebPack resolves this in future major releases, I found a great package - fqborges/webpack-fix-style-only-entries. It solves this issue that I was having and I'd suggest you to use it.
Final configuration would look like this:
const FixStyleOnlyEntries = require('webpack-fix-style-only-entries')
module.exports = {
chainWebpack: config => {
config
.entry('app')
.add('./src/assets/styles/app.scss')
.end()
.entry('vendor')
.add('./src/assets/styles/vendor.scss')
.end()
},
configureWebpack: {
plugins: [
new FixStyleOnlyEntries()
]
}
}
UPDATE #2
After further investigation and use of such configuration for projects, I realized that I had to use !important in styles where I had a need to override anything vendor related.
This is simply because WebPack will inject app, before vendor (both JS and CSS) and it will cause such issue.
Even if we modify the configuration from above and move app entry, below the vendor entry, it will still fail. Reason being, because we're modifying the entry point which already exists by default within vue-cli config. We're adding more entries to the app and we're adding new vendor entry.
To fix this issue of ordering, we must delete the app entirely and then create it ourselves.
const FixStyleOnlyEntries = require('webpack-fix-style-only-entries')
module.exports = {
chainWebpack: config => {
config.entryPoints.delete('app')
config
.entry('vendor')
.add('./src/assets/styles/vendor.scss')
.end()
.entry('app')
.add('./src/main.js')
.add('./src/assets/styles/app.scss')
.end()
},
configureWebpack: {
plugins: [
new FixStyleOnlyEntries()
]
}
}
I´m trying to setup a build workflow using the aforementioned technologies, but I´m getting the following error, which seems very generic upon running tests on karma:
TypeError: 'undefined' is not an object (evaluating 'a.Sifter=b()')
This happens even without adding any ECMSA6 specific feature. The same workflow works fine without the transpiling phase in the workflow.
What I tried was to set the babeljs after a concatenation phase and before executing a uglifying on it, like the following snippet:
var defaultTasks = [
"sass:prod", // compile scss sources
"cleanAll", // clean folders: preparing for copy
"copyAll", // copying bower files
"cssmin:customVendor", // minify and concat 'customized from vendor' css
"concat:vendorStyles", // concat vendors's css + minified 'customized from vendor' and distribute as 'css/vendor.css'
"uglify:rawVendors", // minifies unminified vendors
"concat:vendorScripts", // concat vendors's scripts and distribute as 'scripts/vendor.js'
"ngAnnotate:app", // ng-annotates app's scripts
"concat:appScripts", // concat app's (customized from vendor's + ng-annotated + customer's)
"babel",// uses babeljs to convert brandnew ES6 javascript into ES5 allowing for old browsers
"uglify:app" // minify app script and distribute as 'scripts/app.js'
];
if (!skipTest) {
defaultTasks.push("karma:target"); // run tests on minified scripts
}
The imporant definitions are shown:
babel: {
options: {
"presets": ['es2015']
},
dist: {
files: {
"<%= concat.appScripts.dest %>": "<%= concat.appScripts.dest %>"
}
}
},
uglify: {
options: {
mangle: {
except: [
"jQuery", "angular", "tableau", "LZString", "moment", "Moment", "Modernizr",
"app", "modules"
]
}
},
app: {
files: [{
src: ["<%= concat.appScripts.dest %>"],
dest: "<%= app.dist %>/scripts/app.js"
}]
}
},
I´ve tested the transpile a bit, running the default logic from babel url, and it works well, converting basic stuff.
Is there any better workflow that I could use to still run the tests against the same code that would be executed for real?
Thanks
In the end, the workflow was correct.
I just need to modify the filesets a bit in order to avoid transpiling the selectize.js file (which wasn´t really needed).
However, not sure why it was breaking
That solved to me, so I´m closing the question, but perhaps might be useful for someone else.