Skip to content

Commit 49c1a3a

Browse files
authored
feat: add React MF SharedWorker example (#4307)
1 parent 4b2b004 commit 49c1a3a

File tree

14 files changed

+197
-1
lines changed

14 files changed

+197
-1
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,8 @@
175175
"vue3-demo",
176176
"vue3-demo/*",
177177
"vue3-demo-federation-with-vite",
178-
"vue3-demo-federation-with-vite/*"
178+
"vue3-demo-federation-with-vite/*",
179+
"react-sharedworker"
179180
]
180181
},
181182
"devDependencies": {

react-sharedworker/README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# react-sharedworker
2+
Using Webpack Module Federation in SharedWorker
3+
4+
Federated module (defined under `module` folder), can be consumed by main thread in host application (defined under `host` folder), and it also can be consumed by worker thread.
5+
6+
## How to run
7+
8+
```
9+
yarn
10+
yarn start:module
11+
yarn start:host
12+
```
13+
14+
[Open browser](http://localhost:3000) and observe error in console.
15+
16+
![shared worker](./image.png)
17+
18+
## How to work
19+
20+
1. Use SharedWorker instead of Worker in host application
21+
2. Dynamically import bootstrap file in worker thread
22+
3. Use promise based dynamic remotes `host/webpack.host-config.js`
23+
4. Use multiple entry points in webpack config `module/webpack.module-config.js`

react-sharedworker/host/index.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<body>
4+
open console to see the error
5+
</body>
6+
</html>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { testValue } from 'myModule/testValue';
2+
3+
console.log('testValue in main thread', testValue);
4+
5+
const worker = new SharedWorker(new URL('./worker', import.meta.url));
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { testValue } from 'myModule/testValue';
2+
3+
console.log('testValue in worker thread', testValue);

react-sharedworker/host/src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import('./bootstrap');

react-sharedworker/host/src/worker.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import('./bootstrapWorker');
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
const path = require('path');
2+
const HtmlWebpackPlugin = require('html-webpack-plugin');
3+
const { container } = require('webpack');
4+
5+
module.exports = {
6+
mode: 'development',
7+
entry: path.resolve(__dirname, 'src/index.js'),
8+
output: {
9+
path: path.resolve(__dirname, 'build'),
10+
filename: 'host-bundle-[name].js',
11+
chunkFilename: 'host-chunk-[name].js',
12+
},
13+
plugins: [
14+
new container.ModuleFederationPlugin({
15+
remotes: {
16+
myModule: `promise new Promise(resolve => {
17+
// This part depends on how you plan on hosting and versioning your federated modules
18+
if (globalThis.SharedWorkerGlobalScope) {
19+
const remoteUrlWithVersion = 'http://localhost:3001/worker/remoteEntry.js'
20+
importScripts(remoteUrlWithVersion)
21+
resolve(globalThis.myModule);
22+
return;
23+
}
24+
const remoteUrlWithVersion = 'http://localhost:3001/remoteEntry.js'
25+
const script = document.createElement('script')
26+
script.src = remoteUrlWithVersion
27+
script.onload = () => {
28+
// the injected script has loaded and is available on window
29+
// we can now resolve this Promise
30+
const proxy = {
31+
get: (request) => window.myModule.get(request),
32+
init: (...arg) => {
33+
try {
34+
return window.myModule.init(...arg)
35+
} catch(e) {
36+
console.log('remote container already initialized')
37+
}
38+
}
39+
}
40+
resolve(proxy)
41+
}
42+
// inject this script with the src set to the versioned remoteEntry.js
43+
document.head.appendChild(script);
44+
})
45+
`,
46+
},
47+
}),
48+
new HtmlWebpackPlugin({
49+
template: path.resolve(__dirname, 'index.html'),
50+
}),
51+
],
52+
devServer: {
53+
static: {
54+
directory: path.join(__dirname, 'build'),
55+
},
56+
compress: true,
57+
port: 3000,
58+
}
59+
};

react-sharedworker/image.png

421 KB
Loading

react-sharedworker/module/dev.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
const express = require("express");
2+
const webpack = require("webpack");
3+
const webpackDevMiddleware = require("webpack-dev-middleware");
4+
5+
const app = express();
6+
const [config1, config2] = require("./webpack.module-config.js");
7+
8+
const compiler1 = webpack(config1);
9+
const compiler2 = webpack(config2);
10+
11+
app.use(
12+
webpackDevMiddleware(compiler1, {
13+
publicPath: config1.output.publicPath + "worker/",
14+
})
15+
);
16+
17+
app.use(
18+
webpackDevMiddleware(compiler2, {
19+
publicPath: config2.output.publicPath,
20+
})
21+
);
22+
23+
app.listen(3001, function () {
24+
console.log("App listening on port 3001!\n");
25+
});

0 commit comments

Comments
 (0)