@tramvai/module-child-app v3.40.28
ChildApp
Module for child app
Installation
First, install @tramvai/module-child-app
yarn add @tramvai/module-child-app
And then add module to your app
import { createApp } from '@tramvai/core';
import { ChildAppModule } from '@tramvai/module-child-app';
createApp({
name: 'tincoin',
modules: [ChildAppModule],
});
Explanation
Terms
root-app
- basic tramvai-app constructed withcreateApp
from@tramvai/core
. It can connect with many child-appchild-app
- external microfrontend constructed withcreateChildApp
from@tramvai/child-app-core
. It is loaded by root-app and provides some external functionalitySingletonDI
- DI-container which is exist in single instance for app and exists as long as app itselfRequestDI
- DI-Container which is created for every request and represents specific data for single client. RequestDI inherits providers from SingletonDI and it is independent from other RequestDIsCommandLineRunner
- instance of CommandModule
DI
Every child-app has its own DI-hierarchy which is isolated from other child app and partially from root-app. The only way communicate fpr DIs it's getting providers from root-app di inside child-app.
Next picture shows connection between DI-containers in root-app
and child-app
s
How does it work when we trying to get provider from DI in child-app
:
- First check that provider is exist in the current DI-container. If it is then return it.
- If current DI is
RequestDI
then go toSingletonDI
ofchild-app
and look for provider.- If it exists in
SingletonDI
then return it - Go to
RequestDI
ofroot-app
and if provider exists in it return it - Go to
SingletonDI
ofroot-app
and if provider exists in it return it - Throw error otherwise
- If it exists in
- If current DI is
SingletonDI
then go toSingletonDI
ofroot-app
and check for provider there- If it exists then return it
- Throw error otherwise
CommandLineRunner
Each child-app
has its own CommandLineRunner instance which allows to child-app
make some preparations before the actual page render. This CommandLineRunner has almost identical lines as root-app
to simplicity, but it is actually completely other line which are independent from lines in root-app
All of the accepted line tokens:
const command = {
customer: [
commandLineListTokens.customerStart,
commandLineListTokens.resolveUserDeps,
commandLineListTokens.resolvePageDeps,
],
clear: [commandLineListTokens.clear],
spa: [
commandLineListTokens.resolveUserDeps,
commandLineListTokens.resolvePageDeps,
commandLineListTokens.spaTransition,
],
};
Child-app must be preloaded first to allow to execute commandline runner. In case of late preloading CommandLineRunner will be executed anyway but it will be out of sync with root-app CommandLineRunner (it will be called as soon as child-app code was loaded).
Server
- If child-app was preloaded before root-app
resolvePageDeps
thencustomer
line list is executed on root-appresolvePageDeps
line - If child-app was preloaded on root-app
resolvePageDeps
thencustomer
line list is executed as soon as child-app was loaded.preload
call must be awaited in order to prevent root-app CommandLineRunner to passing to next line. That still counts as executing onresolvePageDeps
line. - Child-app
clear
line list is executed on root-appclear
line for every child-app that was preloaded on previous lines
Client
First Page load
- If child-app was preloaded on server
customer
line list is executed on root-appresolvePageDeps
line - If child-app was not preloaded on server but was preloaded on client then
customer
line list is executed on root-appclear
line - Child-app
clear
line list is executed on root-appclear
line for every child-app that was preloaded on previous lines
Spa-transitions
- If child-app was not preloaded on any previous pages before but was preloaded on next page then
customer
line list is executed as soon as child-app is loaded - If child-app was preloaded on next page then child-app
spa
line list is executed on root-appspaTransition
line
Loading Child App
Loading of child-app is happens only after preloading child-app with CHILD_APP_PRELOAD_MANAGER
. This preloading loads code for a child-app and marks it to execution using CommandLineRunner.
Server
- Calling
PreloadManager.preload(...)
loads a child-app code, executes and marks it as executable to CommandLineRunner - Result of
PreloadManager.preload(...)
must be awaited as it is important to synchronize child-app commands lines execution with a root-appCommandLinerRunner
- Preloads after root-app
resolvePageDeps
are useless as they wont change page render and wont be used by root-app.
Client
- Calling
PreloadManager.preload(...)
loads a child-app code, executes and marks it as executable to CommandLineRunner - Result of
PreloadManager.preload(...)
must be awaited as it is important to synchronize child-app commands lines execution with a root-appCommandLinerRunner
- If child-app was preloaded on server then child-app
customer
line list is executed onresolvePageDeps
on first page render - If child-app was not preloaded on server then actual loading and command-line execution are happens on root-app
clear
line as executing child-app before page render may break React hydration and should be executed only after it. - On spa transition when previously child-app is preloaded it will be reused
- On spa transition if preloaded child-app was not loaded before it will be loaded and executed as soon as possible.
State
State Management is almost completely isolated from root-app and other of child-apps. Every child-app can register own stores, actions.
State for child-apps will be dehydrated on server as separate variable in the result html and then will be automatically rehydrated on client for every child-app.
:::warning
By default, child-app cannot read data from root-app stores, but the you can specify the set of root-app stores that might be used inside child-app.
It may be done using CHILD_APP_INTERNAL_ROOT_STATE_ALLOWED_STORE_TOKEN
token.
This token is considered undesirable to use as it leads to high coupling with stores from root-app and this way stores in root-app might not change their public interface. But, in most cases, changes in stores ignore breaking change tracking and may breaks backward-compatibility. So do not use this token if you can, and if you should - use as little as possible from root-app and provide some fallback in case of wrong data.
:::
API
CHILD_APP_INTERNAL_ROOT_STATE_ALLOWED_STORE_TOKEN
Defines the list of allowed root-app store names that might be used inside child-app.
Specify stores that might be used inside child-app
provide({ provide: CHILD_APP_INTERNAL_ROOT_STATE_ALLOWED_STORE_TOKEN, multi: true, useValue: [MediaStore, AuthenticateStore], });
Use the specified root-app stores the same way as usual stores
import React from 'react'; import { useSelector } from '@tramvai/state'; export const StateCmp = () => { const value = useSelector(['root'], (state) => { return state.root.value; }); return <div id="child-state">Current Value from Root Store: {value}</div>; };
How to
Connect a child app
Place a child-app React component somewhere in your page render
import React from 'react'; import { ChildApp } from '@tramvai/module-child-app'; export const Page = () => { return ( <div> ... <ChildApp name="[name]" /> ... </div> ); };
Add configuration for child-app loading
providers: [ provide({ provide: CHILD_APP_RESOLVE_BASE_URL_TOKEN, // or use `CHILD_APP_EXTERNAL_URL` env useValue: 'http://localhost:4040/', }), provide({ provide: CHILD_APP_RESOLUTION_CONFIGS_TOKEN, useValue: [ { name: '[name]', // name of the child-app byTag: { latest: { version: '[version]', // current version for the child app for tag `latest` }, }, }, ], }), ];
Preload child-app execution in order to improve performance and allow child-app execute its data preparations
import { commandLineListTokens, Provider, provide } from '@tramvai/core'; import { CHILD_APP_PRELOAD_MANAGER_TOKEN } from '@tramvai/module-child-app'; const providers: Provider[] = [ provide({ provide: commandLineListTokens.customerStart, multi: true, useFactory: ({ preloadManager }) => { return function preloadHeaderChildApp() { return preloadManager.preload({ name: '[name]' }); // this call is important }; }, deps: { preloadManager: CHILD_APP_PRELOAD_MANAGER_TOKEN, }, }), ];
Preload child-app
Preloading is vital for using child-app without extensive overhead on its loading.
You may preload using next ways:
Preload with
CHILD_APP_PRELOAD_MANAGER_TOKEN
provide({ provide: commandLineListTokens.customerStart, multi: true, useFactory: ({ preloadManager }) => { return function preloadHeaderChildApp() { return preloadManager.preload({ name: '[name]' }); }; }, deps: { preloadManager: CHILD_APP_PRELOAD_MANAGER_TOKEN, }, });
Add needed child-apps to the pageComponent or layoutComponent through field
childApps
const PageComponent = () => {
return 'Page';
};
PageComponent.childApps = [{ name: '[name]' }];
Debug child-app
Single child-app
Run child-app using cli
yarn tramvai start child-app
Run root-app with
CHILD_APP_DEBUG
environment variableCHILD_APP_DEBUG=child-app yarn tramvai start root-app
Multiple child-app
- Run somehow multiple child-apps. They should be started on different ports.
And either pass
Base Url
showed from cli as url to debug every child-appCHILD_APP_DEBUG=child-app1=baseUrl1;child-app2=baseUrl2 yarn tramvai start root-app
Or implement proxy on default
http:://localhost:4040/
yourself which redirects to concrete server by urlCHILD_APP_DEBUG=child-app1;child-app2 yarn tramvai start root-app
More detailed debug setup
You may specify a full config to debug to a specific child-app:
- To token
CHILD_APP_RESOLUTION_CONFIGS_TOKEN
for needed child-apps add special tagdebug
:({ name: 'child-app', byTag: { latest: { version: 'latest', }, debug: { baseUrl: '...url', version: '...version', client: {}, server: {}, css: {}, }, }, });
- Run root-app with
CHILD_APP_DEBUG
environment variable with value of child-app names needed to debug
3 days ago
8 days ago
8 days ago
8 days ago
9 days ago
8 days ago
9 days ago
12 days ago
12 days ago
13 days ago
13 days ago
17 days ago
17 days ago
18 days ago
18 days ago
18 days ago
19 days ago
19 days ago
19 days ago
19 days ago
20 days ago
20 days ago
22 days ago
24 days ago
24 days ago
24 days ago
24 days ago
24 days ago
27 days ago
27 days ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
8 months ago
8 months ago
8 months ago
8 months ago
6 months ago
7 months ago
8 months ago
8 months ago
8 months ago
8 months ago
9 months ago
9 months ago
9 months ago
7 months ago
5 months ago
9 months ago
7 months ago
7 months ago
7 months ago
6 months ago
6 months ago
6 months ago
6 months ago
10 months ago
5 months ago
5 months ago
10 months ago
6 months ago
8 months ago
8 months ago
5 months ago
9 months ago
7 months ago
9 months ago
6 months ago
8 months ago
8 months ago
10 months ago
9 months ago
10 months ago
9 months ago
10 months ago
10 months ago
6 months ago
9 months ago
7 months ago
9 months ago
9 months ago
8 months ago
9 months ago
9 months ago
9 months ago
8 months ago
8 months ago
7 months ago
8 months ago
8 months ago
6 months ago
10 months ago
6 months ago
7 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
6 months ago
8 months ago
9 months ago
8 months ago
9 months ago
9 months ago
8 months ago
9 months ago
8 months ago
6 months ago
7 months ago
7 months ago
7 months ago
8 months ago
9 months ago
6 months ago
6 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
6 months ago
7 months ago
11 months ago
11 months ago
11 months ago
1 year ago
12 months ago
12 months ago
1 year ago
1 year ago
1 year ago
1 year ago
12 months ago
12 months ago
12 months ago
12 months ago
11 months ago
11 months ago
11 months ago
12 months ago
11 months ago
12 months ago
11 months ago
11 months ago
11 months ago
12 months ago
12 months ago
12 months ago
11 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
1 year ago
1 year ago
2 years ago
2 years ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago