微前端框架single-spa初探

前言

最近入职的一家公司接纳single-spa这个微前端框架,以是自学了此框架。

single-spa这个微前端框架虽然有中文文档,然则有些零星和艰涩。

以是我想在学习之余,写篇博客拉平一下这个学习曲线。

什么是微前端?

微前端的灵感泉源于服务端微服务的理念。

可以简朴明白为,在开发一个庞大前端应用时,将其划分为一系列更小更简朴的前端应用。

这些前端应用可以单独开发、测试、部署,松耦合,可维护性强,还可以让前端代码实现增量升级和使用差其余框架。

它的懒加载还能让整个庞大应用加载速率变快。

常用微前端玩法和single-spa

在我之前的公司是使用iframe来实现微前端的,然则各个子应用间的通讯往往对照穷苦,而且很不天真。

而最新的微前端理念,是由webpack5中模块团结特征实现的,这里就不多讲了。

single-spa是一个对照盛行的微前端框架,它并不是使用iframe来实现微前端,也不是通过模块团结,而是通过路由路径来在dom上加载差其余子应用。

Import maps和SystemJS

在详细解说single-spa前,我们得先领会一个器械:Import maps。

这个功效是Chrome 89才支持的。

顾名思义,它是对import的一个映射处置,让你控制在js中使用import时,到底从哪个url获取这些库。

好比通常我们会在js中,以下面这种方式引入模块:

    import moment from "moment"

正常情形下一定是node_modules中引入,然则现在我们在html中加入下面的代码:

    <script type="importmap">
    {
        "imports": {
            "moment": "/moment/src/moment.js"
        }
    }
    </script>

这里/moment/src/moment.js这个地址换成一个cdn资源也是可以的。最终到达的效果就是:

    import moment from "/moment/src/moment.js"

有了Import maps,import的语法就可以直接在浏览器中使用,而不再需要webpack来帮我们举行处置,不需要从node_modules中去加载库。

Import maps甚至另有一个兜底的玩法:

    "imports": {
        "jquery": [
            "https://某CDN/jquery.min.js",
            "/node_modules/jquery/dist/jquery.js"
        ]
    }

当cdn无效时,再从内陆库中获取内容。

它的功效另有许多,我就不逐一枚举了,只需要对这个有一定的领会即可。

只管Import maps异常壮大,然则事实浏览器兼容性还并不是很好,以是就有了我们的polifill方案:SystemJS。

SystemJS同样是一个模块加载器,可兼容到IE11,同样支持import映射,然则它的语法稍有差异:

    <script src="system.js"></script>
    <script type="systemjs-importmap">
    {
        "imports": {
            "lodash": "https://unpkg.com/[email protected]/lodash.js"
        }
    }
    </script>

在浏览器中引入system.js后,会去剖析type为systemjs-importmap的script下的import映射。

它和Import maps最终到达的效果是一致的。

single-spa

之以是我们要先讲Import mapsSystemJS,就是由于single-spa的微前端往往需要连系SystemJS来实现。

single-spa框架中,基座会检测浏览器url的转变,在转变时往往通过SystemJS的import映射,来加载差其余子应用js。

然则需要注重,single-spa并不是必须依赖SystemJS

刚刚我们提到了一个看法:基座,现在讲下single-spa的两个看法:基座应用

你可以简朴明白应用是一个个的单页面应用,而基座是一个应用治理器,用来凭证路由加载差其余应用

一样平常在基座中我们需要像下面这样注册一个应用:

import { registerApplication, start } from 'single-spa';

// 注册应用1
registerApplication({
    name:'app1',
    app:() => import('./app1.js'),
    activeWhen: '/app1',
    customProps: { myTitle: "转达给应用的自界说参数的值" }
});

// 注册应用2
registerApplication({
    name:'app2',
    app:() => import('./app2.js'),
    activeWhen: '/app2'
});

start();

在上面的代码中,我们注册了app1和app2两个应用,划分匹配路由/app1和/app2。

也就是说当路由是/app1或者/app1/home时,会直接加载app1这个应用。

注册应用后,需要start()来最先挂载应用,否则只会下载应用,而不会挂载应用。

那么应用应该若何设置呢?

我们上面代码引用的./app1.js并没有导出一个真的单页面应用,而一样平常是如下:

console.info('第一步 下载应用阶段')
export function bootstrap(props) {
    const {
        name,        // 应用名称
        singleSpa,   // singleSpa实例
        mountParcel, // 手动挂载的函数
        myTitle  // 我们之前在注册应用时转达给customProps的属性
    } = props;     // Props 会传给每个生命周期函数

    // 这个生命周期函数会在应用第一次挂载前执行一次。
    return Promise.resolve().then(() => {
        console.info('第二步 初始化', myTitle)
    })
}
export function mount(props) {
    // 每当应用路由匹配乐成,但该应用处于未挂载状态时,挂载的生命周期函数就会被挪用。挪用时,函数会凭证URL来确定当前被激活的路由,确立DOM元素、监听DOM事宜等以向用户出现渲染的内容。任何子路由的改变(如hashchange或popstate等)不会再次触发mount,需要各应用自行处置。
    return Promise.resolve().then(() => {
        console.info('第三步 挂载应用', props.name)
        document.getElementById('root').innerHTML = "我是app1啊"
    })
}

export function unmount(props) {
    // 每当应用路由匹配不乐成,但该应用已挂载时,卸载的生命周期函数就会被挪用。卸载函数被挪用时,会整理在挂载应用时被确立的DOM元素、事宜监听、内存、全局变量和新闻订阅等。
    return Promise.resolve().then(() => {
        console.info('第四步 卸载应用', props.name)
        document.getElementById('root').innerHTML = ""
    })
}

export function unload(props) {
    // 移除”生命周期函数的实现是可选的,它只有在unloadApplication被挪用时才会触发。若是一个已注册的应用没有实现这个生命周期函数,则假设这个应用无需被移除。
    // 移除的目的是各应用在移除之前执行部门逻辑,一旦应用被移除,它的状态将会酿成NOT_LOADED,下次激活时会被重新初始化。
    // 移除函数的设计念头是对所有注册的应用实现“热下载”,不外在其他场景中也异常有用,好比想要重新初始化一个应用,且在重新初始化之前执行一些逻辑操作时。
    return Promise.resolve().then(() => {
        console.info('第五步 移除应用', props.name)
    })
}

可以看到我们的app1.js这个应用中的代码,并没有导出一个单页面应用组件,而是导出了几个生命周期函数,然后通过这几个生命周期函数来控制组件的初始化,加载和卸载。

它这个操作是从现在我们的react或者vue这些框架的组件生命周期中获得灵感,将生命周期应用于整个应用程序。

国足世界杯预选赛赛程2022www.9cx.net)实时更新比分国足世界杯预选赛赛程2022数据,国足世界杯预选赛赛程2022全程高清免费不卡顿,100%原生直播,国足世界杯预选赛赛程2022这里都有。给你一个完美的观赛体验。

single-spa 与 SystemJS实现微前端

看了上面的代码之后你可能有点疑惑,你这个器械也没什么用嘛,不就是个懒加载吗?

哪来的微前端?

我用个React.lazy(() => import('./app1.js'))来个懒加载怎么了,你不要说一大堆把我绕晕了。

上面这些现实上还真的没有实现微前端,然则,你可以连系我们之前讲的微前端,想象一下:

若是./app1.js不是基座这个项目内的代码,而是另一个项目呢?

我们将这个app1.js放在一个单独的项目中,它用react来写了一个单页面应用。

再将app2.js放在另一个单独的项目中,它用vue来写了一个单页面应用。

通过我们现在的这个基座项目再来处置这两个应用呢?

我们的基座项目是不是就可以写成下面这样:

import { registerApplication, start } from 'single-spa';

// 注册应用1
registerApplication({
    name:'app1',
    app:() => import('@晓组织/app1'),
    activeWhen: '/app1',
    customProps: { myTitle: "转达给应用的自界说参数的值" }
});

// 注册应用2
registerApplication({
    name:'app2',
    app:() => import('@晓组织/app2'),
    activeWhen: '/app2'
});

start();

然后再在基座项目的模板页中来个引入映射:

<script type="systemjs-importmap">
    {
        "imports": {
            "@晓组织/app1": "//某网站/app1.js",
            "@晓组织/app2": "//另一个网站/app2.js"
        }
    }
</script>

以后我们要做app1模块的部门,只需要在app1这个项目中维护就可以了,不会滋扰到其他的应用。

以后React20,React30出来,或者部门项目升级webpack,或者给一个项目大调整,我们可以一个个小应用实验升级修改,不用所有项目同时调整。不仅风险变小了许多,也加倍可控。

上面是连系SystemJS实现的微前端,实在另有使用npm包和单项目的玩法,然则不推荐,有兴趣的可以参考官网的这篇文章:拆分应用。

single-spa-react

看到这里的同伙一定会想,这个器械好是好,怎么把它和react的单页面应用连系起来?

让我自己写加载和卸载react的单页面应用?这也太挫了吧。

固然不能能,single-spa的生态可是很好的。

single-spa-react就是一个辅助库,它可以辅助React应用程序实现single-spa 需要的生命周期函数(bootstrap、mount 和 unmount)。

import React from 'react';
import ReactDOM from 'react-dom';
import rootComponent from './path-to-root-component.js';

import singleSpaReact from 'single-spa-react';
const reactLifecycles = singleSpaReact({
    React,
    ReactDOM,
    rootComponent,
    errorBoundary(err, info, props) {
        // https://reactjs.org/docs/error-boundaries.html
        return (
            <div>This renders when a catastrophic error occurs</div>
        );
    },
});
export const bootstrap = reactLifecycles.bootstrap;
export const mount = reactLifecycles.mount;
export const unmount = reactLifecycles.unmount;

这是官网提供的示例代码,谁人rootComponent就是我们的顶层React组件。

其它的就不用多说了,事实都很容易明白,想要领会详情可以看下:详情。

其它的一些语言好比vue和Angular都有自己对应的辅助库,详细可以查阅:辅助库列表

single-spa的Parcels

Parcels是single-spa的一个高级特征,是一个与框架无关的组件,与应用的差异之处在于parcel组件需要手动挂载,而不是通过匹配路由的activity方式被激活。

官网说:只有在涉及到跨框架的应用之间举行组件挪用时,我们才需要思量parcel的使用。

一想到我们公司只用react,那么打扰了,再见。

CLI工具:create-single-spa和可定制化的webpack设置

上面许多都是在讲原理,现在到了现实应用的时刻了。

single-spa的相关设置有些繁琐,以是我推荐依赖现有的CLI去新建项目,可以去刷新,而不是自己从零最先去搭建。

npx create-single-spa

运行上面这行下令后,就会让你做一系列选择,那些选择就不多说,只说最要害的。

create-single-spa 可以让你确立三种项目,划分是:

  • single-spa application/parcel :应用和parcel。
  • single-spa root config:基座。
  • in-browser utility module: 通用组件,工具函数,样式指引。

你可以凭证需要去确立差其余项目。

single-spa还提供了一些推荐的webpack设置库,不用自己费心去设置webpack设置。

不外我建议最后输出获得webpack设置你照样稍微打印出来看一下,做到胸有定见,然后才可以再凭证它的设置去做响应修改。

Demo分享

光看不做假把式,下面是我自己凭证single-spa的CLI工具搭建的两个浅易Demo:

  • 基座的代码客栈:https://gitee.com/vvjiang/single-spa-root-config-demo
  • 应用的代码客栈:https://gitee.com/vvjiang/single-spa-app-demo

同时运行起来即可,下令都是:

yarn start

若是确实想入门的话,对比一下我写的和官方CLI工具初始化时的一些差异,可以领会到更多的一些小细节。

总结

总的来说,single-spa是一个异常优异的微前端框架。

微前端领域最近的趋势是用webpack5中模块团结特征来实现,这与single-spa并不冲突,single-spa也有连系模块团结特征实现的例子。

不外这就不在本篇文章的涉及局限内了,也许以后会写下这块的内容。

本篇博客到此竣事。

希望这篇文章能给您带来一些辅助,若有疏漏,也请不惜见教。

欧博手机版下载

欢迎进入欧博手机版下载(www.aLLbetgame.us),欧博官网是欧博集团的官方网站。欧博官网开放Allbet注册、Allbe代理、Allbet电脑客户端、Allbet手机版下载等业务。

  • 评论列表:

添加回复:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。