微前端 Micro-Frontnds - Single-SPA Application API

Felix皇子發表於2020-11-07

微前端 Micro-Frontnds - Single-SPA Application API

Application API

import { registerApplication, start } from 'single-spa';
// or
import * as singleSpa from 'single-spa';

registerApplication

使用在 root config 中,用於註冊應用
Simple arguments

singleSpa.registerApplication(
	'app-name',
	() => System.import('appName'),
	location => location.pathname.startsWith('app')
)

Arguments
- appName: string 應用的名稱
- applicationOrLoadingFn:() => <Function | Promise> 一個返回應用或 Promise 的函式
- activeFn: (location) => boolean 純函式,以 window.location 為引數,返回true 表示啟用該應用,false 表示解除安裝該應用
- customProps?: Object | () => Object 可選,需要傳遞給應用生命週期鉤子的自定義引數

Configuration Object

singleSpa.registerApplication({
	name: 'app-name',
	app: () => System.import('appName'),
	activeWhen: '/app',
	customProps: {
		authToken: 'xxxxx'
	}
})

Arguments
- name: string 應用的名稱
- app: Application | () => Application | Promise 應用
- activeWhen: string | (location) => boolean | (string | (location) => boolean)[] 什麼時候啟用應用與上面的一樣
- customProps?: Object | () => Object 可選,需要傳遞給應用生命週期鉤子的自定義引數

start

讓 single-spa 對應用進行控制,讓您可以控制單頁應用程式的效能

singleSpa.start()

singleSpa.start({
	urlRerouteOnly: true
})

Arguments
一個可選的物件,這個物件有 urlRerouteOnly 屬性
urlRerouteOnly:一個預設為false的布林值。如果設定為true,則除非更改了客戶端路由,否則對history.pushState()和history.replaceState()的呼叫將不會觸發單spa重新路由。在某些情況下,將此值設定為true可能會更好。

triggerAppChange

手動觸發 single-spa 載入應用

singleSpa.triggerAppChange();

navigatToUrl

在已註冊的應用之間執行 url 導航,而不需要處理 event.preventDefault() pushState triggerAppChange

// Three ways of using navigateToUrl
singleSpa.navigateToUrl("/new-url");
singleSpa.navigateToUrl(document.querySelector('a'));
document.querySelector('a').addEventListener(singleSpa.navigateToUrl);

Arguments
navigationObj: string | context | DOMEvent
- url 字串
- 帶有 href 屬性的標籤,例如 a 標籤
- 一個具有href屬性的DOMElement上的click事件的DOMEvent物件;

getMountedApps

獲取掛載(MOUNTED)應用的名字,返回一個字串陣列

const mountedAppNames = singleSpa.getMountedApps();
console.log(mountedAppNames); // ['app1', 'app2', 'navbar']

getAppNames

獲取所有註冊的應用,與應用的狀態無關

const appNames = singleSpa.getAppNames();
console.log(appNames); // ['app1', 'app2', 'app3', 'navbar']

getAppStatus

獲取指定應用的狀態

const status = singleSpa.getAppStatus('app1');
console.log(status); // one of many statuses (see list below). e.g. MOUNTED

Arguments
appName: string 註冊應用的名字

Returns
appStatus: string | null , null 表示應用不存在
- NOT_LOADEDapp :應用已經註冊,但是還沒有載入好
- LOADING_SOURCE_CODE:已經獲取到原始碼
- NOT_BOOTSTRAPPED: 應用已經載入好,但是還沒開始初始化
- BOOTSTRAPPING:已經呼叫 bootstrap 生命週期鉤子,但是還沒執行完
- NOT_MOUNTED:已經執行好 bootstrap ,但是還沒掛載
- MOUNTING:正在執行 mount 鉤子
- MOUNTED:應用已經掛載在 DOM 上了
- UNMOUNTING:正在執行 unmount
- UNLOADING:正在執行 unloading
- SKIP_BECAUSE_BROKEN:應用在載入,引導,安裝或解除安裝期間引發了錯誤,並且由於行為不當而被跳過,因此已被隔離。其他應用將繼續正常執行。
- LOAD_ERROR:應用在 loading 方法中返回了 reject 的 Promise,這通常是由於嘗試下載應用程式的JavaScript軟體包的網路錯誤。

unloadApplication

解除安裝已註冊的應用程式的目的是將其設定回NOT_LOADED狀態,這意味著它將在下次需要安裝時重新引導。這樣做的主要用例是允許熱過載整個已註冊的應用程式,但是無論何時您要重新引導應用程式,unloadApplication都會很有用。

// Unload the application right now, without waiting for it to naturally unmount.
singleSpa.unloadApplication('app1');

// Unload the application only after it naturally unmounts due to a route change.
singleSpa.unloadApplication('app1', {waitForUnmount: true});

當呼叫 unloadApplication 後 Single-spa 會執行如下幾步
1. 在要解除安裝的註冊應用程式上呼叫解除安裝生命週期。
2. 設定 App 狀態為 NOT_LOADED
3. 觸發重新路由,在這期間 single-spa 會掛載剛解除安裝的應用
因為呼叫unloadApplication時可能會掛載已註冊的應用程式,所以您可以指定是要立即解除安裝還是要等待直到不再掛載該應用程式。這是通過waitForUnmount選項完成的。

Arguments
appName: string 應用名稱
Options?: { waitForUnmoun: boolean = false} 可選物件引數,預設 single-spa 會立即解除安裝指定應用,如果設定為 true,single-spa 會等應用不處於 MOUNTED 狀態的時候進行解除安裝。

unregisterApplication

unregisterApplication函式將解除安裝,解除安裝和登出應用程式,一旦呼叫,應用不會再掛載

import { unregisterApplication } from 'single-spa';

unregisterApplication('app1').then(() => {
    console.log('app1 is now unmounted, unloaded, and no longer registered!');
})
- Unregistering 應用程式不會將其從SystemJS模組登錄檔中刪除。
- Unregistering 應用程式不會從瀏覽器記憶體中刪除其程式碼或javascript框架。
- Unregistering 應用程式的另一種方法是在應用程式的活動功能內執行許可權檢查。這具有防止應用程式掛載的類似效果。

Arguments
appName: string 應用名稱

checkActivityFunctions

將使用模擬視窗位置呼叫每個應用程式的活動函式,並提供應在該位置掛載哪些應用程式的列表。

const appsThatShouldBeActive = singleSpa.checkActivityFunctions();
console.log(appsThatShouldBeActive); // ['app1']

const appsForACertainRoute = singleSpa.checkActivityFunctions({pathname: '/app2'});
console.log(appsForACertainRoute); // ['app2']

Arguments
mockWindowLocation: string 表示window.location的字串,將在呼叫每個應用程式的活動函式以測試它們是否返回true時使用。

Returns
appNames: string[] 返回符合的應用名稱的陣列

addErrorHandler

在生命週期函式或者啟用函式中報錯的時候會執行該方法,如果沒有這個處理函式,就會直接丟擲到視窗

singleSpa.addErrorHandler(err => {
    console.log(err);
    console.log(err.appOrParcelName);
    console.log(singleSpa.getAppStatus(err.appOrParcelName));
});

Arguments
errorHandler: Function(error: Error) 將通過Error物件呼叫,該物件還具有message和appOrParcelName屬性。

removeErrorHandler

移除錯誤處理函式

singleSpa.addErrorHandler(handleErr)
singleSpa.removeErrorHandler(handleErr)

function handleErr(err) {
    console.log(err)
}

Arguments
errorHandler: Function 函式名稱

Returns
Boolean :true 表示移除成功

mountRootParcel

建立並掛載一個 singel-spa parcel

// Synchronous mounting
const parcel = singleSpa.mountRootParcel(parcelConfig, {prop1: 'value1', domElement: document.getElementById('a-div')});
parcel.mountPromise.then(() => {
    console.log('finished mounting the parcel!')
})

// Asynchronous mounting. Feel free to use webpack code splits or SystemJS dynamic loading
const parcel2 = singleSpa.mountRootParcel(() => import('./some-parcel.js'), {prop1: 'value1', domElement: document.getElementById('a-div')});

Arguments
parcelConfig: object | loading Function
parcelProps: object 具有 DOM 元素屬性的物件

returns
Parcel object

pathToActiveWhen

pathToActiveWhen 函式將字串URL路徑轉換為活動函式。字串路徑可能包含 single-spa 匹配任何字元的路由引數。假定提供的字串是字首。
當在註冊應用的時候給 ativeWhen 傳遞一個字串時會被呼叫
Arguments
Path: string url 的字首
returns: (location: location) => boolean
Examples

let activeWhen = singleSpa.pathToActiveWhen('/settings');
activewhen(new URL('http://localhost/settings')); // true
activewhen(new URL('http://localhost/settings/password')); // true
activeWhen(new URL('http://localhost/')); // false

activeWhen = singleSpa.pathToActiveWhen('/users/:id/settings');
activewhen(new URL('http://localhost/users/6f7dsdf8g9df8g9dfg/settings')); // true
activewhen(new URL('http://localhost/users/1324/settings')); // true
activewhen(new URL('http://localhost/users/1324/settings/password')); // true
activewhen(new URL('http://localhost/users/1/settings')); // true
activewhen(new URL('http://localhost/users/1')); // false
activewhen(new URL('http://localhost/settings')); // false
activeWhen(new URL('http://localhost/')); // false

activeWhen = singleSpa.pathToActiveWhen('/page#/hash');
activeWhen(new URL('http://localhost/page#/hash')); // true
activeWhen(new URL('http://localhost/#/hash')); // false
activeWhen(new URL('http://localhost/page')); // false

ensureJQuerySupport

用於保障不同版本的 jQuery 的 event delegation

Arguments
jQuery? : JQueryFn = window.jQuery 對jQuery已繫結到的全域性變數的引用

setBootstrapMaxTime

設定全域性 bootstrap 的超時時間

// After three seconds, show a console warning while continuing to wait.
singleSpa.setBootstrapMaxTime(3000);

// After three seconds, move the application to SKIP_BECAUSE_BROKEN status.
singleSpa.setBootstrapMaxTime(3000, true);

// don't do a console warning for slow lifecycles until 10 seconds have elapsed
singleSpa.setBootstrapMaxTime(3000, true, 10000);

Arguments
Mills: number 超時之前等待 bootstrap 完成的毫秒數。
dieOnTimeout: boolean = false 如果為false,則已註冊的應用程式會使速度降低,直到達到 mills 之前,只會在控制檯中引起一些警告。如果為true,則使已註冊應用程式將被置為 SKIP_BECAUSE_BROKEN 狀態,在此狀態下,它們將再也沒有機會改變內容。
warningMillis: number = 1000 在最終超時之前發生的控制檯警告之間等待的毫秒數。

setMountMaxTime

設定全域性 mount 的超時時間

// After three seconds, show a console warning while continuing to wait.
singleSpa.setMountMaxTime(3000);

// After three seconds, move the application to SKIP_BECAUSE_BROKEN status.
singleSpa.setMountMaxTime(3000, true);

// don't do a console warning for slow lifecycles until 10 seconds have elapsed
singleSpa.setMountMaxTime(3000, true, 10000);

Arguments
同上

setUnmountMaxTime

設定全域性 unmount 的超時時間

// After three seconds, show a console warning while continuing to wait.
singleSpa.setUnmountMaxTime(3000);

// After three seconds, move the application to SKIP_BECAUSE_BROKEN status.
singleSpa.setUnmountMaxTime(3000, true);

// don't do a console warning for slow lifecycles until 10 seconds have elapsed
singleSpa.setUnmountMaxTime(3000, true, 10000);

Arguments
同上

setUnloadMaxTime

設定 unload 的超時時間

// After three seconds, show a console warning while continuing to wait.
singleSpa.setUnloadMaxTime(3000);

// After three seconds, move the application to SKIP_BECAUSE_BROKEN status.
singleSpa.setUnloadMaxTime(3000, true);

// don't do a console warning for slow lifecycles until 10 seconds have elapsed
singleSpa.setUnloadMaxTime(3000, true, 10000);

Arguments
同上

Event

single-spa 將事件觸發到視窗,作為您的程式碼掛接到URL過渡的一種方式

PopStateEvent

當 single-spa 想要所有啟用的應用重新渲染時,通過該事件觸發。當一個應用程式呼叫history.pushState,history.replaceState或triggerAppChange時,就會發生這種情況。

window.addEventListener('popstate', evt => {
    if (evt.singleSpa) {
        console.log('This event was fired by single-spa to forcibly trigger a re-render')
        console.log(evt.singleSpaTrigger); // pushState | replaceState
    } else {
        console.log('This event was fired by native browser behavior')
    }
});

Canceling navigation

取消導航是指更改URL,然後立即將其更改回之前的狀態。這個發生在 mounting 、unmounting 或者 loading 之前完成。可以與Vue路由器和Angular路由器的內建導航保護功能結合使用,這些功能可以取消導航事件。

// 監聽 single-spa:before-routing-event 事件
window.addEventListener('single-spa:before-routing-event', ({detail: {oldUrl, newUrl, cancelNavigation}}) => {
    if (new URL(oldUrl).pathname === '/route1' && new URL(newUrl).pathname === '/route2') {
        cancelNavigation();
    }
})

Custom Events

每當重新路由時,single-spa都會觸發一系列自定義事件。每當瀏覽器URL發生任何更改或呼叫triggerAppChange時,都會發生重新路由

window.addEventListener('single-spa:before-routing-event', evt => {
    const { originalEvent, newAppStatuses, appsByNewStatus, totalAppChanges, oldUrl, newUrl, navigationIsCanceled, cancelNavigation } = evt.detail;
    console.log('original event that triggered this single-spa event', originalEvent); // PopStateEvent | HashChangeEvent | undefined
    console.log('the new status for all applications after the reroute finishes', newAppStatuses) // { app1: MOUNTED, app2: NOT_MOUNTED }
    console.log('the applications that changed, grouped by their status', appsByNewStatus) // { MOUNTED: ['app1'], NOT_MOUNTED: ['app2'] }
    console.log('number of applications that changed status so far during this reroute', totalAppChanges); // 2
    console.log('the URL before the navigationEvent', oldUrl); // http://localhost:8080/old-route
    console.log('the URL after the navigationEvent', newUrl); // http://localhost:8080/new-route
    console.log('has the navigation been canceled', navigationIsCanceled); // false

    // The cancelNavigation function is only defined in the before-routing-event
    evt.detail.cancelNavigation();
})

before-app-change event

當至少一個應用程式更改狀態,在重新路由之前會觸發 single-spa:before-app-change事件。

window.addEventListener('single-spa:before-app-change', (evt) => {
    console.log('single-spa is about to mount/unmount applications!');
    console.log(evt.detail.originalEvent) // PopStateEvent
    console.log(evt.detail.newAppStatuses) // { app1: MOUNTED }
    console.log(evt.detail.appsByNewStatus) // { MOUNTED: ['app1'], NOT_MOUNTED: [] }
    console.log(evt.detail.totalAppChanges) // 1
});

before-no-app-change

當零個應用程式更改狀態,在重新路由之前會觸發 single-spa:before-no-app-change事件。

window.addEventListener('single-spa:before-no-app-change', (evt) => {
    console.log('single-spa is about to do a no-op reroute');
    console.log(evt.detail.originalEvent) // PopStateEvent
    console.log(evt.detail.newAppStatuses) // { }
    console.log(evt.detail.appsByNewStatus) // { MOUNTED: [], NOT_MOUNTED: [] }
    console.log(evt.detail.totalAppChanges) // 0
});

before-routing-event

在每個路由事件發生之前(即在每個hashchange,popstate或triggerAppChange之後)都會觸發 single-spa:before-routing-event 事件,即使不需要更改已註冊的應用程式也是如此。

window.addEventListener('single-spa:before-routing-event', (evt) => {
    console.log('single-spa is about to mount/unmount applications!');
    console.log(evt.detail.originalEvent) // PopStateEvent
    console.log(evt.detail.newAppStatuses) // { }
    console.log(evt.detail.appsByNewStatus) // { MOUNTED: [], NOT_MOUNTED: [] }
    console.log(evt.detail.totalAppChanges) // 0
});

before-mount-routing-event

在 routing-event 之前,在 before-routing-event 之後,保證在所有應用 unmounted 之後,所有新應用 mounted 之前觸發

window.addEventListener('single-spa:before-mount-routing-event', (evt) => {
    console.log('single-spa is about to mount/unmount applications!');
    console.log(evt.detail)
    console.log(evt.detail.originalEvent) // PopStateEvent
    console.log(evt.detail.newAppStatuses) // { app1: MOUNTED }
    console.log(evt.detail.appsByNewStatus) // { MOUNTED: ['app1'], NOT_MOUNTED: [] }
    console.log(evt.detail.totalAppChanges) // 1
});

before-first-mount

在任何一個應用第一次掛載之前觸發,因此只觸發一次,這在應用已經載入好但是還沒 mounting 之後。

window.addEventListener('single-spa:before-first-mount', () => {
    console.log('single-spa is about to mount the very first application for the first time');
});

first-mount

在任何一個應用第一次掛載之前觸發,因此只觸發一次

window.addEventListener('single-spa:first-mount', () => {
    console.log('single-spa just mounted the very first application');
});

app-change event

至少一個應用 loaded、bootstrap、mounted、unmounted 或者 unloaded 觸發,hashchange,popstate 或triggerAppChange不會導致觸發事件。

window.addEventListener('single-spa:app-change', (evt) => {
    console.log('A routing event occurred where at least one application was mounted/unmounted');
    console.log(evt.detail.originalEvent) // PopStateEvent
    console.log(evt.detail.newAppStatuses) // { app1: MOUNTED, app2: NOT_MOUNTED }
    console.log(evt.detail.appsByNewStatus) // { MOUNTED: ['app1'], NOT_MOUNTED: ['app2'] }
    console.log(evt.detail.totalAppChanges) // 2
});

no-app-change event

當沒有應用 loaded、boostrapped 、mounted、unmounted 或者 unloaded 時觸發。每個路由事件只會觸發一個。

window.addEventListener('single-spa:no-app-change', (evt) => {
    console.log('A routing event occurred where zero applications were mounted/unmounted');
    console.log(evt.detail.originalEvent) // PopStateEvent
    console.log(evt.detail.newAppStatuses) // { }
    console.log(evt.detail.appsByNewStatus) // { MOUNTED: [], NOT_MOUNTED: [] }
    console.log(evt.detail.totalAppChanges) // 0
});

routing-event

每次發生路由事件時(在每個hashchange,popstate或triggerAppChange之後),都​​會觸發 single-spa:routing-event事件,即使無需更改已註冊的應用程式;之後 single-spa 會驗證所有應用正確 loaded、bootstrapped、mounted、unmounted

window.addEventListener('single-spa:routing-event', (evt) => {
    console.log('single-spa finished mounting/unmounting applications!');
    console.log(evt.detail.originalEvent) // PopStateEvent
    console.log(evt.detail.newAppStatuses) // { app1: MOUNTED, app2: NOT_MOUNTED }
    console.log(evt.detail.appsByNewStatus) // { MOUNTED: ['app1'], NOT_MOUNTED: ['app2'] }
    console.log(evt.detail.totalAppChanges) // 2
});

相關文章