I'm working with Webpack 4.43, Vue 2.6.11 & Vue Router 3.1.6
Webpack generates a dist folder with a bundle.js and an index.html.
If I navigate to nested pages through the app links everything works fine, but if I try to reload a nested page, the index.html can't load bundle.js because it tries to find it at the nested page level. For example if I go to localhost:8080/fr/contact it will try to load localhost:8080/fr/bundle.js
I've seen many other related topic but no one ever gives a real answer. Does anyone have a solution?
EDIT: Don't know if it helps, but I just realized that the images don't load in nested pages even when I reach them through the router-links. Same problem: it looks for localhost:8080/fr/my-image.png instead of localhost:8080/my-image.png
Finally solved it by setting publicPath: '/' in my Webpack config (not the Vue config). Here's how it looks:
module.exports = {
...
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/'
},
...
}
Related
Using Vue CLI, and Vue 2.
Anyone knows how to build the project using relative paths, so I can place it in any subfolder in my server and it will work? (for example www.mysite.com/subfolder/)
I've tried with
// vue.config.js
module.exports = {
publicPath: "",
};
And it builds the paths relative (ie, js/app.js instead of /js/app.js), but the app wont' load. Nothing shows on the page.
Strangest thing is that all files are loaded correctly (I can check on network tab in Chrome devtools), no JS errors, etc. So the page is loading all the files but it seems like it's refusing to mount the app when using relative paths.
I know that I can add the absolute path to the build process but that's not what I need. My client needs to be able to move the files freely from one subfolder to another and the app should work without the need to recompile
PS: Also tried building the project with Vite and Vue 3, same problem.
Thanks!
Alright, looks like all that's needed is:
build with a relative publicPath (empty string)
// vue.config.js
module.exports = {
publicPath: "",
};
add a <base> tag to the final HTML...
<base href="/subfolder/" />
For Vite, the export should be
// vite.config.js
export default {
...
base: "",
};
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 have deployed the contents of my /dist folder in the master branch of christopherkade.github.io, which has deployed my website succesfully.
But when I navigate using the navbar (christopherkade.com/posts or christopherkade.com/work) and reload the page I get an error by Github pages:
404 File not found
Note that my routing is done using Vue router like so:
export default new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/work',
name: 'Work',
component: Work
},
{
path: '/posts',
name: 'Posts',
component: Posts
},
{ path: '*', component: Home }
]
})
And my project is built like such:
build: {
// Template for index.html
index: path.resolve(__dirname, '../docs/index.html'),
// Paths
assetsRoot: path.resolve(__dirname, '../docs'),
assetsSubDirectory: 'static',
assetsPublicPath: '/',
/**
* Source Maps
*/
productionSourceMap: true,
// https://webpack.js.org/configuration/devtool/#production
devtool: '#source-map',
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: false,
productionGzipExtensions: ['js', 'css'],
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report
}
What could be causing this issue?
But when I navigate using the navbar (christopherkade.com/posts or
christopherkade.com/work) and reload the page 404 File not found
Let me explain why 404 File not found is being shown
When christopherkade.com/posts is triggered from web browser, the machine to which the domain christopherkade.com is mapped is contacted.
The path /posts is searched in its server. in your case, i believe the route for /posts doesn't exist in the server. As the result 404 is displayed
There are few ways to fix this
To prevent the browser from contacting the server when triggering the request christopherkade.com/posts, you can keep mode : 'hash' in your route configuration
How mode : 'hash' works? This is one way to fix your issue
mode : 'hash' makes use of default browser behavior which is to prevent http request from triggering the details that exists after #
As the result, when you trigger christopherkade.com/#/posts , christopherkade.com is being triggered by the browser and once response is received the /posts route from the route config is invoked.
Lets assume that you have control over the server and you are adamant
that you need # to be removed from the URL
Then what you could do is to configure server in such a way that server responds with the same page everytime any paths is being sent. Once response is received in the browser, route will automatically kicked off.
Even in your current program, the routeConfig gets kicked off when you click any links (like work,posts) in your page. This is because the browser behavior is not being invoked at this point.
In your case, you use github for hosting this app with mode: 'history' i myself have to look for a specific solution to workaround this. i will update my answer once i get it.
i hope this was useful.
You can fix this issue by a simple workaround. I combined all the insights from reading multiple issues about this and finally this is what helped me fix this problem.
Solution Logic - You just need a copy of index.html with the name 404.html in the dist folder
Steps to fix
Go to you package.json file, under scripts add a new script called "deploy" like below, you just need to execute this everytime after you build your page. It will automatically take care of the issue.
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"deploy": "cd dist && cp index.html 404.html && cd .. && gh-pages -d dist"
},
This will copy the index.html & rename it 404.html and pushes dist folder under the branch gh-pages and after that your script will appear in the vue ui like below
or
If you are using git subtree push --prefix dist origin gh-pages method to push, then edit the deploy script in package.json to below
"deploy": "cd dist && cp index.html 404.html
and then execute the below git command. PS, don't forget to execute this script before manually using npm script method or from the vue ui
git subtree push --prefix dist origin gh-pages
This actually happens since your browser makes a request to christopherkade.com/posts URL which doesn't exist (this route is defined in Vue application running from index.html).
If you were running your own server, you would probably configure it to render your index.html page for any request URI, so your Vue application would be loaded from any path and handle routing by itself.
Speaking of GitHub pages, you can't just configure them to act the same way I described, but fortunately, there is a workaround which uses custom 404 page:
https://github.com/rafrex/spa-github-pages
As a workaround, I duplicated the index.html and renamed it to 404.html.
In this way, if the page is reloaded, you still get the correct page however this is served through the 404.html file.
As a workaround I have created folders for each route (with a script) and placed the index.html in all of them.
404s still don't work.
If you use Nuxt, this fixes the problem.
layaouts/blank.vue
<template>
<nuxt />
</template>
pages/redirect.vue
<template>
<div></div>
</template>
<script>
export default {
layout: 'blank',
fetch({base, redirect, query}) {
const param = query.p
if (param === undefined) {
return redirect('/')
}
const redirectPath = '/' + param.replace(base, '')
return redirect(redirectPath)
}
}
</script>
static/404.html
<html>
<head>
<script>
var pathName = window.location.pathname;
var redirectPath = '/<repository-name>/redirect';
location.href = redirectPath + '?p=' + encodeURI(pathName);
</script>
</head>
</html>
https://gist.github.com/orimajp/2541a8cde9abf3a925dffd052ced9008
Very simple perfect solution just follow the below instruction
Add a _redirects file inside the /public folder like /public/_redirects
After that add /* /index.html 200 into the _redirects file
I think with this solution your redirect problem will be solved