webpack@5ã§å ¥ãModule Federationã«ã¤ãã¦
ðââï¸ ããã°ã移管ãããã®ã§ï¼æ°ããæ¹ã¸ç§»åãã¾ãã
Module Federation(ä»¥ä¸ mfe)ã¯webpack@5ããå ¥ãæ°ããä»çµã¿ã®ä¸ã¤ã§ãã
- ðââï¸ ããã°ã移管ãããã®ã§ï¼æ°ããæ¹ã¸ç§»åãã¾ãã
- Proposal
- ç®ç
- ç¨èª
- 注æ
- è¨å®ã¨ã³ã¼ã
- ä¾åé¢ä¿å ±æ
- Q&A
- ãããã«
Proposal
ç®ç
ã¢ããªã±ã¼ã·ã§ã³ãä½ãæã«ãwebpackã¯ãã«ãæã®ã½ã¼ã¹ã³ã¼ãã¯ä½¿ãåæã§å®è¡ããã®ã§ãæ§ã
ãªæé©åãè¡ããã¨ãã§ãã¾ãã
ãããnode_modulesçµç±ä»¥å¤ã§ã©ã¤ãã©ãªã使ãã¨ããå ´åã¯scriptã¿ã°ãESMããåå¾ããã¨ããã®ãä¸è¬çã§ãã
ãããããã®å ´åã ã¨åé¡ç¹ãåºã¦ãã¾ããããã¯ã©ã¤ãã©ãªã®éè¤åé¡ã§ãã
åå¾ããã©ã¤ãã©ãªã¯ãã§ã«bundleæ¸ã¿ãªããããã®ä¸ã«ã¯åãã©ã¤ãã©ãªãåå¨ãããã¨ãå¤ãã§ãã
ã¦ã¼ã¶ã¼ã¯éè¤ããã©ã¤ãã©ãª(e.g. react, react-dom, etc.)ã®åå¾ãè¡ãå¯è½æ§ãé«ãããããã®æé©åãè¡ãã®ãModule Federationã§ãã
ä¸è¨ã®å³ã ã¨ãmain.jsã§ä½¿ã£ã¦ããç·ã®ã©ã¤ãã©ãªã¯lib-aã§ãlib-bã§ã使ã£ã¦ãã¦ãããã3ã¤ãã¦ã³ãã¼ãããã®ã¯ç¡é§ãªã®ã§main.jsããã¦ã³ãã¼ããã¦ããç·ã«lib-a/bãä¾åããã°ãããã¯ã¼ã¯ã®æé©åãã§ãã¾ãã
DLLPluginãexternals
ã§ãåæ§ã®ãã¨ã¯ã§ãã¾ãããããã¯è²§å¼±ãªã®ã§ä»å¾ã¯ãã¡ãã«ä¹ãæãããã¨ãå¯è½ãªã±ã¼ã¹ãããã¾ãã
ãã®è¨äºã§ã¯å é¨ã¯æ·±ã触ããªããããå é¨ã¢ã«ã´ãªãºã ãç¥ããã人ã¯ãã¡ã
ã³ã¼ã: containers
ãã®ä»çµã¿ã¯Micro Frontendsã®ããã ãã«ããããã§ã¯ããã¾ãããã ä¸çªæ©è½ããå¯è½æ§ãé«ãã¨ã¯æãã¾ãã
Micro Frontendsã¨ã¯ãªã«ãï¼
ç¨èª
- ãã¼ã«ã«(ã¢ã¸ã¥ã¼ã«) => 使ãå´
- ãã«ãã©ã¤ã³ã«å ¥ã£ã¦ããé常ã¢ã¸ã¥ã¼ã«
- ãªã¢ã¼ã(ã¢ã¸ã¥ã¼ã«) => 使ãããå´
- å®è¡æã«ã³ã³ããã¼ããå¼ã³åºãããã¢ã¸ã¥ã¼ã«
- ã³ã³ãã => ãã¼ã«ã«ããªã¢ã¼ãã«ããããããããã¼ã¸ã£ã¼
- ã¢ã¸ã¥ã¼ã«ã®å段ã«ãããã®ãå®éã«ã¯ãªã¢ã¼ãã®URLã§ã¯ãªããªã¢ã¼ãã®ã³ã³ããURLã£ã¦è¨ã£ãã»ããæ£ãã
- ããã§ã¢ã¸ã¥ã¼ã«ã®å ¬éããããã©ãããå¶å¾¡ãã
- ã³ã³ããéã§ã®å¾ªç°çãªä¾åãå¯è½ã§ããã®ã³ã³ãããä¸æ¸ãAPI(
__webpack_override__
)ãæä¾ãã- å å¼é¢ä¿ã§ã®ã¿ä¸æ¸ãã¯å¯è½ã§ãåä¸æ¹åã®æä½
- ã³ã³ããã¯ä»¥ä¸ã®å¦çãè¡ãã¾ã
- éåæãã£ã³ã¯ã®èªã¿è¾¼ã¿
- ãã®ãã£ã³ã¯ã®è©ä¾¡
注æ
ãªã¢ã¼ãã¢ã¸ã¥ã¼ã«ã¯ãéåæãã£ã³ã¯ã¨ãªããã£ã³ã¯ãã¼ãã®å¦çãå¿
è¦ãªããåºæ¬çã«ã¯ãimport()
ã使ããã¾ãããrequire()
ãrequire.ensure()
ã«ã使ç¨å¯è½ã§ãã
top-levelã§ã®importããã«ãã¯ã§ãã¾ãããåæãã£ã³ã¯ã¨ãªã£ã¦ãã¾ãããå®è¡æã«ã¯ã¨ã©ã¼ã¨ãªãã¾ãã
ã¤ã¾ãããã®ä»çµã¿ã¯bundleæã«ã¾ã ä¸æãªããã°ã©ã ã許容ãããããå®è¡æã«åãã¦ã¨ã©ã¼ããããã¾ãã
ããã¦ããã¼ã«ã«ããªã¢ã¼ãã«ãªããã¨ãå¯è½ãªãããåãã£ã³ã¯ã¯ç«ã¡ä½ç½®ããã®å ´ã®ç¶æ³ã«ãã£ã¦å¤åãã¾ãã
ãã¼ã«ã«ã¨æ±ºãã¦ããã£ã³ã¯ã«exposesãè¨å®ããã°ããããªã¢ã¼ããã£ã³ã¯ã¨ãªãã¨ãããã¨ã§ãã
ãã®å ´åã ã¨ãå·¦ã®ãã¼ãããã®ä¾ã§ã/sub
ã¸ã¢ã¯ã»ã¹ããå ´åã¯Localã¨ããæ±ãã«ãªãã¾ããã/
ã¸ã¢ã¯ã»ã¹ããã¨å·¦ã®ãã¼ãã¯ãªã¢ã¼ãã¨ããæ±ãã«ãªãã¾ãã(ä¸ã®ãã¼ãããè¦ã¦ãªã¢ã¼ã)
è¨å®ã¨ã³ã¼ã
å ã«ã³ã¼ããè¦ã¦ããã¾ãããããã®æ§æã§ã¯ããªã¢ã¼ãã®ãã¡ã¤ã«ããã¼ã«ã«ãåãã·ã³ãã«ãªä¾ã§ãã
ãªã¢ã¼ã
// webpack.config.js const { ModuleFederationPlugin } = require('webpack').container; module.exports = { output: { // ãã®ãªã¢ã¼ãã®ãã¡ã¤ã«ããã¼ã«ã«ã§å±éãããã¨ãã«publicPathãæ¸ããªãã¨è¦ªã®å®è¡æã«è¦ªã®URLãè¦ã¦ãã¾ãã®ã§å¿ é // ãã®PRã§å¤æ´ããã: https://github.com/webpack/webpack/pull/10703 publicPath: 'http://localhost:8081/', }, plugins: [ new ModuleFederationPlugin({ name: 'page1', // ã¨ã³ããªã¼ã®ååãexposesãããå ´åã¯å¿ é ãã¼ library: { type: 'var', // scriptã¿ã°ãçµç±ãããä»ã®ãªãã·ã§ã³ã¯ãã¡ã https://github.com/webpack/webpack/blob/dev-1/schemas/plugins/container/ModuleFederationPlugin.json#L155 name: 'page1' // importãããã¨ãã®åå(ãã®å ´åã¯ãimport('page1/xxxx')) }, filename: 'page1RemoteEntry.js', // åºåãããentryã®ãã¡ã¤ã«å exposes: { Page: './src/index.js', // ã³ã³ãã¼ãã³ãå(ãã®å ´åã¯ãimport('page1/Page')) }, }), ], };
// src/index.js import React from 'react'; const Page1 = () => <h1>This is Page1</h1>; export default Page1; // React.lazyã¯default exportsã®ã¿è¨±å®¹
Asset Size 579.js 7.27 KiB [emitted] 579.js.LICENSE.txt 295 bytes [emitted] main.js 7.68 KiB [emitted] [name: main] main.js.LICENSE.txt 295 bytes [compared for emit] page1RemoteEntry.js 2.09 KiB [emitted] [name: page1]
ãã¼ã«ã«
// webpack.config.js const { ModuleFederationPlugin } = require('webpack').container; const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { plugins: [ new HtmlWebpackPlugin({ template: './index.html', }), new ModuleFederationPlugin({ remotes: { page1: 'page1', // page1ããã¼ã«ã«å´ã§ä½¿ç¨ãããã¨ãä¼ãããimport('page1/xxx') }, }), ], };
// src/index.js import React, { lazy, Suspense } from 'react'; import { render } from 'react-dom'; // page1/page(remote)ãdynamic import const Page1 = lazy(() => import('page1/Page')); const Wrapper = () => ( <div> <Suspense fallback={<span>Loading...</span>}> <Page1 /> </Suspense> </div> ); render(<Wrapper />, document.getElementById('root'));
<!-- index.html --> <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> </head> <body> <div id="root"></div> <!-- import('page1/page'))ã§èªã¿è¾¼ãããã«remoteã®ãã¡ã¤ã«ãåå¾ --> <script src="http://localhost:8081/page1RemoteEntry.js"></script> </body> </html>
Asset Size index.html 194 bytes [compared for emit] main.js 128 KiB [emitted] [name: main] main.js.LICENSE.txt 790 bytes [compared for emit]
ãã®ããã«ãimport('<scope>/<request>')
ã¨ããå½¢ã§ãã¼ã«ã«å´ã¯åãè¾¼ã¿ã¾ãã
ä¾åé¢ä¿å ±æ
shared
ã¨ãããªãã·ã§ã³ãã¤ãããã¨ã«ãããä¾åé¢ä¿ã®å
±æãè¡ãããªã¢ã¼ãã¯ãã¼ã«ã«ã®ä¾åé¢ä¿ãåªå
çã«åç
§ãã¾ãã
ãããã¼ã«ã«ã«ä¾åé¢ä¿ããªãå ´åããªã¢ã¼ãã¯ç¬èªã«ãã¦ã³ãã¼ããè¡ãã¾ãã
ãã®ä»çµã¿ã«ããããã³ãã«ãè·¨ãã é¢ä¿ã§ãæå°éã«ãã¡ã¤ã«éãçãããã¨ãã§ãã¾ãã
ä¸è¨ã®ã³ã¼ãã§ã¯ããã®ãªãã·ã§ã³ãå ¥ããåã®Networkã¯ä»¥ä¸ã®ããã«ãªãã¾ãã
579.jsã¨ãããªã¢ã¼ãã®JSã«æ³¨ç®ãã¦ãã ããããã®ãã¡ã¤ã«ã¯page1RemoteEntry.jsããå¼ã³åºããã¾ãã
ãã®579.jsã«Reactã®ã©ã¤ãã©ãªã½ã¼ã¹ã³ã¼ããå
¥ã£ã¦ãã¾ãã(main.jsã®ä¸ã«ãåæ§ã®ã³ã¼ããå
¥ã£ã¦ãã)
ã§ã¯ããã¼ã«ã«ããªã¢ã¼ããReactã使ã£ã¦ããã®ã§ã両è ã®webpack.config.jsã«ä»¥ä¸ã追å ãã¾ãã
new ModuleFederationPlugin({ ..., shared: ['react'], ]
ããããã¨ã以ä¸ã®ããã«579.jsã®ãµã¤ãºã3.4kbãã506bã¾ã§ä¸ããã¾ããã
ããã¯ãReactã©ã¤ãã©ãªã®ã½ã¼ã¹ã³ã¼ãããªããªãã579.jsãmain.jsã«å«ã¾ãã¦ããReactã®ã©ã¤ãã©ãªã³ã¼ããåç
§ãã¦ããã¨ããç¶æ
ã§ãã
ãã®579.jsã«æ®ã£ã¦ããã³ã¼ãã¯const Page1 = () => <h1>This is Page1</h1>;
ã®ã¿ã¨ãªãã¾ãã
ã§ã¯ããªã¢ã¼ãå´ã«ã¯shared: ['react']
ãä»ãããã¼ã«ã«å´ããªããã¨ã©ããªãã§ãããï¼
æåã«èª¬æããã¨ããããã¼ã«ã«å´ãshared
ãè¨å®ãã¦ãªãã£ãå ´åã¯ããªã¢ã¼ãå´ãèªèº«ã®URLããReactã©ã¤ãã©ãªããã¦ã³ãã¼ãããããã«ãã©ã¼ã«ããã¯ãè¡ããã¾ãã
ãã®466.jsãReactã®ã©ã¤ãã©ãªã³ã¼ãã¨ãªãã¾ãã
ãããã¯ã³ã³ããã§ããpage1RemoteEntry.jsã管çãã¦ãã¦ããªã¢ã¼ãã«åä¸ã®ã©ã¤ãã©ãªããªãã®ã§ãã®ã³ã³ããã466.jsããã¦ã³ãã¼ãããå¦çãè¡ãã¾ãã
ã¾ã¨ããã¨ããªã¢ã¼ãã¯å
±æãããã§ãããã©ã¤ãã©ãªã¯shared
ã«å
¥ãã¦ãããæ¹ã管çã楽ã ã¨æãã¾ãã ããããã¨ä½¿ãå´ãããã許容ãããã©ããã管çã§ããããã§ãã
Q&A
Q: URLã¯HTMLã«æ¸ãå¿
è¦ããã®ï¼
A: html-webpack-pluginã®å¯¾å¿ãå¾
ã¤ãwebpackã§containerã®urlãå¼ãããããããhtmlã«ãããå®è£
ãæ¸ã
Q: å
±æã©ã¤ãã©ãªã®ãã¼ã¸ã§ã³ãç°ãªãå ´åã©ãããã°ããã®ï¼
A: æ¨æºã§ã¯ãªããã以ä¸ã®ãããªæ¸ãæ¹ã¯ã§ããã
shared: { "react@6": "react" }
Q: ããã·ã¥ä»ããã¡ã¤ã«åã®å ´åã©ãããã°ããã®ï¼
A: webpackéã§ã®ãªã¼ã±ã¹ãã¬ã¼ã·ã§ã³ãç¶æããããããã·ã¥ä»ããã¡ã¤ã«åã¯åºåããã¾ãã
Q: ãã¼ã«ã«ã¨ãªã¢ã¼ãã§å
±éã©ã¤ãã©ãªã管çããã®ã ãã
A: ä»å¾èªåçã«å
¥ããä»çµã¿ãå
¥ãã¨æãã3rd partyã§ã¯ãã§ã«åå¨ãããããããã æã§æ¸ããã»ããããæ°ã¯ãã
Q: IDEã§ã®è£å®ãå¹ãã¾ããã
A: ãããã¾ãããã©ããwebpack.config.jsãIDEãç解ã§ããããã«ãªããªãã¨è§£æ±ºããªã
Q: SSRã§ãåããï¼
A: è¨è¨ä¸ãwebã«éå®ãã¦ä½ã£ã¦ãªãã®ã§åããlibrary.type
ãvar
ããcommonjs-module
ã«å¤ãã¦ã¿ã¦ã
ãããã«
ã¦ã¼ã¶ã¼ã¯ãã®sharedããç¥ã£ã¦ããã°ããã¦containersã¨ãã¯åºæ¬çã«ã¯ç¥ããªãã¦ããã§ãã
ã¾ã å®å®çãªãã§ã¼ãºã§ã¯ãªãå®é¨çãªã®ã§ã¤ã³ã¿ã¼ãã§ã¤ã¹ã®å¤æ´ã«ã¯æ³¨æãã¦ãã ããã
ãªã«ãèããããã¨ããã°ãtwitterã¾ã§ã