ã¢ããªã±ãŒã·ã§ã³ãiframeã«ããŒãããããããã¬ã€ã¢ãŠãã«åé¡ãçºçãããã©ã°ã€ã³ãæ£ããæ©èœãããã¢ããªã±ãŒã·ã§ã³ãšãã¬ãŒã ãããŒãžã£ãŒã®Angularã®ããŒãžã§ã³ãåãã§ãã£ãŠããã¯ã©ã€ã¢ã³ãã¯Angularã§2ã€ã®ãã³ãã«ãããŠã³ããŒãããŸãããããŠã2020幎ã«iframeã䜿çšããããšã¯æªããããŒã®ããã§ãããããããã¬ãŒã ããããããŠããã¹ãŠã®ã¢ããªã±ãŒã·ã§ã³ã1ã€ã®ãŠã£ã³ããŠã«ããŒããããšã©ããªãã§ããããã
ãããå¯èœã§ããããšã倿ããã®ã§ã次ã«ãããå®è£ ããæ¹æ³ã説æããŸãã
å¯èœãªè§£æ±ºç
ã·ã³ã°ã«ã¹ãïŒãããã³ããšã³ããã€ã¯ããµãŒãã¹çšã®javascriptã«ãŒã¿ãŒã-ã©ã€ãã©ãªã®Webãµã€ãã«ç€ºãããŠããŸããç°ãªããã¬ãŒã ã¯ãŒã¯ã§èšè¿°ãããã¢ããªã±ãŒã·ã§ã³ãåãããŒãžã§åæã«å®è¡ã§ããŸãããã®ãœãªã¥ãŒã·ã§ã³ã¯æ©èœããŸããã§ãããã»ãšãã©ã®æ©èœã¯å¿ èŠãããŸããã§ããããŸããSystem.jsããŒããŒã䜿çšãããšãWebpackã䜿çšããŠãã«ããããšãã«åé¡ãçºçããå ŽåããããŸãããŸããwebpackã§ã¢ãžã¥ãŒã«ããŒããŒã䜿çšããããšã¯æåã®è§£æ±ºçã§ã¯ãªãããã§ãã
AngularèŠçŽ ïŒãã®ããã±ãŒãžã䜿çšãããšãAngularã³ã³ããŒãã³ããWebã³ã³ããŒãã³ãã§ã©ããã§ããŸããã¢ããªã±ãŒã·ã§ã³å šäœãã©ããã§ããŸããæ¬¡ã«ãå€ããã©ãŠã¶çšã«ããªãã£ã«ã远å ããå¿ èŠããããŸããç¬èªã®ã«ãŒãã£ã³ã°ã䜿çšããŠã¢ããªã±ãŒã·ã§ã³å šäœããWebã³ã³ããŒãã³ããäœæããããšã¯ãã€ããªãã®ãŒçã«ééã£ã決å®ã®ããã«èŠããŸãã
ãã¬ãŒã ãããŒãžã£ãŒã®å®è£
äŸã䜿çšããŠããã¬ãŒã ãããŒãžã£ã§ãã¬ãŒã ãªãã§ã¢ããªã±ãŒã·ã§ã³ãããŒãããæ¹æ³ãèŠãŠã¿ãŸãããã
åæèšå®ã¯æ¬¡ã®ããã«ãªããŸããã¡ã€ã³ã¢ããªã±ãŒã·ã§ã³-mainããããŸããåžžã«æåã«ããŒããããããèªäœã®äžã«ä»ã®ã¢ããªã±ãŒã·ã§ã³ïŒapp-1ããã³app-2ïŒãããŒãããå¿ èŠããããŸããng new <app-name>ã³ãã³ãã䜿çšããŠ3ã€ã®ã¢ããªã±ãŒã·ã§ã³ãäœæããŸããããæ¬¡ã¯ãããã«ãããã·ã®èšå®ã«å¿ èŠãªã¢ããªã±ãŒã·ã§ã³ã®HTMLãšJSãã¡ã€ã«ããããŠãããã©ãŒã ã®èŠæ±ã«éã/<app-name>/*.jsã/<app-name>/*.htmlãããã³ã¡ã€ã³ã¢ããªã±ãŒã·ã§ã³ã®éååŠã¯ãä»ã®ãã¹ãŠã®èŠæ±ã«éä¿¡ãããŸãã
proxy.conf.js
const cfg = [
{
context: [
'/app1/*.js',
'/app1/*.html'
],
target: 'http://localhost:3001/'
},
{
context: [
'/app2/*.js',
'/app2/*.html'
],
target: 'http://localhost:3002/'
}
];
module.exports = cfg;
ã¢ããªã±ãŒã·ã§ã³app-1ããã³app-2ã®å Žåããããããangular.jsonapp1ããã³app2ã§baseHrefãæå®ããŸãããŸããã«ãŒãã³ã³ããŒãã³ãã»ã¬ã¯ã¿ãŒãapp-1ãšapp-2ã«å€æŽããŸãã
ããã¯ã¡ã€ã³ã¢ããªã±ãŒã·ã§ã³ãã©ã®ããã«èŠãããã§ã
ãŸããå°ãªããšã1ã€ã®ãµãã¢ããªã±ãŒã·ã§ã³ãããŒãããŸãããããããè¡ãã«ã¯ãindex.htmlã§æå®ãããŠãããã¹ãŠã®jsãã¡ã€ã«ãããŒãããå¿ èŠããããŸãã
jsãã¡ã€ã«ã®URLã確èªããŸããindex.htmlã®httpãªã¯ãšã¹ããäœæããDOMParserã䜿çšããŠæååãè§£æãããã¹ãŠã®ã¹ã¯ãªããã¿ã°ãéžæããŸãããã¹ãŠãé åã«å€æããŠãã¢ãã¬ã¹ã®é åã«ãããããŸãããããã®æ¹æ³ã§ååŸããã¢ãã¬ã¹ã«ã¯location.originãå«ãŸãããããç©ºã®æååã«çœ®ãæããŸãã
private getAppHTML(): Observable<string> {
return this.http.get(`/${this.currentApp}/index.html`, {responseType: 'text'});
}
private getScriptUrls(html: string): string[] {
const appDocument: Document = new DOMParser().parseFromString(html, 'text/html');
const scriptElements = appDocument.querySelectorAll('script');
return Array.from(scriptElements)
.map(({src}) => src.replace(this.document.location.origin, ''));
}
ã¢ãã¬ã¹ããããŸããã¹ã¯ãªãããããŒãããå¿ èŠããããŸãã
private importJs(url: string): Observable<void> {
return new Observable(sub => {
const script = this.document.createElement('script');
script.src = url;
script.onload = () => {
this.document.head.removeChild(script);
sub.next();
sub.complete();
};
script.onerror = e => {
sub.error(e);
};
this.document.head.appendChild(script);
});
}
ã³ãŒãã¯ãå¿ èŠãªsrcãå«ãã¹ã¯ãªããèŠçŽ ãDOMã«è¿œå ããã¹ã¯ãªãããããŠã³ããŒãããåŸããããã®èŠçŽ ãåé€ããŸããããã¯ããªãæšæºçãªãœãªã¥ãŒã·ã§ã³ã§ãããwebpackãšsystem.jsãžã®ããŒããåæ§ã«å®è£ ãããŸãã
ã¹ã¯ãªãããããŒãããåŸïŒçè«çã«ã¯ïŒãçµã¿èŸŒã¿ã¢ããªã±ãŒã·ã§ã³ãèµ·åããããã®ãã¹ãŠãæã£ãŠããŸãããããå®éã«ã¯ãã¡ã€ã³ã¢ããªã±ãŒã·ã§ã³ã®ååæåãååŸããŸããããŒããããã¢ããªãã¡ã€ã³ã®ã¢ããªãšäœããã®åœ¢ã§ç«¶åããŠããããã§ãããiframeã«ããŒãããããšãã«çºçããŸããã§ããã
Webããã¯ãã³ãã«ã®èªã¿èŸŒã¿
Angularã¯webpackã䜿çšããŠã¢ãžã¥ãŒã«ãããŒãããŸããæšæºæ§æã§ã¯ãWebpackã¯ã³ãŒããæ¬¡ã®ãã³ãã«ã«åå²ããŸãã
- main.js-ãã¹ãŠã®ã¯ã©ã€ã¢ã³ãã³ãŒãã
- polyfills.js-ããªãã£ã«;
- styles.js-ã¹ã¿ã€ã«;
- vendor.js-Angularãå«ããã¢ããªã±ãŒã·ã§ã³ã§äœ¿çšããããã¹ãŠã®ã©ã€ãã©ãªã
- runtime.js-webpackã©ã³ã¿ã€ã ;
- <module-name> .module.js-ã¬ã€ãžãŒã¢ãžã¥ãŒã«ã
ãããã®ãã¡ã€ã«ã®ãããããéããšãæåã«æ¬¡ã®ã³ãŒãã衚瀺ãããŸãã
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([/.../])
ãããŠruntime.jsã§ã¯ïŒ
var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
jsonpArray.push = webpackJsonpCallback;
jsonpArray = jsonpArray.slice();
for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
ããã¯æ¬¡ã®ããã«æ©èœããŸãããã³ãã«ãããŒãããããšãé åwebpackJsonpããŸã ååšããªãå Žåã¯äœæããããã®å 容ããã³ãã«ã«ããã·ã¥ãããŸããwebpackã©ã³ã¿ã€ã ã¯ããã®é åã®ããã·ã¥æ©èœããªãŒããŒã©ã€ãããŠãåŸã§æ°ãããã³ãã«ãã¢ããããŒãã§ããããã«ããé åã«æ¢ã«ååšãããã¹ãŠã®ãã®ãåŠçããŸãã
ãã³ãã«ãããŒããããé åºãéèŠã«ãªããªãããã«ãããã¯ãã¹ãŠå¿ èŠã§ãã
ãããã£ãŠã2çªç®ã®Angularã¢ããªã±ãŒã·ã§ã³ãããŒããããšããã®ã¢ãžã¥ãŒã«ãæ¢åã®Webpackã©ã³ã¿ã€ã ã«è¿œå ããããšããŸããããã«ãããããããã¡ã€ã³ã¢ããªã±ãŒã·ã§ã³ãååæåãããŸãã
webpackJsonpã®ååã倿ŽããŸã
ç«¶åãåé¿ããã«ã¯ãwebpackJsonpé åã®ååã倿Žããå¿ èŠããããŸãã Angular CLIã¯ç¬èªã®webpackæ§æã䜿çšããŸãããå¿ èŠã«å¿ããŠæ¡åŒµã§ããŸãããããè¡ãã«ã¯ãangular-builders / custom-webpackããã±ãŒãžãã€ã³ã¹ããŒã«ããå¿ èŠããããŸãïŒ
npm i -D @ angle-builders / custom-webpackã
次ã«ããããžã§ã¯ãæ§æã®angular.jsonãã¡ã€ã«ã§ãarchitect.build.builderã@ angle-builders / custom-webpackïŒbrowserã«çœ®ãæããarchitect.build.optionsïŒã«è¿œå ããŸãã
"customWebpackConfig": {
"path": "./custom-webpack.config.js"
}
ãŸãããããdevãµãŒããŒãšããŒã«ã«ã§æ©èœãããã«ã¯ãarchitect.serve.builderã@ angle-builders / custom-webpackïŒdev-serverã«çœ®ãæããå¿ èŠããããŸãã
次ã«ãäžèšã®customWebpackConfigã§æå®ãããŠããWebpackæ§æãã¡ã€ã«ãäœæããå¿ èŠããããŸããcustom -webpack.config.js
ã«ã¹ã¿ã èšå®ãå®çŸ©ããŸãã詳现ã«ã€ããŠã¯ãå ¬åŒããã¥ã¡ã³ããåç §ããŠãã ãããjsonpFunctionã«
èå³ããããŸãã
ããŒãããããã¹ãŠã®ã¢ããªã±ãŒã·ã§ã³ã§ãã®ãããªæ§æãèšå®ããŠãç«¶åãåé¿ã§ããŸãïŒãã®åŸãç«¶åãæ®ã£ãŠããå Žåã¯ãåªãããŠããå¯èœæ§ããããŸãïŒã
module.exports = {
output: {
jsonpFunction: Math.random().toString()
},
};
ããã§ãäžèšã®æ¹æ³ã§ãã¹ãŠã®ã¹ã¯ãªãããããŒãããããšãããšããšã©ãŒã衚瀺ãããŸãã
ã¢ããªã±ãŒã·ã§ã³ãããŒãããåã«ããã®ã«ãŒãèŠçŽ ãDOMã«è¿œå ããå¿ èŠããããŸãã
private addAppRootElement(appName: string) {
const rootElementSelector = APP_CFG[appName].rootElement;
this.appRootElement = this.document.createElement(rootElementSelector);
this.appContainer.nativeElement.appendChild(this.appRootElement);
}
ããäžåºŠè©ŠããŠã¿ãŸããã-äžæ³ãã¢ããªã±ãŒã·ã§ã³ãããŒããããŸããïŒ
ã¢ããªã±ãŒã·ã§ã³ãåãæ¿ãã
以åã®ã¢ããªã±ãŒã·ã§ã³ãDOMããåé€ããã¢ããªã±ãŒã·ã§ã³ãåãæ¿ããããšãã§ããŸãã
destroyApp () {
if (!this.currentApp) return;
this.appContainer.nativeElement.removeChild(this.appRootElement);
}
ãã ããããã«ã¯æ¬ é¥ããããŸããapp-1âapp-2âapp-1ã«ç§»åãããšãapp-1ã¢ããªã±ãŒã·ã§ã³ã®jsãã³ãã«ããªããŒããããããã®ã³ãŒããå®è¡ããŸããããã«ã以åã«ããŒããããã¢ããªã±ãŒã·ã§ã³ãç Žæ£ããªããããã¡ã¢ãªãªãŒã¯ãäžèŠãªãªãœãŒã¹æ¶è²»ãçºçããŸãã
ã¢ããªã±ãŒã·ã§ã³ãã³ãã«ãåããŠã³ããŒãããªãå ŽåãããŒãã¹ãã©ããããã»ã¹ã¯ããèªäœã§ã¯å®è¡ããããã¢ããªã±ãŒã·ã§ã³ã¯ããŒããããŸãããããŒãã¹ãã©ããèµ·åããã»ã¹ãã¡ã€ã³ã¢ããªã±ãŒã·ã§ã³ã«å§ä»»ããå¿ èŠããããŸãã
ãããè¡ãã«ã¯ãããŒããããã¢ããªã±ãŒã·ã§ã³ã®main.tsãã¡ã€ã«ãæžãçŽããŠã¿ãŸãããã
const BOOTSTRAP_FN_NAME = 'ngBootstrap';
const bootstrapFn = (opts?) => platformBrowserDynamic().bootstrapModule(AppModule, opts);
window[BOOTSTRAP_FN_NAME] = bootstrapFn;
bootstrapModule ã¡ãœããã¯ããã«ã¯å®è¡ãããŸããããã°ããŒãã«å€æ°ã«ããã©ãããŒé¢æ°ã«æ ŒçŽãããŸããã¡ã€ã³ã¢ããªã±ãŒã·ã§ã³ã§ã¯ãããã«ã¢ã¯ã»ã¹ããŠãå¿ èŠã«å¿ããŠå®è¡ã§ããŸãã
ã¢ããªã±ãŒã·ã§ã³ãç Žæ£ããŠã¡ã¢ãªãªãŒã¯ãä¿®æ£ããã«ã¯ãã«ãŒãã¢ããªã±ãŒã·ã§ã³ã¢ãžã¥ãŒã«ïŒAppModuleïŒã®destroyã¡ãœãããåŒã³åºããŸãã platformBrowserDynamicïŒïŒãBootstrapModuleã¡ãœããã¯ãããã«ãªã³ã¯ãè¿ããŸããããã¯ãã©ãããŒé¢æ°ãæå³ããŸãã
this.getBootstrapFn$().subscribe((bootstrapFn: BootstrapFn) => {
this.zone.runOutsideAngular(() => {
bootstrapFn().then(m => {
this.ngModule = m; //
});
});
});
this.ngModule.destroy(); //
ã«ãŒãã¢ãžã¥ãŒã«ã§destroyïŒïŒãåŒã³åºããåŸããã¹ãŠã®ãµãŒãã¹ããã³ã¢ããªã±ãŒã·ã§ã³ã³ã³ããŒãã³ãïŒå®è£ ãããŠããå ŽåïŒã®ngOnDestroyïŒïŒã¡ãœãããåŒã³åºãããŸãã
ãã¹ãŠãæ©èœããŸãããã ããããŒããããã¢ããªã±ãŒã·ã§ã³ã«é å»¶ã¢ãžã¥ãŒã«ãå«ãŸããŠããå Žåããããã¯ããŒãã§ããŸããã
ã¢ãã¬ã¹ã«ã¢ããªã±ãŒã·ã§ã³ãã¹ããªãããšãããããŸãïŒ/app2/lazy-lazy-module.jsãããã¯ãã§ãïŒããã®åé¡ã解決ããã«ã¯ãã¡ã€ã³ã¢ããªã±ãŒã·ã§ã³ãšããŒããããã¢ããªã±ãŒã·ã§ã³ã®ããŒã¹hrefãåæããå¿ èŠããããŸãã
private syncBaseHref(appBaseHref: string) {
const base = this.document.querySelector('base');
base.href = appBaseHref;
}
ããã§ããã¹ãŠãæ£åžžã«æ©èœããŸãã
çµæ
ã¡ã€ã³ã¢ããªã±ãŒã·ã§ã³ã«ã¹ã¯ãªãããããŒãããåã«console.timeïŒïŒãé 眮ããã¡ã€ã³ã¢ããªã±ãŒã·ã§ã³ã®ã«ãŒãã³ã³ããŒãã³ãã®ã³ã³ã¹ãã©ã¯ã¿ãŒã«console.timeEndïŒïŒãé 眮ããŠããµãã¢ããªã±ãŒã·ã§ã³ã®ããŒãã«ãããæéãèŠãŠã¿ãŸãããã
app-1ããã³app-2ã¢ããªã±ãŒã·ã§ã³ãåããŠããŒãããããšã次ã®ãããªãã®ã衚瀺ãã
ãŸããããªãé«éã§ãããã ãã以åã«ããŠã³ããŒãããã¢ããªã±ãŒã·ã§ã³ã«æ»ããšã次ã®çªå·ã衚瀺
ãããŸããå¿ èŠãªãã¹ãŠã®ãã£ã³ã¯ããã§ã«ã¡ã¢ãªã«ãããããã¢ããªã±ãŒã·ã§ã³ã¯å³åº§ã«ããŒããããŸãããã ããã¢ããªã±ãŒã·ã§ã³ãç Žæ£ãããå Žåã§ããã¡ã¢ãªãªãŒã¯ãçºçããå¯èœæ§ããããããæªäœ¿çšã®ãªããžã§ã¯ãåç §ãšãµãã¹ã¯ãªãã·ã§ã³ã«ã¯ããã«æ³šæããå¿ èŠããããŸãã
ãã¬ãŒã ãªãã®ãã¬ãŒã ãããŒãžã£ãŒ
äžèšã®ãœãªã¥ãŒã·ã§ã³ã¯ãiframeã®æç¡ã«ãããããã¢ããªã±ãŒã·ã§ã³ã®ããŒãããµããŒããããã¬ãŒã ãããŒãžã£ãŒã«å®è£ ãããŠããŸããTinkoff Businessã®ãã¹ãŠã®ã¢ããªã±ãŒã·ã§ã³ã®çŽ4åã®1ããã¬ãŒã ãªãã§ããŒãããããã®æ°ã¯çµ¶ããå¢å ããŠããŸãã
ãŸãã説æãããœãªã¥ãŒã·ã§ã³ã®ãããã§ãAngularãšããã¬ãŒã ãããŒãžã£ãŒããã³ã¢ããªã±ãŒã·ã§ã³ã§äœ¿çšãããäžè¬çãªã©ã€ãã©ãªãæäœããæ¹æ³ãåŠã³ãŸãããããã«ãããèªã¿èŸŒã¿ãšäœæ¥ã®é床ãããã«åäžããŸãããããã«ã€ããŠã¯æ¬¡ã®èšäºã§èª¬æããŸãã
ãµã³ãã«ã³ãŒãã®ãããªããžããª