react-js based web page crashes when refreshing the page - javascript

I am using Reactjs, redux, webpack and webpack server to develop a website.
The website crashes when I refresh a certain pages. To be more specific when I visit http://localhost:8080/article/Id the page works like a charm, but when I hit refresh the page turns to white with errors.
GET http://localhost:8080/article/scripts/vendor.bundle.js?cfd99d78961c5e13afca 404 (Not Found)
Refused to execute script from 'http://localhost:8080/article/scripts/vendor.bundle.js?cfd99d78961c5e13afca' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled.
The error displayed by Chrome is this and this.
My webpack config page looks like this :
//webpack.config.js
var webpack = require('webpack')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var UglifyJsPlugin = require('uglifyjs-webpack-plugin')
var path = require('path')
var isProd = process.env.NODE_ENV === 'production'
var cssDev = ['style-loader', 'css-loader', 'sass-loader']
var cssProd = ExtractTextPlugin.extract({
use: [{
loader: 'css-loader',
options: {
minimize: 'true'
}
},
{
loader: 'sass-loader'
}
],
fallback: 'style-loader',
publicPath: '/docs'
})
var cssConfig = isProd ? cssProd : cssDev
module.exports = {
entry: {
'app': './src/app.js',
'vendor': './src/vendor.js'
},
output: {
path: path.resolve(__dirname, 'docs'),
filename: 'scripts/[name].bundle.js'
},
module: {
rules: [{
test: /\.jsx?/,
exclude: [/node_modules/, path.resolve(__dirname, './src/lib')],
use: 'babel-loader',
},
{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
loader: 'file-loader?name=assets/[name].[hash].[ext]'
},
{
test: /\.s?css$/,
use: cssConfig
},
{
test: /\.(jpe?g|png|gif|svg)$/i,
use: [
'file-loader?name=images/[name].[ext]',
'image-webpack-loader'
]
},
{
test: /\.(xml|json|ico)$/i,
use: [
'file-loader?name=images/[name].[ext]',
]
}
]
},
devServer: {
contentBase: path.join(__dirname, 'docs'),
compress: true,
hot: true,
stats: 'errors-only',
open: true,
disableHostCheck: true,
historyApiFallback: true, //when refreshing
inline: true //reloads the entire browser page
},
plugins: [
new ExtractTextPlugin({
filename: 'styles/[name].bundle.css',
disable: !isProd,
allChunks: true
}),
new HtmlWebpackPlugin({
title: 'React MovieDB - Powered by The Movie Database',
minify: {
collapseWhitespace: true
},
hash: true,
template: './src/index.ejs'
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(),
new UglifyJsPlugin()
],
watch : true
}
And I run the project with : "dev": "webpack-dev-server",
And this how my index.ejs looks like:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="apple-touch-icon" sizes="180x180" href="<%= require('./icons/apple-touch-icon.png') %>">
<link rel="icon" type="image/png" sizes="32x32" href="<%= require('./icons/taleed.png') %>">
<link rel="icon" type="image/png" sizes="16x16" href="<%= require('./icons/taleed.png') %>">
<link rel="manifest" href="<%= require('./icons/manifest.json') %>">
<link rel="mask-icon" href="<%= require('./icons/safari-pinned-tab.svg') %>" color="#01d277">
<script src="//cdnjs.cloudflare.com/ajax/libs/wavesurfer.js/2.0.5/wavesurfer.min.js"></script>
<meta name="theme-color" content="#ffffff">
<title>
Archive Search Engine
</title>
</head>
<body>
<div class="smoke-screen"></div>
<div id="root">
</div>
</body>
</html>
Honestly, I can't manage to figure out the main source of the problem because the project is based on a template and I couldn't get in touch with the template' maker.

You're trying to grab the bundle from the base path, which works just fine. But when you go to a different part of the app, you are requesting to get the bundle from /articles/app.bundle.... So you need to change the index file to pull from the absolute path from the server for the bundle instead of a relative path.
You need to do something like this to your server.js (if you use node);
app.get('/*', (req, res) => {
res.send(template({
css: bundle.css,
js: bundle.js,
}));
});
for example on using mustache as the template engine.

Related

Phoenix 1.4 server is delivering default HTML instead of requested JS files

I am trying to render Vue.js directly in my Phoenix 1.4 application. But the following error is occurring after installing Vue-router:
Refused to execute script from 'http://localhost:4000/0.app.js'
because its MIME type ('text/html') is not executable, and strict MIME
type checking is enabled.
My Phoenix router.ex file looks as follows:
scope "/", Web do
pipe_through :browser
get "/*path", PageController, :index
end
My webpack.config.js:
const path = require('path');
const glob = require('glob');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = (env, options) => ({
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
},
extensions: ['*', '.js', '.vue', '.json']
},
optimization: {
minimizer: [
new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: false }),
new OptimizeCSSAssetsPlugin({})
]
},
entry: {
'./js/app.js': glob.sync('./vendor/**/*.js').concat(['./js/app.js'])
},
output: {
filename: 'app.js',
path: path.resolve(__dirname, '../priv/static/js')
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
},
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.s(c|a)ss$/,
use: [
'css-loader',
{
loader: 'sass-loader',
// Requires sass-loader#^7.0.0
options: {
implementation: require('sass'),
fiber: require('fibers'),
indentedSyntax: true // optional
}
}
]
},
{ test: /\.(png|woff|woff2|eot|ttf|svg)$/, use: 'url-loader' }
]
},
plugins: [
new MiniCssExtractPlugin({ filename: '../css/app.css' }),
new CopyWebpackPlugin([{ from: 'static/', to: '../' }]),
new VueLoaderPlugin()
]
});
I am currently seeing an issue with (I think) the cache manifest whereby my site delivers HTML and CSS files properly but for JS files, the default HTML template is being delivered - which results in an erro.
I can see in the Chrome Dev Tools Network tab that the response from this request http://localhost:4000/0.app.js is the app.html.eex file.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Phoenix Framework</title>
</head>
<body>
<div id="vue-app"></div>
<script type="text/javascript" src="<%= Routes.static_path(#conn, "/js/app.js") %>"></script>
</body>
</html>
Any help greatly appreciated.
If you use a wildcard catch-all route to show your index page, and requests to resources are hitting that route, this means that Plug.Static is not set up correctly in your endpoint.ex.
Ours looks something like this:
plug Plug.Static,
at: "/", from: :my_app_name, gzip: true,
only: ~w(css images js favicon.ico robots.txt fonts)
put that anywhere before you do plug MyApp.Router.
Another reason could be that 0.app.js simply doesn't exist in priv/static/js. Check there to see if it isn't actually called app.js or has a hash appended to its name.

How can I build a React app with Webpack and import an assets folder?

My main problem is that I'm trying to build a React Application but the assets folder is missing and I don't know how to import it and configure it in webpack.conf. Another problem is the relative route of index.html: I don't know if it will be affected in the application build.
Thanks in advance.
Application Tree
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>App</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="shortcut icon" href="src/assets/favicon.ico"/>
</head>
<body>
<div id="app"></div>
</body>
</html>
Webpack Config:
const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: './src/index.tsx',
output: {
path: path.resolve(__dirname, 'build'),
filename: 'bundle.js'
},
resolve: {
// Add '.ts' and '.tsx' as resolvable extensions.
extensions: [".ts", ".tsx", ".js", ".jsx", ".json"]
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: "awesome-typescript-loader"
},
// All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
{ enforce: "pre", test: /\.js$/, loader: "source-map-loader" },
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"sass-loader"
]
}
]
},
plugins: [
new HTMLWebpackPlugin({
template: 'public/index.html'
}),
new MiniCssExtractPlugin("style.css")
],
// Enable sourcemaps for debugging webpack's output.
devtool: "source-map",
devServer: {
historyApiFallback: true,
port: 3000
}
};
You can use webpack-copy-plugin to copy additional folders / files as part of the build process
new CopyPlugin([{
from: path.resolve(__dirname, 'src', 'assets'),
to: path.resolve(__dirname, 'build', 'assets')
}])
As #James mentioned: "You can use webpack-copy-plugin to copy additional folders / files as part of the build process"
However, there is a small error while using you example #James, it should be like this:
webpack.config.js
plugins: [
//...
new CopyPlugin({
patterns: [
{ from: path.resolve(__dirname, 'src', 'assets'), to: path.resolve(__dirname, 'build', 'assets') },
],
})
],
Hope it helps (upvote james for his help if it helps you too)

Index.html template isn't loading favicon for HtmlWebpackPlugin

I'm trying to load a favicon using the index.html that is the template for the HtmlWebpackPlugin but it's not loading.
That is how my Webpack config looks like:
'use strict'
const webpack = require('webpack')
const { join, resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
devtool: 'cheap-module-eval-source-map',
entry: join(__dirname, 'src', 'index'),
output: {
filename: 'bundle.js',
path: resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
},
{
test: /\.s?css$/,
exclude: /node_modules/,
use: ['style-loader', 'css-loader', 'sass-loader']
}
]
},
resolve: {
extensions: ['.js']
},
devServer: {
contentBase: resolve(__dirname, 'build')
},
plugins: [
new HtmlWebpackPlugin({
template: join(__dirname, 'public', 'index.html')
})
]
}
And that is my index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="shortcut icon" href="favicon.ico">
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
</body>
</html>
HTMLWebpackPlugin will not parse the HTML to find your resources. You'll need to include it like this in your template:
index.html
<link rel="shortcut icon" href="${require('./favicon.ico')}">
You'll also need to include file-loader for your .ico file:
webpack.config.js
{
test: /\.ico$/,
loader: 'file-loader'
}
HtmlWebpackPlugin has an option called favicon which lets you inject an icon link into the header in development or production.
new HtmlWebpackPlugin({
title: "Title Here",
template: "./src/index.html",
favicon: "./src/favicon.ico",
inject: "body",
})
You should also have a rule to grab the icon, and also import it in your bundler file.
// # Target: favicon
{
test: /\.ico$/i,
type: "asset/resource",
// Use 'generator' to output unique name (based on webpack pattern e.g. [name], [ext], etc.)
generator: {
filename: "[name][ext][query]"
}
},
NB: I'm writing for Webpack 5
I'm not sure if Webpack 4 has the type: "asset/resource" feature, but I assume you can achieve the same thing with file-loader and its options.
{
test: /\.ico$/i,
use: {
loader: "file-loader",
options: {
name: "[name].[ext]"
}
}
},
*Not guaranteed for Webpack 4.

HTML5 ServiceWorker slows down page loading dramatically

I am using Google Lighthouse to figure out ways to speed up my page loading time.
One of the red points was that I was missing a Service Worker.
So I get one working.
Before I used the Service Worker my page was fully loaded and ready in 7 seconds.
After I added the Service Worker my page fully loads in 29 seconds!
When I check on Fiddler was is going on, it looks like the Service Worker is loading all the files before letting the page render.
I am using the SWPrecacheWebpackPlugin plugin in my webpack.config to automatically generate the service-worker.js file because my libraries name are hashed automatically to optimize the caching.
This is my webpack.config:
const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ChunkManifestPlugin = require('chunk-manifest-webpack-plugin');
const WebpackChunkHash = require('webpack-chunk-hash');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
var SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
// To handle the regeneratorRuntime exception
require('babel-polyfill');
require('file-loader');
require('css-loader');
require('style-loader');
require('html-webpack-template');
/* Shared Dev & Production */
const config = {
context: path.resolve(__dirname, 'src'),
entry: {
index: [
// To handle the regeneratorRuntime exception
'babel-polyfill',
'./index.js'
],
vendor: ['react', 'react-dom', 'react-router', 'react-redux', 'history', 'react-router-dom', 'redux', 'react-router-redux', 'redux-form', 'lodash'],
},
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/,
},
{
test: /\.(ico|jpg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/,
exclude: /\/favicon.ico$/,
use: [
{
loader: 'file-loader',
query: {
name: '[path][name][hash].[ext]',
publicPath: '/'
}
}
]
},
{
test: /\.css$/,
include: path.join(__dirname, 'src/style'),
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.(ico)(\?.*)?$/,
exclude: /node_modules/,
use: {
loader: 'file-loader',
options: { name: '[name].[ext]' },
},
},
{
test: /\.xml/,
use: {
loader: 'file-loader',
options: { name: '[name].[ext]' },
},
},
],
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].bundle.js',
publicPath: '/',
},
resolve: {
extensions: ['.js'],
modules: [path.resolve(__dirname, 'src'), 'node_modules'],
},
plugins: [
// New moment.js optimization
new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /en/),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: Infinity
}),
new webpack.optimize.ModuleConcatenationPlugin(),
new HtmlWebpackPlugin({
appMountId: 'app-root',
inlineManifestWebpackName: 'webpackManifest',
template: 'templateIndex.html',
title: 'Our Site',
}),
new SWPrecacheWebpackPlugin()
],
devServer: {
historyApiFallback: true,
},
};
//if (process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test') {
config.plugins = [
...config.plugins,
new BundleAnalyzerPlugin({analyzerMode: 'static'}),
];
//}
//if (process.env.NODE_ENV === 'production') {
config.output.filename = '[name].[chunkhash].js';
config.plugins = [
...config.plugins,
new webpack.HashedModuleIdsPlugin(),
new WebpackChunkHash(),
/*new ChunkManifestPlugin({
filename: 'chunk-manifest.json',
manifestVariable: 'webpackManifest',
inlineManifest: true,
}),*/
];
//}
module.exports = config;
And my templateIndex.html (I am using HtmlWebpackPlugin for the same reason, hashed files name)
The service loader is loaded at the end of the body.
If I remove it, things are fast again. But then Lighthouse complains...
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#242424" />
<title itemprop="name">My Site</title>
<base href="/">
<link rel="apple-touch-icon" sizes="57x57" href="/images/favicon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/images/favicon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/images/favicon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="/images/favicon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="/images/favicon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="/images/favicon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/images/favicon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/images/favicon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/images/favicon-180x180.png">
<link rel="icon" type="image/png" href="/images/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="/images/favicon-192x192.png" sizes="192x192">
<link rel="icon" type="image/png" href="/images/favicon-160x160.png" sizes="160x160">
<link rel="icon" type="image/png" href="/images/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="/images/favicon-16x16.png" sizes="16x16">
<link type="image/png" href="/images/favicon.png" rel="icon">
<link rel="icon" href="/images/favicon.ico" />
<link rel="shortcut icon" href="/images/favicon.png" />
</head>
<body>
<div id="app"></div>
<script type="text/javascript">
// ServiceWorker is a progressive technology. Ignore unsupported browsers
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js').then(function(registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, function(err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
} else {
console.log('CLIENT: service worker is not supported.');
}
</script>
</body>
</html>
I have searched on Google and I could not find anything related to the issue I am experiencing.
Is that the expected behavior? Because if that is so, then I will not use a Service Loader.
Otherwise, does anyone have a clue about how to get my page to load in parallel to the Service Worker?
You should most likely register the SW after the load event of the page. Currently you're registering the SW right away when the browser executes the script in the HTML script tag. The registration is even happening before any critical app logic since Webpack most likely inserts your actual JS files just before the closing body tag.
More on the topic over here with good details and explanations!
https://developers.google.com/web/fundamentals/primers/service-workers/registration

How after building the dist folder in Webpack, move the bundle.js and css file into the statics folder?

When I run my npm run build or npm run build-dev
It creates the index.html and manage2.bundle.js and manage2.css files in the root. I need to move those files into the static directory.
So the generated index.html below will actually work, with the correct paths:
<!doctype html>
<html lang="en">
<head>
<title>Manage2</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="description" content="The TickerTags backend manage app">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="style.css">
<link href="https://fonts.googleapis.com/css?family=Roboto+Condensed:300|Source+Sans+Pro:200,600" rel="stylesheet">
<link rel="icon" type="image/x-icon" href="static/favicon.ico">
<link href="/static/manage2.css" rel="stylesheet"></head>
<body>
<div id="manage2"></div>
<script type="text/javascript" src="/static/manage2.bundle.js"></script></body>
</html>
How is this acomplished? webpack.config below
const fs = require('fs');
const webpack = require('webpack')
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const path = require("path");
const dist = path.resolve(__dirname, "dist");
const src = path.resolve(__dirname, "src");
const environment = process.env.NODE_ENV;
const stream = fs.createWriteStream("src/services/environment.js");
stream.once('open', function(fd) {
stream.write('const env = "'+environment+'"\n');
stream.write('export default env');
stream.end();
});
module.exports = {
context: src,
entry: [
"./index.js"
],
output: {
path: dist,
filename: "manage2.bundle.js",
publicPath: '/static/',
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: ["babel-loader"]
},
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallbackLoader: "style-loader",
loader: ["css-loader", "sass-loader"],
publicPath: dist
})
}
]
},
devServer: {
hot: false,
quiet: true,
publicPath: "",
contentBase: path.join(__dirname, "dist"),
compress: true,
stats: "errors-only",
open: true
},
plugins: [
new HtmlWebpackPlugin({
template: "index.html"
}),
new ExtractTextPlugin({
filename: "manage2.css",
disable: false,
allChunks: true
}),
new CopyWebpackPlugin([{ from: "static", to: "static" }])
]
};
// new webpack.DefinePlugin({ env: JSON.stringify(environment) })
My npm scripts
"scripts": {
"dev": "NODE_ENV=development webpack-dev-server --history-api-fallback",
"prod": "NODE_ENV=production webpack-dev-server -p",
"build": "NODE_ENV=production webpack -p",
"build-dev": "NODE_ENV=production webpack -d",
This config is saving the *.js and *.css to the static folder.
output: {
// the output bundle
filename: '[name].[hash].js',
// saves the files into the dist/static folder
path: path.resolve(__dirname, 'dist/static'),
// set static as src="static/main.js as relative path
publicPath: 'static/'
},
With the HtmlWebpackPlugin you can generate a html file from a template. With this config in the Webpack plugins section the index.html is saved to the dist. folder with the correct path to the *.js and *.css.
plugins: [
// is only working with npm run build
new HtmlWebpackPlugin({
title: '',
// save index.html one director back from the output path
filename: '../index.html',
template: 'index.template.ejs',
hash: false
}),
],
index.template.ejs
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<title>
<%= htmlWebpackPlugin.options.title %>
</title>
</head>
<body>
</body>
</html>
Results in
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<title>
</title>
<link href="static/main.css" rel="stylesheet">
</head>
<body>
<script type="text/javascript" src="static/main.js"></script>
</body>
</html>
Your output.path is incorrect. It should be path.resolve(__dirname, 'dist', 'static').
And since now your output.path points to dist/static, set publicPath back to /.

Categories