⼏种微前端⽅案探究
前端 | ⼏种微前端⽅案探究.png
前⾔
随着技术的发展,前端应⽤承载的内容也⽇益复杂,基于此⽽产⽣的各种问题也应运⽽⽣,从MPA(Multi-Page Application,多页应⽤)到SPA(Single-Page Application,单页应⽤),虽然解决了切换体验的延迟问题,但也带来了⾸次加载时间长,以及⼯程爆炸增长后带来的巨⽯应⽤(Monolithic)问题;对于MPA来说,其部署简单,各应⽤之间天然硬隔离,并且具备技术栈⽆关、独⽴开发、独⽴部署等特点。要是能够将这两⽅的特点结合起来,会不会给⽤户和开发带来更好的⽤户体验?⾄此,在借鉴了微服务理念下,微前端便应运⽽⽣。
An architectural style where independently deliverable frontend applications are composed into a greater whole. [Micro Frontends from martinfowler]
根据martinfowler对微前端的定义可以看出:微前端是⼀种由独⽴交付的多个前端应⽤组成整体的架构风格,即微前端和微服务⼀样是⼀种架构风格,因⽽其并不是⼀种框架或者库,⽽是⼀种风格或者说是⼀种思想,所以为了实现微前端的⽅案就有很多种,最常见的⽅案有以下⼏种:
1. 路由分发
image
2. iframe
3. 应⽤微服务
nginx部署前端项目image 4. 微件化
image 5. 微应⽤化
image
<colgroup><col span="1"><col span="1"><col span="1"><col span="1"><col span="1"><col span="1"><col span="1"><col span="1">
| ⽅案 | 开发成本 | 维护成本 | 可⾏性 | 同⼀框架要求 | 实现难度 | 潜在风险 | 落地实践 |
| 路由分发 | 低 | 低 | ⾼ | 否 | easy | ⽆ | http服务器反向代理,如nginx配置location |
| iframe | 低 | 低 | ⾼ | 否 | easy | seo不友好、cookie管理、通信机制、弹窗问题、刷新后退、安全问题 | 前后端不分离项⽬常⽤ |
| 应⽤微服务 | ⾼ | 低 | 中 | 否 | hard | 共享及隔离粒度不统⼀ | qiankun、icestark、mooa及类single-spa应⽤ |
| 微件化 | ⾼ | 中 | 低 | 是 | hard | 实现微件管理机制 | ⽆ |
image
single-spa的整体思路是通过⽣命周期的钩⼦函数来对劫持的路由进⾏应⽤的加载,核⼼在于apps及reroute这两个⽂件
image
0, 0, 0); background-position: 0% 0%; background-repeat: repeat repeat;">export function getAppCh
anges() { const appsToUnload = [], appsToUnmount = [], appsToLoad = [], appsToMount = []; const currentTime = new Date().getTime(); apps.forEach((app) => { const appShouldBeActive = app.status !== SKIP_BECAUSE_BROKEN && shouldBeActive(app); switch (app.status) { case LOAD_ERROR: if (appShouldBeActive && currentTime - app.loadErrorTime >= 200) { appsToLoad.push(app); } break; case NOT_LOADED: case LOADING_SOURCE_CODE: if (appShouldBeActive) { appsToLoad.push(app); } break; case NOT_BOOTSTRAPPED: case NOT_MOUNTED: if (!appShouldBeActive && getAppUnloadInfo(toName(app))) {
appsToUnload.push(app); } else if (appShouldBeActive) { appsToMount.push(app); } break; case MOUNTED: if
(!appShouldBeActive) { appsToUnmount.push(app); } break; } }); return { appsToUnload, appsToUnmount, appsToLoad, appsToMount }; } export function getMountedApps() { return apps.filter(isActive).map(toName); } export function registerApplication( appNameOrConfig, appOrLoadApp, activeWhen, customProps ) { const registration = sanitizeArguments( appNameOrConfig, appOrLoadApp, activeWhen, customProps ); if (getAppNames().indexOf(registration.name) !== -1) throw Error( formatErrorMessage( 21, DEV && There is already an app registered with name ${registration.name}, registration.name ) ); apps.push(
assign( { loadErrorTime: null, status: NOT_LOADED, parcels: {}, devtools: { overlays: { options: {}, selectors: [], }, }, }, registration ) ); if (isInBrowser) { ensureJQuerySupport(); reroute(); } } export function unregisterApplication(appName) { if (apps.filter((app) => toName(app) === appName).length === 0) { throw Error( formatErrorMessage( 25, DEV && Cannot unregister application
'${appName}' because no such application has been registered, appName ) ); } return unloadApplication(appName).then(() => { const appIndex = apps.map(toName).indexOf(appName); apps.splice(appIndex, 1); }); } export function unloadApplication(appName, opts = { waitForUnmount: false }) { if (typeof appName !== "string") { throw Error( formatErrorMessage( 26, DEV && unloadApplication requires a string 'appName' ) ); } const app = find(apps, (App) => toName(App) === appName); if (!app) { throw Error( formatErrorMessage( 27, DEV && Could not unload application '${appName}' because no such application has been registered, appName ) ); } const appUnloadInfo = getAppUnloadInfo(toName(app)); if (opts && opts.waitForUnmount) { if (appUnloadInfo) { return appUnloadInfo.promise; } else { const promise = new Promise((resolve, reject) => { addAppToUnload(app, () => promise, resolve, reject); }); return promise; } } else { let resultPromise; if (appUnloadInfo) { resultPromise = appUnloadInfo.promise; immediatelyUnloadApp(app, solve, ject); } else { resultPromise = n
ew Promise((resolve, reject) => { addAppToUnload(app, () => resultPromise, resolve, reject); immediatelyUnloadApp(app, resolve, reject); }); } return resultPromise; } }</pre>
r er o ute.js
image
<pre class="cm-s-default" >export function reroute(pendingPromises = [], eventArguments) { if (appChangeUnderway) { return new Promise((resolve, reject) => { peopleWaitingOnAppChange.push({ resolve, reject, eventArguments, }); }); } const { appsToUnload, appsToUnmount, appsToLoad, appsToMount, } = getAppChanges(); let appsThatChanged, navigationIsCanceled = false, oldUrl = currentUrl, newUrl = (currentUrl =

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。