I use react-loadable for dynamically loading JS modules. I also use server-side rendering, which is set up and works for react-loadable. On the client side, however, there seems to be a problem, because when I load the page a warning appears in the console:
Warning: Text content did not match. Server: "Choose a name and enter passwordNamePasswordLogin" Client: "Loading..."
I use preloadReady on the client side, which should prevent this error.
My index.jsx looks like this:
import { preloadReady } from 'react-loadable';
window.addEventListener('load', async () => {
await preloadReady();
hydrate(
<App />,
document.getElementById('root')
);
});
Package versions:
Node: v8.12.0
webpack: 4.19.1
webpack-cli: 3.1.1
#babel/core: 7.1.0
babel-loader: 8.0.2
react-loadable: 5.5.0
Edit:
So I realized, that the server-side part of react-loadable doesn't actually work, because it can't get the list of bundles to include. The cause of this is that Loadable.Capture doesn't fire the report callback, and so the modules array will be empty.
It turned out that I made a very basic mistake, which was that I tried to use getBundles on the server side after the react app was defined, but before it was rendered. So obviously the report callback wasn't called as it is called at render time. After I changed the code to get the bundles after rendering, it works just fine (although I still have the problem of bundle duplicates, but I can easily filter that).
If András's solution doesn't resolve the issue. Then in your served .html file, ensure that chunk's script is available before vendor and main bundle. So, reorder chunk JS before vendor and main JS.
<script src="chunk.js" defer></script>
<script src="vendor.js" defer></script>
<script src="main.js" defer></script>
Related
I have imported react dev to a webpage (legacy HTML+JQuery) as follows:
<script src="https://unpkg.com/react#17/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#17/umd/react-dom.development.js" crossorigin></script>
<script type="module" src="dist/Menu.js"></script>
and I'm trying to use the Menu component, which has the following code:
import menuData from "./menuData";
import MenuItem from "./MenuItem";
export const Menu = () => {
return (
<p>Some code..</p>
)
};
let domContainer = document.querySelector('#main_menu');
ReactDOM.render(<Menu />, domContainer);
In my legacy app, before importing I had to transpile the JSX to JS using:
npx babel --watch src --out-dir dist --presets react-app/prod,
And in my legacy app, in addition to the imports (above), I wrote the following element:
<div id="main_menu"></div>
but my legacy app shows an 404 Error for my imports:
404 https://mywebpage.com/dist/menuData
404 https://mywebpage.com/dist/MenuItem
Any idea what I'm missing? How I can start using React components (with imports of another components) in my legacy HTML+JQuery app?
Do the imported paths really exist on your site? Open a browser and go to
https://mywebpage.com/dist/menuData
If they don't resolve to plain .js source code - which it sounds like they don't - they won't work. You probably need to add the file extension, since it sounds like your server isn't doing it:
import menuData from "./menuData.js";
import MenuItem from "./MenuItem.js";
such that going to
https://mywebpage.com/dist/menuData.js
shows you the JS file.
But an even better option you might also consider would be to bundle the code together during a build process, so that only one .js file has to be served to the client. As an app grows larger and larger, if the client has to request 20 or 30 or more modules from your server, that can be a significant performance hit if you aren't using HTTP/2. Consider a module bundler like Webpack. You may be able to turn your HTML into
<script src="dist/bundle.js"></script>
by bundling all of your modules (and React, and any other libraries you happen to be using here) together into a single file.
Background
I'm trying to create a "buildless" JavaScript app, one where I don't need a watch task running to transpile JSX, re-bundle code, etc every time I save any source file.
It works fine with just first-party code, but I'm stuck when I try to import dependencies from npm.
Goal
I want to achieve this kind of workflow:
npm install foo (assume it's an ES module, not CommonJS)
Edit source/index.js and add import { bar } from 'foo'
npm run build. Something (webpack, rollup, a custom script, whatever) runs, and bundles foo and its dependencies into ./build/vendor.js (without anything from source/).
Edit index.html to add <script src="build/vendor.js" type="module"...
I can reload source/index.js in my browser, and bar will be available. I won't have to run npm run build until the next time I add/remove a dependency.
I've gotten webpack to split dependencies into a separate file, but to import from that file in a buildless context, I'd have to import { bar } from './build/vendor.js. At that point webpack will no longer bundle bar, since it's not a relative import.
I've also tried Snowpack, which is closer to what I want conceptually, but I still couldn't configure it to achieve the above workflow.
I could just write a simple script to copy files from node_modules to build/, but I'd like to use a bundled in order to get tree shaking, etc. It's hard to find something that supports this workflow, though.
I figured out how to do this, using Import Maps and Snowpack.
High-Level Explanation
I used Import Maps to translate bare module specifiers like import { v4 } from 'uuid' into a URL. They're currently just a drafted standard, but are supported in Chrome behind an experimental flag, and have a shim.
With that, you can use bare import statements in your code, so that a bundler understands them and can work correctly, do tree-shaking, etc. When the browser parses the import, though, it'll see it as import { v4 } from 'http://example.org/vendor/uuid.js', and download it like a normal ES module.
Once those are setup, you can use any bundler to install the packages, but it needs to be configured to build individual bundles, instead of combining all packages into one. Snowpack does a really good job at this, because it's designed for an unbundled development workflow. It uses esbuild under the hood, which is 10x faster than Webpack, because it avoids unnecessarily re-building packages that haven't changed. It still does tree-shaking, etc.
Implementation - Minimal Example
index.html
<!doctype html>
<!-- either use "defer" or load this polyfill after the scripts below-->
<script defer src="es-module-shims.js"></script>
<script type="importmap-shim">
{
"imports": {
"uuid": "https://example.org/build/uuid.js"
}
}
</script>
<script type="module-shim">
import { v4 } from "uuid";
console.log(v4);
</script>
snowpack.config.js
module.exports = {
packageOptions: {
source: 'remote',
},
};
packageOptions.source = remote tells Snowpack to handle dependencies itself, rather than expecting npm to do it.
Run npx snowpack add {module slug - e.g., 'uuid'} to register a dependency in the snowpack.deps.json file, and install it in the build folder.
package.json
"scripts": {
"build": "snowpack build"
}
Call this script whenever you add/remove/update dependencies. There's no need for a watch script.
Implementation - Full Example
Check out iandunn/no-build-tools-no-problems/f1bb3052. Here's direct links to the the relevant lines:
snowpack.config.js
snowpack.deps.json
package.json
core.php outputs the shim
plugin.php - outputs the import map
passphrase-generator.js - imports the modules. (They're commented out in this example, for reasons outside the scope of this answer, just uncomment them, run the bundle script, and they'll work).
If you are willing to use an online service, the Skypack CDN seems to work nicely for this. For instance I wanted to use the sample-player NPM module and I've chosen to use a bundle-less workflow for my project using only ES6 modules as I'm targeting embedded Chromium latest version so don't need to worry about legacy browser support, so all I needed to do was:
import SamplePlayer from "https://cdn.skypack.dev/sample-player#^0.5.5";
// init() once the page has finished loading.
window.onload = init;
function init() {
console.log('hello sampler', SamplePlayer)
}
and in my html:
<script src="./src/sampler/sampler.js" type="module"></script>
And of course you could just look inside the JS file the CDN generates at the above url and download the generated all-in-one js file it points to, in order to use it offline as well if needed.
Alright, I downloaded this off Github trying to run it locally/modify it. https://tympanus.net/Tutorials/InteractiveRepulsionEffect/interactive-repulsive-effect.zip
The main index.html calls the JS in with: <script type="text/javascript" src="app.0ffbaba978f8a0c5e2d0.js"></script> which seems to be a minified version of app.js which I want to modify.
File structure looks like:
I changed the html to: <script type="text/javascript" src="../src/scripts/app.js"></script> which is the correct filepath to the JS that makes the scene, but I then get
Uncaught SyntaxError: Unexpected string
on line 1 of app.js which is:
import 'styles/index.scss';
import Cone from './elements/cone';
import Box from './elements/box';
import Tourus from './elements/tourus';
I tried changing this path, it doesn't matter. It just doesn't "like" the line. What is going on here? How can I reference the editable JS file?
You can't. The JavaScript code you've got isn't ready to be run in a browser.
Those public/app.xxxxxxxxxx.js files are what's ready to run in a browser, and they're likely compiled by Webpack (or something similar). Your repository has some sort of "build" process in place - chances are you can look at the scripts section of package.json to see the available build commands.
Exactly, you have to place yourself in the first-demo folder and modify your app js. Then run
npm install
to install webpack and any missing packages (just once). Then you can run
npm run build
and it will rebuild your public folder with your changes. Better yet, you can just
npm run start
and you will see a hot reload of your changes when you modify app.js in
http://localhost:9000
Alright, I have looked on this site and have found several different answers, none of which have worked for me.
Basically had a js file that had many functions in it along with the main code for the app. I wanted to move all my functions to another js file so that I could clean up my code a little. I am fairly new to js but I know in python it was as simple as saying "import (module) as (nickname) from (path)"
anyways let's say I have a function named show message in my functions.js module.
export function show_message(){
alert("Hello");
}
and then I at the top of my main.js file I did
import { show_message } from './functions.js'
//I have also tried to import like this:
import * as func from './functions.js'
//And then I call it
show_message();
//I have also tried
func.show_message();
I know this is something simple, but as I said everywhere I have looked I have seen different answers, none of which work for me. I am using Firefox btw. I am also getting an error in the console saying that my import declarations need to be at the top of my module, I fixed that by specifying the type in my HTML link (script src="/static/main.js" type="module")
The error went away but is now saying "same origin policy disallows reading the remote resource at the file (path) (reason: cors request not HTTP)."
And the other error says "module source URI is not allowed in this document".
which makes me think maybe my syntax for importing is right and the error is in my HTML code?
Any help is appreciated.
0. The short answer
You need to install and run a local web server. - For a suggestion on how,
read on.
1. The basics
I tried a simple HTML file – index.html – as follows:
<!-- index.html - minimal HTML to keep it simple -->
<html>
<head>
<meta charset="UTF-8">
<link rel="shortcut icon" href="#">
</head>
<body>
<h1>Hello world!</h1>
<p>Experimenting with JavaScript modules.</p>
<script type="module" src="js/functions.js"></script>
</body>
</html>
In the subfolder js I put the JavaScript file functions.js:
// js/functions.js
alert('Hello');
When double-clicking index.html, my default web browser – Firefox 89.0
(64-bit) – shows the following, after pressing F12.
Notice how the JavaScript code is not running:
The error message:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at file:///C:/stackexchange/reproduce/jsModule/moduleNW/basics/js/functions.js. (Reason: CORS request not http).
A cheating "solution" is to (temporarily) remove type="module" from the HTML
code.
The alert then displays without errors.
But I want to run the JavaScript code as a module, so I put back
type="module" in the HTML.
2. Install and run a local web server
To run it as a module, it needs to run on a web server.
Thus, if you want to run the code on your own computer, you will need to
(install and) start a local web server.
One currently popular alternative is live-server.
Here is what worked for me.
Open a terminal. (On Windows: cmd.exe.)
Type npm and hit Enter to see if Node.js is installed.
If you get command not found, download at https://nodejs.org/en/download/
and install. 1
(On Ubuntu, you can try sudo apt install -y nodejs.)
Install live-server: npm install live-server -g.
Change directory to where your page lives: cd <path-to-index.html>.
Start the server: live-server .
(Should open localhost:8080 in your default browser and show the alert.
See below.)
Note 1.
I am on Windows 10, but the above instructions should work fine on Linux and
macOS too.
Note 2.
Here I used Firefox 89.0, but I have tried Google Chrome 91.0 as well.
The only notable difference is the CORS error message, which in Chrome reads:
Access to script at 'file:///C:/stackexchange/reproduce/jsModule/basics/js/functions.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, chrome-untrusted, https.
3. Exporting and importing
Next I create a new folder demo2 containing the following demo2.html:
<!-- demo2.html - even shorter HTML for simplicity -->
<body>
<h1>Hello world!</h1>
<p>Javascript modules.</p>
<script type="module" src="js/main.js"></script>
</body>
I also create the following three JavaScript files in the subfolder js:
// js/module1.js
export function hi () { console.log('Hi from module 1.'); }
and
// js/module2.js
export function howdy () { console.log('Howdy from module 2!'); }
and
// js/main.js
import { hi } from './module1.js';
import { howdy } from './module2.js';
hi();
howdy();
Now I run live-server from the terminal in the folder where demo2.html
resides.
This time I start by typing
live-server --port=1234 --entry-file=demo2.html
and hitting Enter. Screenshot:
References:
Installing Node.js live-server
The live-server docs
Live-server can't find the file specified
Export and Import
1 On Windows 10, I once needed to
repair the installation.
On the script tag you are using to load the js in the browser you need to add the attribute
type="module"
It will look like the following:
<script type="module">
import {addTextToBody} from './utils.mjs';
addTextToBody('Modules are pretty cool.');
</script>
utils.mjs:
export function addTextToBody(text) {
const div = document.createElement('div');
div.textContent = text;
document.body.appendChild(div);
}
This is if you are not using a bundler like webpack and working directly in the browser.
Source of code: https://jakearchibald.com/2017/es-modules-in-browsers/
You might want to use broswerify instead. It allows you to write NodeJS-style modules and then compiles them into a single browser-friendly JavaScript file, allowing you to get all the performance benefits of loading only a single file. It also means you can easily use the same code both server side and client side.
If you want to stick with separate files, it looks like you are well on your way. Unlike regular JavaScript files, modules are subject to Cross-Origin Resource Sharing (CORS) restrictions. They have to be loaded from the same origin, and cannot be loaded from the local filesystem. If you are loading them from the local file system, move them to a server. If you are already hosting them on a server, add the Access-Control-Allow-Origin: * header to the response that serves the module file.
Lots more gotchas and solutions here and here.
function show_message(){
alert("Hello");
}
export { show_message };
and
import { show_message } from './functions'
i think this should do the trick. this is a named export/import technique. you can under this name find more information if you desire it.
Shortcut for Accepted answer
In case you are using Visual Studio Code just install the Live Preview extension by Microsoft.
In any HTML file click the Show preview icon. It will automatically run a local server and show up in the code editor. After every edit you make it refreshes. You can also show it in your default browser.
No need for command line anymore!
JavaScript has had modules for a long time. However, they were implemented via libraries, not built into the language i.e. you can't import or export part of those modules into your js files (whole library needs to be loaded). ES6 is the first time that JavaScript has built-in modules.
Please refer Here for more info about ES modules.
But things have changed and ES modules are now available in browsers! They're in…
Safari 10.1+, Chrome 61+, Firefox 60+, Edge 16+, etc,.
Now, you need to create your JS file using a new extension .mjs, like,
// utils.mjs
export function addTextToBody(text) {
const div = document.createElement('div');
div.textContent = text;
document.body.appendChild(div);
}
and then, you can import that file into your html page like,
<script type="module">
import {addTextToBody} from './utils.mjs';
addTextToBody('Modules are pretty cool.');
</script>
Please refer Here for more info about using ES module in browsers.
Consider going through this url some extension might be causing an issue with the loading of modules:
This blog might be an answer to what you're expecting.
You should first check if browser accepts type="module" and use fallback if it doesn't like this:
<script type="module" src="module.mjs"></script>
<script nomodule src="fallback.js"></script>
This might be the main reason for the CORS error as written here:
Unlike regular scripts, module scripts (and their imports) are fetched
with CORS. This means cross-origin module scripts must return valid
CORS headers such as Access-Control-Allow-Origin: *
So you need to add CORS header to the module file
Consider this blog for CORS issue. You should add CORS header ie. Access-Control-Allow-Origin: * to the server config most probably.
Using JS modules in the browser
On the web, you can tell browsers to treat a element as a module by setting the type attribute to module.
<script type="module" src="main.mjs"></script>
<script nomodule src="fallback.js"></script>
More on
https://developers.google.com/web/fundamentals/primers/modules
If you're using webpack and babel and want to import the code into your bundle, I guess it should be one of the following:
export default function show_message(){
alert("Hello");
}
and then in your code:
import show_message from 'path/to/show_message.js'
// or
import { default as someOtherName } from 'path/to/show_message.js'
Or if you'd like to export several functions:
const show_message = function(){
alert("Hello");
}
export { show_message };
and then in your code:
import { show_message } from 'path/to/show_message.js'
// or
import { show_message as someOtherName } from 'path/to/show_message.js'
Hope that helps.
I know this old thread but I just fixed this problem myself by using Parcel to launch my website Parcel index.html, in my situation I was using Live server and it didn't work until I switched to parcel .
Instead of using .js, try using .mjs.
Let's say your module file is /modules/App.js, just change it to /modules/App.mjs.
And ofcourse, make sure you have added type="module" in script tag, like this - <script type="module" src="./index.js" defer></script>
My folder structure -
index.html
index.js
modules/App.mjs
This worked for me!
I'm not using Node, how do move my react code (jsx) which is currently in a text/babel script tag into separate component files? I have tried moving them and then referencing them in the html file but as soon as it hits the first bit of HTML it throws a syntax error, I have tried both .jsx and .js extensions of the file and when I include it I give it the script type of text/babel.
<script src="components/nav.jsx" type="text/babel" ></script>
<script src="components/map.jsx" type="text/babel" ></script>
<script src="components/app.jsx" type="text/babel" ></script>
If you are transforming your JSX in the browser, you would have to attach each component to the window object.
As an example:
var Nav = React.createClass({ ... });
window.Nav = Nav;
Or more tersely:
window.Nav = React.createClass({ ... });
Jim Nielsen is right, however it is considered to be a bad practice to expose parts of you code to a global scope and to transpile JSX in the browser. Instead you should consider to use some building system like Webpack.
This way you would be able to use es2015 import syntax to import components from one file to another, bundle everything in one file and much more additional benefits like code minification, sourcemaps, livereload etc.
Setting up React for ES6 with Webpack and Babel
Using React with Webpack Tutorial
Related issue