This repository contains the sample code which shows how to load various applications created using different technology stack ex. React, Angular, Vue, Backbone, inside a single-spa shell.
single-spa is an awesome project which makes it very easy to build Front-end Microservices.
This project consist of following folders:
- angularApp - A very minimilistic application written in Angular.
//singleSpaEntry
import 'core-js/es7/reflect';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { MainModule } from './main-module';
import { environment } from '../environments/environment';
import { Router } from '@angular/router';
if (environment.production) {
enableProdMode();
}
const spaProps = {
bootstrappedModule: null,
Router: Router
};
// This lifecycle function will be called by singleSPA exactly once, right before the registered application is mounted for the first time.
export function bootstrap(props) {
return Promise.resolve();
}
// This lifecycle function is called by singleSPA every time the route for this app is active and the app should be rendered.
export function mount(props) {
createDomElement();
return platformBrowserDynamic()
.bootstrapModule(MainModule).then(module => {
return spaProps.bootstrappedModule = module;
});
}
// This lifecycle function will be called when the user navigates away from this apps route.
export function unmount(props) {
return new Promise((resolve, reject) => {
spaProps.bootstrappedModule.destroy();
delete spaProps.bootstrappedModule;
resolve();
});
}
function createDomElement() {
// Make sure there is a div for us to render into
let el = window.document.getElementById('shell-container');
if (el) {
let cl = window.document.createElement('appMain');
cl.id = 'app2';
el.appendChild(cl);
}
return el;
}- reactApp - A very minimilistic application written in React
//singleSpaEntry
import React from 'react';
import ReactDOM from 'react-dom';
import singleSpaReact from 'single-spa-react';
import Root from './root.component.js';
const reactLifecycles = singleSpaReact({
React,
ReactDOM,
rootComponent: Root,
domElementGetter,
});
export function bootstrap(props) {
return reactLifecycles.bootstrap(props);
}
export function mount(props) {
return reactLifecycles.mount(props);
}
export function unmount(props) {
return reactLifecycles.unmount(props);
}
function domElementGetter() {
// Make sure there is a div for us to render into
let el = document.getElementById('shell-container');
if (!el) {
el = document.createElement('div');
el.id = 'app1';
document.body.appendChild(el);
}
return el;
}- vueApp - A very minimilistic application written in VueJs
//singleSpaEntry
import Vue from 'vue'
import singleSpaVue from 'single-spa-vue';
import App from './App.vue'
Vue.config.productionTip = false;
const vueLifecycles = singleSpaVue({
Vue,
appOptions: {
el: '#app3',
render: h => h(App)
}
});
export const bootstrap = [
vueLifecycles.bootstrap,
];
export function mount(props) {
createDomElement();
return vueLifecycles.mount(props);
}
export const unmount = [
vueLifecycles.unmount,
];
function createDomElement() {
// Make sure there is a div for us to render into
let el = document.getElementById('shell-container');
if (el) {
el = document.createElement('div');
el.id = 'app3';
document.body.appendChild(el);
}
return el;
}- backboneApp1 - A very minimilistic application written in Backbone
//singleSpaEntry
import singleSpaBackbone from '@emtecinc/single-spa-backbone';
const backBoneLifecycles = singleSpaBackbone({
BasePath: 'app4',
AppWithRequire:
{
IsDataMain: true,
AppPath: 'src/app',
RequireJsPath: 'lib/require.js'
},
DomElementSetter: domElementSetter
});
export const bootstrap = [
backBoneLifecycles.bootstrap,
];
export const mount = [
backBoneLifecycles.mount,
];
export const unmount = [
backBoneLifecycles.unmount,
];
function domElementSetter() {
//use the same element to render into in the backbone app
let el = document.getElementById('shell-container');
if (!el) {
el = document.createElement('div');
el.id = 'shell-container';
document.body.appendChild(el);
}
}- backboneApp2 - Another sample application written in Backbone
//singleSpaEntry
import singleSpaBackbone from '@emtecinc/single-spa-backbone';
const backBoneLifecycles = singleSpaBackbone({
BasePath: 'app5',
AppWithRequire:
{
IsDataMain: false,
AppPath: 'index.js',
RequireJsPath: 'js/require.js'
},
DomElementSetter: domElementGetter
});
export const bootstrap = [
backBoneLifecycles.bootstrap,
];
export const mount = [
backBoneLifecycles.mount,
];
export const unmount = [
backBoneLifecycles.unmount,
];
function domElementGetter() {
//use the same element to render into in the backbone app
let el = document.getElementById('shell-container');
if (!el) {
el = document.createElement('div');
el.id = 'shell-container';
document.body.appendChild(el);
}
}Each of the above application has a singleSpaEntry file, which is called by the shell, and has several lifecycle methods like bootstrap, mount & unmount, which gets called at various stages of single-spa application.
Each of the apps are hosted using webpack-dev-server and runs on different port over localhost.
Please go through this link for further details.
-
shell - This is the main application, where the above applications gets loaded. It acts as an orchestrator, and can also provides common services to the apps getting loaded.
webpack-dev-serveris used to host shell, as well.shell registers all the application to
single-spaframework, by calling loadApp functions.
// app1: The URL "/app1/..." is redirected to "http://localhost:9001/..." this is done by the webpack proxy (webpack.config.js)
loadApp('app1', '/app1', '/app1/singleSpaEntry.js', '/app1/store.js', globalEventDistributor);
// app2: The URL "/app2/..." is redirected to "http://localhost:9002/..." this is done by the webpack proxy (webpack.config.js)
loadApp('app2', '/app2', '/app2/singleSpaEntry.js', '/app2/store.js', globalEventDistributor);
// app3: The URL "/app3/..." is redirected to "http://localhost:9003/..." this is done by the webpack proxy (webpack.config.js)
loadApp('app3', '/app3', '/app3/singleSpaEntry.js', null, null); // does not have a store, so we pass null
// app4: The URL "/app4/..." is redirected to "http://localhost:9004/..." this is done by the webpack proxy (webpack.config.js)
loadApp('app4', '/app4', '/app4/singleSpaEntry.js', null, null);
// app5: The URL "/app5/..." is redirected to "http://localhost:9005/..." this is done by the webpack proxy (webpack.config.js)
loadApp('app5', '/app5', '/app5/singleSpaEntry.js', null, null);loadApp function calls singleSpa.registerApplication function as shown below.
function loadApp(name, hash, appURL, storeURL, globalEventDistributor)
{
... //rest of the code
singleSpa.registerApplication(name, () => SystemJS.import(appURL), hashPrefix(hash), customProps);
}Go to root of each of the sub-folders i.e. angularApp, backboneApp1, backboneApp2, reactApp, shell & vueApp and run below command one by one.
npm installIt will install all the npm packages specified in the package.json.
followed by
npm run watchThis will start the webpack-dev-server and will run each of the applications.
Once, all the apps are running go to http://localhost:9000 where the shell has been configured to run.