I would like to create a React app that will not be a standalone site, but rather an extension to an existing site. The site is built using asp.net and I cannot make edits to it. I do have an entry with javascript and can run code such as the following:
$(document).onload(() => {
$(body).parent().append(`
<script src='{...}'></script>
`);
});
Is there any way I can change the standard Index.html output from the default create-react-app to a .js file and have the react chunks added into the append? It does not necessarily need to follow the above direction, but it's as far as I got.
I believe you could just attach it to the body using ReactDOM.render
ReactDOM.render(
<React>
<App />
</React>,
document.getElementByTagName('body')
)
But you might have to switch away from create-react-app to using webpack, and then use something like HTMLWebpackPlugin within your webpack.config.js to specify the entry point
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
target: 'web',
mode: 'development',
entry: './src/index.jsx',
devtool: "source-map",
output: {
path: path.resolve(__dirname, 'dist'),
publicPath: '/',
filename: "[name]/[name].js",
},
...
new HtmlWebpackPlugin({
template: './html/index.html',
filename: './index.html'
}),
...
}
You would have to install html-webpack-plugin as well as webpack and anything you need with npm install html-webpackplugin webpack ...
If you look up tutorials on this, try to find React 17 and Webpack 5 they're fairly new. Webpack 5 has some differences with hot reloading compared to Webpack 4
Related
I am trying to include react in my HTML page. At first I tried like this:
<script src="js/custom/custom.js"></script>
But that syntax gives me the following error:
Uncaught SyntaxError: Cannot use import statement outside a module
So I added the module attribute:
<script type="module" src="js/custom/custom.js"></script>
But with that, webpack isn't able to resolve the module path correctly, giving me this error:
Uncaught TypeError: Failed to resolve module specifier "react". Relative references must start with either "/", "./", or "../".
I have set up webpack with a custom entry path like so:
const webpack = require('webpack');
const path = require('path');
module.exports = {
entry: './js/custom/custom.js',
output: {
path: path.join(__dirname, '/build'),
publicPath: '/',
filename: 'bundle.js'
},
devServer: {
contentBase: path.join(__dirname, '/'),
port: 9000
},
html-webpack-plugin is thing you need
check this out
The plugin will generate an HTML5 file for you that includes all your webpack bundles in the body using script tags. Just add the plugin to your webpack configuration as follows:
var HtmlWebpackPlugin = require('html-webpack-plugin');
var path = require('path');
...
plugins: [new HtmlWebpackPlugin()]
..
Check how you can add custom template
The problem is that that script element is pointing to the raw, uncompiled source script, instead of the output script which webpack generates. It needs to point to webpack's output script so that it will be run through babel, compiled, compressed, etc.
The correct path to webpack's output script is the dev server address, plus the filename specified in webpack.config.js output filename. Thus, if config looks like:
module.exports = {
output: {
filename: 'badoongy-face.js'
},
Then the script tag should be:
<script src="https://localhost:8000/badoongy-face.js"></script>
or the same thing as a relative path:
<script src="badoongy-face.js"></script>
I'm working on a web app that has a companion WordPress site. Let's assume domains like:
app.site.com
blog.site.com
What I'd like to do is have my webpack 4 configuration output the bundle.js but also generate something like header.js that includes a single <Header /> component (not the complete app).
Then I'd add a script tag to blog.site.com like so:
<script src="https://app.site.com/build/header.js" />`
and expect it to mount the app's <Header /> in the blog's #dom-element
How can this be achieved?
Create the another entry point for header component. Quoting from webpack docs:
module.exports = {
entry: {
bundle: './src/app.js',
header: './src/header.js'
},
output: {
filename: '[name].js',
path: __dirname + '/build'
}
};
This will create bundle.js from your inital entry point. And also header.js from the Header component.
I have this website made using React and of course I used React Router for the routing and it works when I run on localhost. Now when I created my production build and uploaded online, clicking the link to en/signup for example didn't work. It showed an error page not found. So I was kind of wondering what actually went wrong here. I don't think its any problem with the bundling. Btw, I'm using Redux, React-Redux also in my project.
TLDR: Navigating to a url like en/signup doesn't work in production build.
Hope you guys can help me with this. Thanks.
What does your webpack config look like?
Try adding this:
var path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './app/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index_bundle.js',
publicPath: '/' // <- this right here
},
module: {
rules: [
{ test: /\.(js)$/, use: 'babel-loader' },
{ test: /\.css$/, use: [ 'style-loader', 'css-loader' ]}
]
},
devServer: { // <-- and this object too
historyApiFallback: true,
},
plugins: [
new HtmlWebpackPlugin({
template: 'app/index.html'
})
]
};
What I believe is happening currently is that if you navigate directly to a url, since React (and React Router) have not yet loaded, they can't take care of the url routing. So the browser navigates to static resources at en/signup, and of course nothing is there.
So by adding the publicPath value above, you're telling it to start by sending requests to root, and then when everything is loaded, the requested url is passed through.
I'm not sure if that's exactly what's going on behind the scenes, but I think the effect is correct.
After getting stuck on react-router for a couple of days, I read the documentation in React Training. Then I followed the quick start steps and modified it to satisfy my requirements.
import {
HashRouter as Router,
Route,
} from 'react-router-dom';
<Router>
<div>
<Header />
<Route exact path="/" component={Content} />
<Route path="/:country" component={SnaContent} />
<Footer />
</div>
</Router>
This have fixed the issue for now. Hope its helpful for others who faced similar issue.
I have a directory structure like this:
and inside node_modules:
>node_modules
>./bin
>webpack.config.js
>bootstrap
>bootstrap.css
>bootstrap.js
I need to generate separate CSS and JS bundles like this:
custom-styles.css, custom-js.js, style-libs.css, js-libs.js
where style-libs and js-libs should contain syles and js files of all libraries like bootstrap and jQuery. Here's what I have done so far:
webpack.config.js:
const path = require('path');
const basedir = path.join(__dirname, '../../client');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const stylesPath = path.join(__dirname, '../bootstrap/dist/css');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
watch: true,
// Script to bundle using webpack
entry: path.join(basedir, 'src', 'Client.js'),
// Output directory and bundled file
output: {
path: path.join(basedir, 'dist'),
filename: 'app.js'
},
// Configure module loaders (for JS ES6, JSX, etc.)
module: {
// Babel loader for JS(X) files, presets configured in .babelrc
loaders: [
{
test: /\.jsx?$/,
loader: 'babel',
babelrc: false,
query: {
presets: ["es2015", "stage-0", "react"],
cacheDirectory: true // TODO: only on development
}
},
{
test: /\.css$/,
loader: ExtractTextPlugin.extract("style-loader", "css-loader")
},
]
},
// Set plugins (for index.html, optimizations, etc.)
plugins: [
// Generate index.html
new HtmlWebpackPlugin({
template: path.join(basedir, 'src', 'index.html'),
filename: 'index.html'
}),
new ExtractTextPlugin(stylesPath + "/bootstrap.css", {
allChunks: true,
})
]
};
Client.js
import * as p from 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App.jsx';
ReactDOM.render(<App />, document.getElementById('app'));
I am able to run the app and render all of the components correctly except loading the external JS and CSS file using webpack.
I'm not much experienced with webpack and find it really difficult it wrap my hear around it. There're are a few simple questions:
1- Is this configuration correct? If yes, then how can I include my CSS and JS files in components using ES6. Something like import keyword.
2- Should I even be using webpack for CSS files?
3- How to specify individual directories for input and their respective output files in webpack? Something like all-custom.js should be output for custom1.js and custom2.js?
I know these are some very basic question and I tried Google but didn't find a single tutorial for Webpack that is simple and targets beginners.
After playing out with Webpack in multiple projects, I figured out how Webpack loads stuff. Since the question is still unanswered, I decided to do it myself for anybody with same need.
Directory structure
->assets
->css
->my-style-1.css //custom styling file 1
->my-style-2.css //custom styling file 2
->src
->app
->app.js
->variables.js
->libs.js //require all js libraries here
->styles-custom.js //require all custom css files here
->styles-libs.js //require all style libraries here
->node_modules
->index.html
->package.json
->webpack.config.js
Bundle 1 (main code of app)
app.js: assuming this is main file and app starts from here
var msgs = require('./variables');
//similarly import other js files you need in this bundle
//your application code here...
document.getElementById('heading').innerText = msgs.foo;
document.getElementById('sub-heading').innerText = msgs.bar;
Bundle 2 (js modules)
libs.js: this file will require all modules needed
require('bootstrap');
//similarly import other js libraries you need in this bundle
Bundle 3 (external css files)
styles-libs.js: this file will require all external css files
require('bootstrap/dist/css/bootstrap.css');
//similarly import other css libraries you need in this bundle
Bundle 4 (custom css files)
styles-custom.js: this file will require all custom css files
require('../assets/css/my-style-1.css');
require('../assets/css/my-style-2.css');
//similarly import other css files you need in this bundle
webpack.config.js
const path = require('path');
const webpack = require('webpack');
const extractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: {
'app': './src/app/app.js', //specifying bundle with custom js files
'libs': './src/libs.js', //specifying bundle with js libraries
'styles-custom': './src/styles-custom.js', //specifying bundle with custom css files
'styles-libs': './src/styles-libs.js', //specifying bundle with css libraries
},
module: {
loaders: [
//used for loading css files
{
test: /\.css$/,
loader: extractTextPlugin.extract({ fallbackLoader: 'style-loader', loader: 'css-loader?sourceMap' })
},
//used for loading fonts and images
{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
loader: 'file-loader?name=assets/[name].[hash].[ext]'
}
]
},
output: {
path: path.resolve(__dirname, 'dist'), //directory for output files
filename: '[name].js' //using [name] will create a bundle with same file name as source
},
plugins: [
new extractTextPlugin('[name].css'), //is used for generating css file bundles
//use this for adding jquery
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jQuery'
})
]
}
index.html
<head>
<link rel="stylesheet" href="dist/styles-libs.css" />
<link rel="stylesheet" href="dist/styles-custom.css" />
</head>
<body>
<h2 id="heading"></h2>
<h3>
<label id="sub-heading" class="label label-info"></label>
</h3>
<script src="dist/libs.js"></script>
<script src="dist/app.js"></script>
</body>
You can include css & JS files using import in es6 in the source files in your project. example:
import './style.css';
import Style from './path/style.js';
NB. Generally You need to code in es5 in webpack.config.js file. If you want to use es6 just follow the link How can I use ES6 in webpack.config.js?
You can use https://github.com/webpack/css-loader for CSS configuration.
You can use code splitting in webpack and specify multiple entry point but that will generate multiple output files. Have a look at multiple entry point section of that following link.
https://webpack.github.io/docs/code-splitting.html
After too many unsuccessful trials my question is: What is the proper way to setup Webpack so that:
Use react.min.js + react-dom.min.js - not the npm installed sources
Don't parse/com them again, just bundle with my own components.
"React" and "ReactDOM" variables can be used from all .jsx files.
The tutorials and guides I found didn't work - or maybe I did some errors. Usually I got error in browser developer tools about missing variable React.
My aim is just to save parsing/bundling time. Now I parse React from scratch every time I bundle my app. And it takes tens of seconds on a slowish computer. In watch mode it is faster, but I find I'm doing unnecessary work.
Any ideas with recent React versions?
Assuming you have a webpack.config.js that looks something like this:
module.exports = {
entry: "./entry.js",
output: {
path: __dirname,
filename: "bundle.js"
},
module: {
loaders: [
...
]
}
};
You just need to specify React and ReactDOM as external dependencies (from the docs):
module.exports = {
entry: "./entry.js",
output: {
path: __dirname,
filename: "bundle.js"
},
module: {
loaders: [
...
]
},
externals: {
// "node/npm module name": "name of exported library variable"
"react": "React",
"react-dom": "ReactDOM"
}
};
The key point about the externals section is that the key is the name of the module you want to reference, and the value is the name of the variable that the library exposes when used in a <script> tag.
In this example, using the following two script tags:
<script src="https://fb.me/react-0.14.6.js"></script>
<script src="https://fb.me/react-dom-0.14.6.js"></script>
results in two top-level variables being created: React and ReactDOM.
With the above externals configuration, anytime in your source code you have a require('react'), it will return the value of the global variable React instead of bundling react with your output.
However, in order to do this the page that includes your bundle must include the referenced libraries (in this case react and react-dom) before including your bundle.
Hope that helps!
*edit*
Okay I see what you're trying to do. The webpack configuration option you want is module.noParse.
This disables parsing by webpack. Therefore you cannot use dependencies. This may be useful for prepackaged libraries.
For example:
{
module: {
noParse: [
/XModule[\\\/]file\.js$/,
path.join(__dirname, "web_modules", "XModule2")
]
}
}
So you'd have your react.min.js, react-dom.min.js, and jquery.min.js files in some folder (say ./prebuilt), and then you'd require them like any other local module:
var react = require('./prebuilt/react.min');
And the entry in webpack.config.js would look something like this (untested):
{
module: {
noParse: [
/prebuilt[\\\/].*\.js$/
]
}
}
The [\\\/] mess is for matching paths on both Windows and OSX/Linux.