Webpack configuration is a part of Vue CLI setup (can be checked with vue inspect). This is a relevant part of it:
entry: {
foo: [
'.../src/foo.js'
],
barWidget: [
'.../src/barWidget.js'
],
},
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
name: 'chunk-vendors',
test: /[\\/]node_modules[\\/]/,
priority: -10,
chunks: 'initial'
},
common: {
name: 'chunk-common',
minChunks: 2,
priority: -20,
chunks: 'initial',
reuseExistingChunk: true
}
}
},
...
And HTML output is:
<script type="text/javascript" src="/assets/js/chunk-vendors.[HASH].js"></script>
<script type="text/javascript" src="/assets/js/foo.[HASH].js"></script>
and
<script type="text/javascript" src="/assets/js/chunk-vendors.[HASH].js"></script>
<script type="text/javascript" src="/assets/js/barWidget.[HASH].js"></script>
There's no problem for foo to have as many script tags as needed, but barWidget is widget entry point that is supposed to be loaded at once with no initial chunk dependencies. My intention is to make barWidget be loaded with a single line of code (hash will likely be disabled for this purpose):
<script type="text/javascript" src="/assets/js/barWidget.js"></script>
But in its current state it fails to load if chunk-vendors is omitted.
I'd prefer to keep vendors and common chunks as they are because they are splitted in a reasonable way and can be reused on client side between entry points, but I need barWidget to auto-load a chunk it depends on. A less preferable way would be to disable chunks but for barWidget only, chunk splitting in other entry points should remain unchanged.
A way to reproduce it is basically a new Vue CLI project with 2 entry points added, or any Webpack project with similarly configured splitting.
Here is the project (listed for completeness):
package.json
{
"name": "foobar",
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build"
},
"dependencies": {
"core-js": "^3.6.5",
"vue": "^3.0.0"
},
"devDependencies": {
"#vue/cli-plugin-babel": "~4.5.0",
"#vue/cli-service": "~4.5.0",
"#vue/compiler-sfc": "^3.0.0"
}
}
vue.config.js
module.exports = {
pages: {
foo: {
entry: 'src/foo.js',
template: 'public/foo.html',
filename: 'foo.html'
},
barWidget: {
entry: 'src/barWidget.js',
template: 'public/barWidget.html',
filename: 'barWidget.html'
},
},
};
public/foo.html
public/barWidget.html
<!DOCTYPE html>
<html>
<body>
<div id="app"></div>
</body>
</html>
src/foo.js
import { createApp } from 'vue'
import Foo from './Foo.vue'
createApp(Foo).mount('#app')
Foo.vue
<template>
<HelloWorld msg="Foo"/>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
components: {
HelloWorld
}
}
</script>
src/barWidget.js
import { createApp } from 'vue'
import BarWidget from './BarWidget.vue'
createApp(BarWidget).mount('#app')
BarWidget.vue
<template>
<HelloWorld msg="Bar widget"/>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
components: {
HelloWorld
}
}
</script>
Can barWidget be forced to automatically load chunk-vendors.[HASH].js by means of Webpack, without loading it explicitly in the place where barWidget.[HASH].js is being used?
Can barWidget entry point be forced to not use other initial chunks (chunk-vendors, etc) and output self-sufficient barWidget.js bundle, without affecting the way splitting works in other entry points?
Are there other options for the described scenario?
I think that what you want is what is described in this webpack issue reply
the reply uses a function to exclude the dependencies of a specific entrypoint from being included in a chunk:
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
// ... your current config, just change the chunks property
// Exclude pre-main dependencies going into vendors, as doing so
// will result in webpack only loading pre-main once vendors loaded.
// But pre-main is the one loading vendors.
// Currently undocument feature: https://github.com/webpack/webpack/pull/6791
chunks: chunk => chunk.name !== "barWidget"
}
}
}
},
to do this with vue should be just a matter of changing the webpack config in the vue.config.js file like this:
module.exports = {
configureWebpack: config => {
config.optimization.splitChunks.cacheGroups.vendors.chunks =
chunk => chunk.name !== "barWidget";
}
}
I haven't tried this but I think it should work as is or with some minimal tweaks
You can use a function to filter the chunks. See this Webpack issue
vue.config.js
module.exports = {
pages: {
foo: {
entry: 'src/foo.js',
template: 'public/foo.html',
filename: 'foo.html'
},
barWidget: {
entry: 'src/barWidget.js',
template: 'public/barWidget.html',
filename: 'barWidget.html',
chunks: ['barWidget']
},
},
chainWebpack: config => {
if (process.env.NODE_ENV !== 'test') {
config.optimization.splitChunks({
cacheGroups: {
defaultVendors: {
name: `chunk-vendors`,
test: /[\\/]node_modules[\\/]/,
priority: -10,
chunks: chunk => chunk.name !== "barWidget"
},
common: {
name: `chunk-common`,
minChunks: 2,
priority: -20,
chunks: 'initial',
reuseExistingChunk: true
}
}
})
}
}
}
Note the pages.barWidget.chunks - it is needed because otherwise HtmlWebpackPlugin includes vendors chunk into barWidget.html even it is not needed at all...
Result:
Related
So I'm using vite to build my Vue 3 application for a legacy website which still uses jQuery and a few other JS frameworks.
I'm using the esm bundler as I would still like to boot it up and use it with slotted components.
<div id="app">
<vue-component-name></vue-component-name>
</div>
And it works perfectly. But when jQuery is used on the page, no where near my components it seems the esm bundled version of Vue has set a global variable named $ which breaks jQuery.
Has anyone had this issue or know of a way to fix it?
import { defineConfig } from 'vite';
import type { UserConfig as VitestUserConfigInterface } from 'vitest/config';
import svgLoader from 'vite-svg-loader';
import vue from '#vitejs/plugin-vue';
import path from 'path';
const vitestConfig : VitestUserConfigInterface = {
test: {
globals: true,
include: ['./tests/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
},
};
export default defineConfig({
test: vitestConfig.test,
plugins: [vue(), svgLoader()],
base: '/',
resolve: {
alias: {
vue: 'vue/dist/vue.esm-bundler.js',
'#': path.resolve(__dirname, '/src'),
},
},
build: {
outDir: '../wwwroot/dist',
emptyOutDir: true,
manifest: true,
rollupOptions: {
input: {
main: './src/main.ts',
},
output: {
entryFileNames: 'assets/js/[name].js',
chunkFileNames: 'assets/js/[name].js',
assetFileNames: ({ name }) => {
if (/\.(gif|jpe?g|png|svg)$/.test(name ?? '')) {
return 'assets/images/[name][extname]';
}
if ((name ?? '').endsWith('.css')) {
return 'assets/css/[name][extname]';
}
return 'assets/[name][extname]';
},
globals: {
vue: 'Vue',
},
},
},
},
server: {
hmr: {
protocol: 'ws',
},
},
});
EDIT:
More information, I've tracked this down to using
#input="handleInput($event.target, index)"
This right here breaks existing jQuery. Still no idea how to get around it
For anyone interested, How to wrap Vite build in IIFE and still have all the dependencies bundled into a single file?
I am trying to use webpack in ES6 but there is something wrong, the bundle is created but it seems to contain nothing.
src/index.js
console.log("inside");
export const testData = 15;
webpack.prod.js
import path from "path";
import HtmlWebpackPlugin from "html-webpack-plugin";
export default {
entry: {
myApp: './src/index.js',
},
plugins: [
new HtmlWebpackPlugin({
title: 'Production',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(".", './dist'),
clean: true,
module: true,
libraryTarget: 'umd'
},
module: {
rules: [
{
test: /\.(js)$/,
exclude: /node_modules/,
use: "babel-loader",
include: path.resolve(".", "src")
}
]
},
mode: "production",
devtool: "source-map"
};
package.json
{
...
"scripts": {
"build": "webpack --config ./webpack.prod.js"
},
"devDependencies": {
"html-webpack-plugin": "5.5.0",
"path": "0.12.7",
"webpack": "^5.72.0",
"webpack-cli": "^4.9.2",
"webpack-dev-middleware": "5.3.1",
"webpack-dev-server": "4.8.1",
"webpack-merge": "^5.8.0",
"babel-core": "6.26.3",
"babel-loader": "8.2.5",
"babel-preset-es2015": "6.24.1"
},
"type": "module"
...
After creating the bundle, the index.html file inside the dist folder I insert a log but when it starts does not show the log and gives me the error testData is not defined
dist/index.html
...
<head>
<title>Production</title>
<script defer="defer" src="myApp.bundle.js"></script>
<script>
window.addEventListener("load", () => {
console.log( "window load" );
console.log( "testData" );
console.log( testData ); // <--------------------- it give the error
}, false)
</script>
</head>
...```
Since you are exporting const value, which get disolve in build and webpack optimization process. It's better to make it part of some module and export it.
With few modifications you can get what you are expecting.
index.js
export default {
fuel: 100,
passengers: 7,
target: 'mars',
launch: function () {
console.log('Saturn VI ready to launch');
},
reportPorblem: function () {
return 'Houston, we have a problem';
},
land: function () {
console.log('Saturn VI landed successfully')
}
};
Remove module: true from output, what it is you can learn here.
output.library
Also you need to make few to changes to output in webpack. Remove libraryTarget and add library to output.
With library name here it's MyLibrary you can access exported details. Type property set how module to be exposed, webpack allow you to expose your module with certain standards with libraryTarget or library.type. Here its expected to be var.
webpack.prod.js
output: {
...
library: {
name: 'MyLibrary',
type: 'var',
export: 'default',
},
},
There are quite few options supported by webpack.
libraryTarget: "umd", enum
libraryTarget: "umd-module", ES2015 module wrapped in UMD
libraryTarget: "commonjs-module", ES2015 module wrapped in CommonJS
libraryTarget: "commonjs2", exported with module.exports
libraryTarget: "commonjs", exported as properties to exports
libraryTarget: "amd", defined with AMD defined method
libraryTarget: "this", property set on this
libraryTarget: "var", variable defined in root scope
You can learn more about libraryTarget here
So finally you can access all you wanted from library MyLibrary.
<script>
window.addEventListener("load", () => {
const mySpaceShip = MyLibrary;
console.log(`Traveling to ${MyLibrary.target} with ${MyLibrary.crewmates} crewmates`)
}, false)
</script>
I've been recently frustrated when developing using Webpack, because whenever I introuduce a new dependency there are no printed errors if I forget to include the depending JavaScript file(s).
I have created an example project: https://github.com/manstie/test-django-webpack
In the example you can see I have purposely set up the home module to depend on the utils module.
When you run the server and load localhost:8000 it is expected that you get a console log of Here and You have successfully depended on utils.js if you import all the necessary script files.
When utils.js is not included in index.html, base.js does not run at all - it silently fails, with no errors.
I am hoping there is a way to have errors show in the javascript console in these cases? I just can't find any resources or related questions on this issue.
Here is the webpack.common.js config:
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const { WebpackManifestPlugin } = require('webpack-manifest-plugin');
const dist = path.resolve(__dirname, 'static/bundles');
module.exports = {
entry: {
utils: './home/js/utils.js',
home: {
import: './home/js/index.js',
dependOn: ['utils']
}
},
resolve: {
modules: ['node_modules']
},
output: {
filename: '[name].[chunkhash].js',
path: dist,
publicPath: 'bundles/',
},
plugins: [
new CleanWebpackPlugin(), // deletes files inside of output path folder
new WebpackManifestPlugin({ fileName: "../../manifest.json" }),
],
optimization: {
moduleIds: 'deterministic', // so that file hashes don't change unexpectedly?
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 0,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name(module) {
// get the name. E.g. node_modules/packageName/not/this/part.js
// or node_modules/packageName
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
// npm package names are URL-safe, but some servers don't like # symbols
return `npm.${packageName.replace('#', '')}`;
},
},
},
},
},
};
Here is the html with a missing dependency - I am using django-manifest-loader in order to prevent JavaScript caching issues
...
{% load manifest %}
<script src="{% manifest 'runtime.js' %}"></script>
<!-- Is there a way to have webpack / js tell you about the missing dependency? -->
<!-- <script src="{% manifest 'utils.js' %}"></script> -->
<script src="{% manifest 'home.js' %}"></script>
...
And here are the js files:
index.js
import { testFunc } from "./utils.js";
console.log("Here");
testFunc();
utils.js
export function testFunc()
{
console.log("You have successfully depended on utils.js");
}
Thanks in advance.
I'm trying to create my own webpack configuration for a react / redux project. The configuration seems to work pretty well, but the bundle size is big (in development mode, I know how to reduce it in production mode)
My package.json looks like
"dependencies": {
"react": "15.4.2",
"react-dom": "15.4.2",
"react-hot-loader": "3.0.0-beta.6"
},
"devDependencies": {
"babel-core": "6.24.0",
"babel-loader": "6.4.1",
"babel-preset-react": "6.23.0",
"commitizen": "2.9.6",
"cz-conventional-changelog": "2.0.0",
"html-webpack-plugin": "2.28.0",
"webpack": "2.3.1",
"webpack-bundle-analyzer": "^2.3.1",
"webpack-dev-server": "2.4.2"
}
and my webpack configuration looks like
const webpack = require('webpack');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const HtmlWebpackPlugin = require('html-webpack-plugin');
const project = require('./project.config')
const __DEV__ = project.globals.__DEV__;
const __PROD__ = project.globals.__PROD__;
const __TEST__ = project.globals.__TEST__;
const APP_ENTRIES = [project.paths.client('index.js')];
if (__DEV__) {
APP_ENTRIES.unshift(
'react-hot-loader/patch',
`webpack-dev-server/client?http://${project.server_host}:${project.server_port}`,
'webpack/hot/only-dev-server'
)
}
const config = {
devtool: project.compiler_devtool,
entry: APP_ENTRIES,
output: {
path: project.paths.dist(),
filename: `[name].[${project.compiler_hash_type}].js`,
publicPath: project.compiler_public_path,
},
resolve: {
modules: [
project.paths.client(),
'node_modules',
],
},
module: {
rules: [{
test: /\.js?$/,
loader: 'babel-loader',
exclude: /node_modules/,
}],
},
plugins: [
new BundleAnalyzerPlugin(),
new HtmlWebpackPlugin({
template: project.paths.client('index.html'),
hash: false,
filename: 'index.html',
inject: 'body',
}),
],
node: {
fs: 'empty',
net: 'empty',
tls: 'empty'
}
}
if (__DEV__) {
config.plugins.push(
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin());
}
module.exports = config
Right now I got this output when I run webpack-dev-server
As you can see my bundle is bigger than 1.3 Mb but I'm only using a few libraries.
I tried to find out why is my bundle so big using webpack-bundle-analyser. Here is the result
It seems that react and readct-dom are the biggest libraries.
Is there a way to reduce the size of my bundle in development mode ??? Did I do something wrong ?
PS : I set compress = true in webpack-dev-server options, but it did not reduce the size of the bundle.
Here are more details about how I use webpack-dev-server
bin/start.js (I run it using node bin/start.js)
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const config = require('../config/webpack.config');
const project = require('../config/project.config');
function runDevServer() {
const devServer = new WebpackDevServer(webpack(config), {
compress: true,
hot: true,
publicPath: project.compiler_public_path,
stats: project.compiler_stats,
watchOptions: {
ignored: /node_modules/
},
});
// Launch WebpackDevServer.
devServer.listen(project.server_port, project.server_host, (err) => {
if (err) {
console.log('Webpack dev server encountered an error', err);
return reject(err);
}
console.log(`Listening at ${project.server_host}:${project.server_port}`);
});
}
runDevServer()
src/index.js (my application. Home is just a component returning "Hello world")
import React from 'react';
import ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader';
import Home from './routes';
const render = () => {
try {
ReactDOM.render(
<AppContainer>
<Home />
</AppContainer>,
document.getElementById('root')
);
} catch (err) {
console.error(err)
}
};
if (module.hot) {
module.hot.accept('./routes', () => render());
}
render()
Having a large bundle during development is normal, 1.3MB is still small and should normally be fast to load. Since you are using webpack-dev-server, it should take no time to reload after each change. If you use compress = true, you bundle size will be the same, except that it will be gzip compressed when the client ask for it (you should see gzip in the network tab). I will recommend you not to try to reduce the size of the bundle for development because it will be hard to debug and slower to refresh. IF you really want to reduce the size, you can uglify and minify only the libraries code. This is a very common approach, you will basically have every library in a file named vendor.js that you will minify, uglify, etc. In another file, bundle.js you will have all the code you've wrote. This will reduce significantly the total size. but you will have to load 2 js files. You can also active the tree shaking for webpack 2. Her are more info about the tree shaking. You can refer to this answer on how to bundle vendor script separately and read more about code splitting here. But keep in mind that you dev bundle will always be of a bigger size because it is very handy when debugging.
I use Webpack dev server and browserHistory in React Router to manipulate with urls by HTML5 History API. historyapifallback-option does not work in my webpack config file. After refreshing http://localhost:8080/users or http://localhost:8080/products I got 404.
webpack.config.js
var webpack = require('webpack');
var merge = require('webpack-merge');
const TARGET = process.env.npm_lifecycle_event;
var common = {
cache: true,
debug: true,
entry: './src/script/index.jsx',
resolve: {
extensions: ['', '.js', '.jsx']
},
output: {
sourceMapFilename: '[file].map'
},
module: {
loaders: [
{
test: /\.js[x]?$/,
loader: 'babel-loader',
exclude: /(node_modules)/
}
]
},
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery"
})
]
};
if(TARGET === 'dev' || !TARGET) {
module.exports = merge(common,{
devtool: 'eval-source-map',
devServer: {
historyApiFallback: true
},
output: {
filename: 'index.js',
publicPath: 'http://localhost:8090/assets/'
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('dev')
})
]
});
}
index.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
<title>Test</title>
</head>
<body>
<div id="content">
<!-- this is where the root react component will get rendered -->
</div>
<script src="http://localhost:8090/webpack-dev-server.js"></script>
<script type="text/javascript" src="http://localhost:8090/assets/index.js"></script>
</body>
</html>
index.jsx
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import {Router, Route, useRouterHistory, browserHistory, Link} from 'react-router';
class Home extends Component{
constructor(props) {
super(props);
}
render() {
return <div>
I am home component
<Link to="/users" activeClassName="active">Users</Link>
<Link to="/products" activeClassName="active">Products</Link>
</div>;
}
}
class Users extends Component{
constructor(props) {
super(props);
}
render() {
return <div> I am Users component </div>;
}
}
class Products extends Component{
constructor(props) {
super(props);
}
render() {
return <div> I am Products component </div>;
}
}
ReactDOM.render(
<Router history={browserHistory} onUpdate={() => window.scrollTo(0, 0)}>
<Route path="/" component={Home}/>
<Route path="/users" component={Users} type="users"/>
<Route path="/products" component={Products} type="products"/>
</Router>
, document.getElementById('content'));
package.json
{
"name": "test",
"version": "1.0.0",
"description": "",
"main": "index.jsx",
"scripts": {
"start": "npm run serve | npm run dev",
"serve": "./node_modules/.bin/http-server -p 8080",
"dev": "webpack-dev-server -d --progress --colors --port 8090 --history-api-fallback"
},
"author": "",
"license": "MIT",
"dependencies": {
"events": "^1.1.0",
"jquery": "^2.2.3",
"path": "^0.12.7",
"react": "^15.0.2",
"react-dom": "^15.0.2",
"react-mixin": "^3.0.5",
"react-router": "^2.4.0"
},
"devDependencies": {
"babel": "^6.5.2",
"babel-core": "^6.8.0",
"babel-loader": "^6.2.4",
"babel-polyfill": "^6.8.0",
"babel-preset-es2015": "^6.6.0",
"babel-preset-react": "^6.5.0",
"babel-register": "^6.8.0",
"http-server": "^0.9.0",
"webpack": "^1.13.0",
"webpack-dev-server": "^1.14.1",
"webpack-merge": "^0.12.0"
}
}
I tried to change devServer in my config, but it didn't help:
devServer: {
historyApiFallback: {
index: 'index.html',
}
},
devServer: {
historyApiFallback: {
index: 'index.js',
}
},
devServer: {
historyApiFallback: {
index: 'http://localhost:8090/assets',
}
},
devServer: {
historyApiFallback: {
index: 'http://localhost:8090/assets/',
}
},
devServer: {
historyApiFallback: {
index: 'http://localhost:8090/assets/index.html',
}
},
devServer: {
historyApiFallback: {
index: 'http://localhost:8090/assets/index.js',
}
},
devServer: {
historyApiFallback: {
index: 'http://localhost:8090/assets/index.js',
}
},
output: {
filename: 'index.js',
publicPath: 'http://localhost:8090/assets/'
},
I meet the same question today.
let config in webpack.config.js: output.publicPath be equal to devServer.historyApiFallback.index and point out html file route。my webpack-dev-server version is 1.10.1 and work well. http://webpack.github.io/docs/webpack-dev-server.html#the-historyapifallback-option doesn't work, you must point out html file route.
for example
module.exports = {
entry: "./src/app/index.js",
output: {
path: path.resolve(__dirname, 'build'),
publicPath: 'build',
filename: 'bundle-main.js'
},
devServer: {
historyApiFallback:{
index:'build/index.html'
},
},
};
historyApiFallback.index indicate that when url path not match a true file,webpack-dev-server use the file config in historyApiFallback.index to show in browser rather than 404 page. then all things about your route change let your js using react-router do it.
output: {
...
publicPath: "/"
},
Adding public path solved this for me
I had this problem and was only able to fix it using index: '/' with webpack 4.20.2
historyApiFallback: {
index: '/'
}
There is a very tricky thing going on here!
The 404 can be two totally different things below. You can open the Network tab of Chrome to see if it's the initial request that is 404, or the assets within.
If you are not afraid of terminal, you can also do curl -v http://localhost:8081/product to see the actual HTTP response code.
Case 1: 404 on the initial HTML page
This is the case that set-up with historyFallbackApi is not correct.
Usually just historyApiFallback: true should work in devServer configs of Webpack. (source) Yet, if you happen to have a custom output folder, you have to set the index property of historyApiFallback to the same path, as advised by the other answers on this question like jsdeveloper's or echzin's.
Case 2: 404 on the assets (e.g. bundle.js or vendor.js)
This one is quite interesting!
In this case you do actually get the initial HTML (i.e. if you add view-source: before your URL to become view-source:http://localhost:8081/admin, you see your HTML, and/or curl command shows the HTML), but the page doesn't work in the browser.
What historyApiFallback does, which might sound like a magic, is literally just setting req.url of the Express server to be the index.html file, for any incoming GET request within the domain. (The main two lines in the Source Code)
However, if the path to your assets is relative (e.g. in the view-source, you see <script src="vendor.js"></script>) AND the path that you are landing is not at the same path-depth as index (e.g. you are trying to load http://localhost:8081/admin/user/12/summary while the main server is at http://localhost:8081/admin), what happens is it cannot find the .js files for your JavaScript code. (in my case http://localhost:8081/admin/user/12/vendor.js)
Note that whatever router that deals with HTML5 History here (react router or vue router), knows how to initialize the internal path to document.location.href upon the initial load. But, it doesn't know where is the "root" to properly update the assets' path to it. (And maybe it's not even its job, in terms of responsibility.) As a result, the path to assets would be calculated based on the URL's path, not the index.html's path! So, for just src="vendor.js" with no absolute / prefix, it tries to find /admin/user/12/vendor.js instead of /vendor.js.
What you need to do here is to make sure the output path of your WebPack is an absolute path and starts with /, so regardless of the landing URL, it would always work. i.e. it's always <script src="/vendor.js"></script> in the HTML source.
To do so, you have to set output.publicPath to be an absolute path (with or without domain). You can verify this via the view-source: or curl technique above. :)
Ref.: https://webpack.js.org/configuration/dev-server/#devserver-historyapifallback
This works with any react-router
You have to add historyApiFallback: true
module.exports = {
cache: true,
entry: "./index.js",
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'public')
},
context: SRC,
devServer: {
contentBase: path.resolve(__dirname, 'public/assets'),
stats: 'errors-only',
open: true,
port: 8080,
compress: true,
historyApiFallback: true
},
...
}
If you're finding that some paths work but others don't, you may be falling foul of the "dot rule". Per the docs:
When using dots in your path (common with Angular), you may need to
use the disableDotRule:
webpack.config.js
module.exports = {
//...
devServer: {
historyApiFallback: {
disableDotRule: true,
},
},
};
For me this setting worked out:
module.exports = {
output: {
path: path.resolve(__dirname, 'build'),
publicPath: '/',
filename: 'bundle-main.js'
},
devServer: {
/* Telling the server to fallback to index.html if the route is not found at the backend server */
historyApiFallback: true,
}
};
In webpack.config.js just add these lines
devServer: {
open: true,
historyApiFallback: true,
allowedHosts: 'all',
hot: true
},
and add this -> // prefixes: ["http://your url:8081/"]
it's working perfectly.