I'm using Nuxt v2.15.8 to create a static website using nuxt generate command. I expect the requests to the server for fetching the main js and CSS files to be made to / path, as I specified in my nuxt.cofig:
export default {
ssr: false,
target: 'static',
// ...
build: {
publicPath: process.env.NODE_ENV === 'development' ? '/_nuxt/' : '/',
},
// ...
}
When I visit the generated static website, despite what I specified in the build config, at first it tries to load data from /_nuxt/ path which does not exist. As you can see in the attached pictures, after getting a 404 error for this route, it loads data successfully from the correct path which is /, but it should not send those requests to /_nuxt path to begin with.
Request to the wrong path
Request to the correct path
Any idea about how can I eliminate those initial requests to /_nuxt/?
So I fixed the issue by adding the following lines to nuxt.config.js:
export default {
ssr: false,
target: 'static',
// ...
build: {
publicPath: process.env.NODE_ENV === 'development' ? '/_nuxt/' : '/',
},
// added the following lines
hooks: {
render: {
resourcesLoaded(resources) {
const path = `/`
resources.clientManifest && (resources.clientManifest.publicPath = path)
resources.modernManifest && (resources.modernManifest.publicPath = path)
resources.serverManifest && (resources.serverManifest.publicPath = path)
}
}
},
// ...
}
I understand how to control what the publicPath would be based on process.env.NODE_ENV variable.
My vue.config.js is working as expected, but only for production and non-production environments. How would I control the publicPath variable when I have qa, dev, and stage environments?
Note: I have added my .env.qa, .env.dev, and .env.stage.
vue.config.js:
module.exports = {
publicPath: process.env.NODE_ENV === 'production'
? '/ProductionEnv/'
: '',
"transpileDependencies": [
"vuetify"
]
}
I would compute publicPath in vue.config.js like this:
function getPublicPath() {
switch (process.env.NODE_ENV) {
case 'production': return '/ProductionEnv/'
case 'qa': return '/QaEnv/'
case 'dev': return '/DevEnv/'
case 'stage': return '/StageEnv/'
default: return ''
}
}
module.exports = {
publicPath: getPublicPath()
}
If you need conditional behavior based on the environment you can use a function and mutate the values inside, or return an object which will be merged.
// vue.config.js
module.exports = {
configureWebpack: config => {
if (process.env.NODE_ENV === 'production') {
// mutate config for production...
} else {
// mutate for development...
}
}
}
See more here: configureWebpack
I have following setup within my app:
.env
VUE_APP_API_BASE_URL=http://localhost:8000/v5/
VUE_APP_HTTPS=true
VUE_APP_HOST=local.ei.run
I'm setting the env variables within the vue.config.js and however I am trying to work out a way to call them within there as follows
// vue.config.js
var webpack = require('webpack')
module.exports = {
configureWebpack: {
plugins: [
new webpack.DefinePlugin({
'process.env': {
'API_BASE_URL': JSON.stringify(process.env.VUE_APP_API_BASE_URL),
'HOST': JSON.stringify(process.env.VUE_APP_HOST)
}
})
]
},
// options...
devServer: {
open: process.platform === 'darwin',
host: process.env.HOST,
https: false,
disableHostCheck: true
}
}
So basically how can I set my host dynamically based off the env?
I'm trying to set up an express server for developing a React project with hot-reload, but the HMR thing requests the wrong path for some reason and I can't fix it by changing the "publicPath" option. It requests the "public" folder even though this is the folder I server static files from, so it causes the error. How do I tweak the configuration so HMR starts working?
This is the error I see in the console:
My Webpack config:
const path = require('path');
const webpack = require('webpack');
const tsRules = {
test: /\.ts(x?)$/,
use: [
{loader: 'react-hot-loader/webpack'},
{loader: 'awesome-typescript-loader'},
]
};
const scssRules = {
test:/\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader']
};
//All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
const jsRules = {
enforce: "pre",
test: /\.js$/,
loader: "source-map-loader"
};
module.exports = {
entry: [
'./js/App.tsx',
'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000'
],
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'public'),
publicPath: '.'
},
resolve: {
extensions:['.ts','.tsx','.js', '.json']
},
module: {
rules: [
tsRules,
scssRules
]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin()
],
externals: {
//"react": "React",
//"react-dom": "ReactDOM"
},
devtool: '#source-map',
}
Server.js:
import http from 'http';
import express from 'express';
import bodyParser from 'body-parser';
import openBrowser from 'react-dev-utils/openBrowser';
var webpack = require('webpack');
var webpackDevMiddleware = require('webpack-dev-middleware');
var webpackHotMiddleware = require('webpack-hot-middleware');
var config = require('../webpack.config');
var compiler = webpack(config);
const app = express();
const port = process.env.PORT || 3090;
//middleware
app.use(webpackDevMiddleware(compiler, {
noInfo: true,
publicPath: config.output.publicPath
}));
app.use(webpackHotMiddleware(compiler,{
log: console.log,
path: '/__webpack_hmr'
}));
app.use(express.static('public'));
const server = http.createServer(app);
server.listen(port, () => {
console.log('listening on port' + port);
openBrowser(`http://localhost:${port}/`);
});
I also ran into this problem and here's how I solved it:
1) Inside webpack config, order of the "entry" configs matter, so set 'webpack-hot-middleware/client...' as the first item of the array
2) Define a public path for the middleware like so:
"webpack-hot-middleware/client?path=http://localhost:3000/__webpack_hmr"
Hope this helps!
I'm new to webpack so it's probably a stupid mistake on my part.
This is my project setup (root, atleast the relevant part):
+-- public
| |
| +-- index.html
|
+-- src
| |
| +-- App.vue
| +-- main.js
| +-- assets
|
+-- package.json
|
+-- webpack.config.js
Now I would like to use the webpack-dev(and hot)-middleware to serve my index.html and create a bundle in memory from my src folder. Now I can get the middleware set up (via the npm page) and I see the bundle gets created (through logging in console) but two things are not clear to me:
How to serve index.html
How to use the bundle thats created in memory?
Can somebody please explain how that middleware works exactly? This is my webpack config file (which is needed to plug into the middleware, it's just a copy of the webpack config file that gets created through the vue-cli):
var path = require('path')
var webpack = require('webpack')
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'build.js'
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
// vue-loader options go here
}
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
}
]
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.common.js'
}
},
devServer: {
historyApiFallback: true,
noInfo: true
},
devtool: '#eval-source-map'
}
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = '#source-map'
// http://vue-loader.vuejs.org/en/workflow/production.html
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
warnings: false
}
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
}
I know this is probably not the correct configuration, can someone maybe point out some things?
Thanks in advance for your help!
Edit (this setup works):
My server.js now:
var express = require('express');
var logger = require('morgan');
var bodyParser = require('body-parser');
var exphbs = require('express-handlebars');
var helmet = require('helmet');
var redis = require('redis');
var redisAdapter = require('socket.io-redis');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);
// use the redis adapter to create sticky sessions, this is needed because of the different clusters
io.adapter(redisAdapter( require('./app/lib/config').credentials.redis.url ));
//setup security ===============================================================
require('./app/lib/security-setup')(app, helmet);
// configuration ===============================================================
app.use(logger('dev')); // log every request to the console
// set up our express application ==============================================
// Make the body object available on the request
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
//set handlebars as default templating engine
app.engine('handlebars', exphbs());
app.set('view engine', 'handlebars');
// serve the static content ====================================================
if (app.settings.env === 'development') {
var webpackConfig = require('./webpack.config.js')
var compiler = require('webpack')(webpackConfig)
var devMiddleware = require('webpack-dev-middleware')(compiler, {
publicPath: webpackConfig.output.publicPath,
})
var hotMiddleware = require('webpack-hot-middleware')(compiler)
app.use(devMiddleware)
app.use(hotMiddleware)
} else {
app.use(express.static(__dirname + '/public'));
}
// set up global variables =====================================================
app.use(function(req, res, next) {
//set the io object on the response, so we can access it in our routes
res.io = io;
next();
});
// routes ======================================================================
require('./app/routes.js')(app); // load our routes and pass in our app
// export so bin/www can launch ================================================
module.exports = {app: app, server: server};
my ./bin/www:
#!/usr/bin/env node
/**
* Module dependencies.
*/
var app = require('../server').app;
var cluster = require('cluster');
var debug = require('debug')('temp:server');
var http = require('http');
var numCPUs = process.env.WORKERS || require('os').cpus().length;
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
if (cluster.isMaster) {
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
// If a worker dies, log it to the console and start another worker.
cluster.on('exit', function(worker, code, signal) {
console.log('Worker ' + worker.process.pid + ' died.');
cluster.fork();
});
// Log when a worker starts listening
cluster.on('listening', function(worker, address) {
console.log('Worker started with PID ' + worker.process.pid + '.');
});
} else {
/**
* Create HTTP server.
*/
var server = require('../server').server;
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
}
// The rest of the bin/www file.....
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
debug('Listening on ' + bind);
}
My working webpack.config.js:
var path = require('path')
var webpack = require('webpack')
var HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: [
'webpack/hot/dev-server',
'webpack-hot-middleware/client',
'./src/main.js'
],
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'build.js'
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
// vue-loader options go here
}
},
{
test: /\.less$/,
loader: "style!css!less"
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
}
]
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.common.js'
}
},
devServer: {
historyApiFallback: true,
noInfo: true
},
devtool: '#eval-source-map',
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.resolve(__dirname, 'public/index.html'),
inject: true
}),
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
]
}
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = '#source-map'
// http://vue-loader.vuejs.org/en/workflow/production.html
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
warnings: false
}
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
}
How to serve index.html
To serve the index.html file you need to have a dev server running. It sounds like you are successfully doing this based on your logging of the creation of your bundle in memory. However I can't see a file for it in your set up? I've assumed another file below called: dev-server.js and it would be your entry point to serving your app, i.e. npm run dev:
package.json
"scripts": {
"dev": "node dev-server.js",
...
In webpack this dev server is generally going to be express and it's the config that you pass to your express server that will serve your index.html. As you are wanting hot-reloading you will pass express your webpack config through your middleware layers.
For hot reloading you'll need two main middlewares:
webpack-dev-middleware
webpack-hot-middleware
Then you will need to pass your config to webpack and pass your webpack compiler to the middleware, i.e.
dev-server.js
var app = require('express')()
var webpackConfig = require('./webpack.config.js')
var compiler = require('webpack')(webpackConfig)
var devMiddleware = require('webpack-dev-middleware')(compiler, {
publicPath: webpackConfig.output.publicPath,
})
var hotMiddleware = require('webpack-hot-middleware')(compiler)
app.use(devMiddleware)
app.use(hotMiddleware)
Now the key file becomes your webpack config, referenced above as: ./webpack.config.js
This leads to your next question: How to use the bundle thats created in memory?
The file you posted above looks to be about right and the key parts in regards to using the bundle are held within the output, you have:
webpack.config.js
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'build.js'
},
You are creating the bundle at dist/build.js relative to the current working directory. This is essentially where you need to point any references to that file within your index.html, i.e. <script src="/dist/build.js"></script>
How you do this can be manual however we'd often add a further webpack plugin to automatically build this script tag within your output html (again in this case in memory):
webpack.config.js
plugins: [
// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.resolve(__dirname, 'public/index.html'),
inject: true
}),
...
The HtmlWebpackPlugin is now essentially how you reference what the output file is called: filename and crucially where it is stored: template so if you wanted to move your index.html file this is where you tell webpack where to find it. The HtmlWebpackPlugin will now place the output 'index.html' at the publicPath referenced earlier, so to get to this file you would call /dist/index.html.
Finally you'll need some further plugins for hot reloading so your entire webpack plugins array will look like:
webpack.config.js
plugins: [
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'bar/index.html',
inject: true
})
]
Now we return to the dev-server.js file - or whatever yours is called that you are configuring express within - and fire up the configured express server:
dev-server.js
module.exports = app.listen(8080, function (err) {
if (err) {
console.log(err)
return
}
})
so with the above config you'd open the following uri: localhost:8080/dist/index.html