基于Umi探索微前端以及其融合方案

写在前面

“微前端”这个概念已经在前端圈火了很长一段时间了,关于什么是微前端,微前端干了什么,其和传统iframe嵌套有啥区别等等一系列疑问,社区的介绍也不少,为了保证各位同学读到本文干货时还保持着精力,这里对于以上概念将不做详细赘述,当然,为了照顾没有了解过微前端的同学,还是要做一个简单的描述。做完概念描述后,我会用demo的方式,从基础微前端到微前端的融合方案(重点!)做一个详细讲解。

概念简答

什么是微前端

微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。

也就是说,一个“大项目”由一个个独立的小项目拼起来的,或者说把一个“大项目”拆成了若干的小项目,独立开发。并且其与技术栈无关,说到这里,很多人就会想到iframe,确实,通过iframe可以满足上面我们所说的一些功能特点,但其实,我们使用的微前端是一个叫qiankun的库,当然了,也有人说iframe是最早的微前端实现,那我们姑且先这么认为吧。实际上,这种说法也是有一些道理的,那么为什么我们不使用iframe呢?
这里有一篇文章,有兴趣的可以看一下。大概意思就是:

  • url 不同步。浏览器刷新 iframe url 状态丢失、后退前进按钮无法使用。
  • UI 不同步,DOM 结构不共享。
  • 全局上下文完全隔离,内存变量不共享。
  • 慢。每次子应用进入都是一次浏览器上下文重建、资源重新加载的过程。

关注过qiankun的同学,应该看得出来,上面的知识点,都搬运自官网,如果你已经很熟悉了,那就可以直接跳过这段了~

什么场景下适合使用微前端

关于这样的概念和理解,我想社区中已经数不胜数了,那么按照我的理解就是:“当你遇到了超大型巨无霸中后台系统需要优化拆分时、当你遇到了一个需要多个团队维护各自模块堆积起来的项目时”,那么请让微前端带你来爽一把吧。

OK~概念性的废话就说到这,下面我将手摸手带大家搭建一个微前端项目,并对其融合方案做研究。

搭建基础微前端环境

环境准备

为简单起见,我们直接使用umi的qiankun插件plugin-qiankun,所以,你的项目中,只需要能跑起来umi即可。
我们知道,一个微前端,是由几个小的模块组成的一个大的系统,一般的,我们会把这几个小的模块用一个负责layout的应用包裹,我们管这个“盒子”叫主应用,管下面一个一个的小模块叫子应用
下面,我们就使用umi作为脚手架,一个一个大分别搭建三个项目呢,其中一个为“主应用”,另外两个为“主应用”

搭建项目
yarn create @umijs/umi-app

谈笑间,三个项目已经搭建完毕
其中主项目采用ant design pro模板,依赖为umi3+antd4
两个子项目采用umi app 模板,依赖为umi3 + antd4
为了快速搭建环境,按个应用我使用了相同的技术栈,因为umi3在集成微前端上非常方便,当然了,大多数情况下,微前端的子应用之间都是采用的不同的技术栈,那时候可能要大家去踩踩坑了,现在社区关于微前端的知识还是比较丰富的,相比解决问题比较容易,当然了,热心的我,也为大家提供了一些踩坑的资料

  • 如果你使用的是umi2 + qiankun,那么请看这里
  • 另外你可能会踩到这些坑
  • 在其他技术栈中,你必须注意你的入口文件生命周期函数的导出和webpack出口的相关配置。

当然了,如果你使用的是umi3,那么你可能不会踩到上面的坑,直接创建项目吧

我们使用umi3创建的项目如下:

  • app-main(主应用,启动端口8000)
  • app-one(子应用1,启动端口7001)
  • app-two(子应用2,启动端口9002)

项目搭建好了,那就简单写点业务吧,下面是我们假设的一个需求如下,在某租房公司业务上,有一个后台。现在“房源模块”和“合同模块”分别由两个团队维护,于是,我们的技术方案如下:
主应用提供容器,两个子应用一个负责房源信息,一个负责合同信息。
经过开发的不懈努力,我们的各自模块如下。



虽然页面略显简陋,但有几个重点很突出

  • 每一个项目都可以单独启动
  • 各自有各自的启动端口
创建主应用,将子应用装载进去

既然主应用是一个盒子,要装载两个或者多个子应用,那么配置主应用将比较重要,这里我们不再赘述详细过程,大家可以参照官网做更详细的了解。这里我为了布局方便,使用的是ant design pro的模板,当然,你可以使用任何你想使用的layout,但是如果你使用的是umi,那么请安装插件plugin-qiankun
这里,我们也是参照参照官网,对主应用做了配置。

  1. 注册子应用
  qiankun: {
    master: {
      apps: [
        {
          name: 'app-one', // 唯一 id
          entry: '//localhost:7001',
        },
        {
          name: 'app-two', // 唯一 id
          entry: '//localhost:9001', // html entry
        },
      ],
    },
  },
  1. 装载子应用

我们采用的是组件方式注册的子组件,拿‘app-one’为例

import { MicroApp } from 'umi';

const MyPage = () => {
  return (
    <div>
      <div>
       <MicroApp
        name="app-one"
        autoSetLoading
        className="myContainer"
        wrapperClassName="myWrapper"
      />
      </div>
    </div>
  )
}
export default MyPage

实际上,在你注册子应用,装载子应用的这个过程中,和我们react组件的路由注册和组件创建非常的相似,大家可以类比着去理解。
之所以用组件式,是因为这样,我们就可以用ant design pro的路由和菜单耦合的功能,如果你使用的其他layout做主应用,那么你可以使用组件形式,也可以使用路由形式,这里官网描述的都很清楚,就不一一赘述了。我使用的是组件形式,那么我必须在路由中注册这个页面组件

{
  path: 'app-one',
  name: 'houseManage',
  component: './appOne'
},
{
  path: 'app-two',
  name: 'hetongManage',
  component: './appTwo',
  exact: false, // 需要设置为fasle,这样才能匹配到子应用的下的子路由
},
  1. 子应用配置

一个微前端子应用,是需要做相应的配置的,不然不会自然的集成进主应用中。
你大概需要注意的是

  • package.json中必须有一个name的字段,来保证子应用的唯一性
  • 如果你使用的是umi3,那么你需要这样开启即可
  qiankun: {
    slave: {},
  },

当然了,在这之前,你需要安装qiankun插件,才能在umi中配置这一项

yarn add @umijs/plugin-qiankun -D
  • 如果你使用的技术栈(上文说过了,这再赘述一下),那么你还另外需要注意其入口文件的生命周期函数处理和webpack关于output的配置,那你就必须要按照文档的步骤来集成了。
启动

经过上面的一系列操作,我们的主应用已经可以访问装载的子应用了。


小结

上面的一些列操作,都是微前端中最基础的用法,如果你耐心看文档,多踩踩坑,基本毫无压力的就搭建出了上面的环境,当然了,如果你已经对搭建一个微前端环境很熟悉,你就会觉得,我上面说了一顿“废话”,然后你就可以自动的跳过了。下面我们来探究,子应用之间的融合

子应用之间的融合

通过上面的demo,我们已经了解到了一些初步的概念,按照我们上面demo的实现,就是说一个url(路由)对应一个子应用,然后这样,我们就可以把一个大的应用拆分成不同的小块。

路由与应用绑定的方式简单直观,是微前端中最为常见的使用场景,通常我们会用这种方式将一堆独立域名访问的 MPA 应用,整合成一个一体化的 SPA 应用。

下面我们来思考一个问题:
我们能不能在一个Url下挂载多个子应用?
如果按照上面我们demo中的样子,那必然是不行的,因为url的唯一性,我们只能在一个url下访问一个唯一确定的资源,但是我们能不能解决这个问题呢?

路由模式(小插曲)

因为我们的主应用是Spa,那么Url也就对应了路由,下面做个小插曲,来探索一下,不同模式的路由,对于url的影响是什么,看看有没有什么空子可以让我们钻一下,从而解决上面我们提出的问题。
众所周知,Spa中的路由模式有两种:browserhash。其中各自的原理简单描述如下

  • 对于hash: 通过监听Url的location.hash,触发onhashchange然后拿对应的前端资源,这里会依赖Url
  • 对于browser:使用HTML5的history对象中的pushState()replaceState()这两个api来实现的跳转,加载对应的资源。这里也会依赖Url变化。

那么,我们有没有一种方式,可以摆脱Url的控制来加载对应的前端资源呢?如果可以,那么我们是不是就可以在同一个Url下加载多个微前端的子应用了呢?
实际上,路由还有另外一个不为人知的类型,那就是memory类型,这里我对memory路由类型做一个简单的描述。

  • memory类型:

不会把地址存放在URL上,而是将地址存放在本地或者数据库中,在使用时获取本地或者数据库中的地址,然后匹配相对应的资源。

看上面的描述可能有点懵逼,看一下代码:

//例如
//获取保存在localStorage中的地址
const path = window.localStorage.getItem('path');

//当用户在某个页面就重新设置localStorage中的地址
//userHref是用户所在的页面
window.localStorage.setItem('path', userHref);

也就是说,我们可以把页面存在本地或者其他地方,而脱离Url唯一性的束缚。因为,这个路由模式比较适用于“非浏览器”的场景,故这个路由模式也变得鲜为人知,但是他却能很好的解决我们上面提出的问题。
当然,也就是官网中说的

除了导航应用之外,App1 与 App2 均依赖浏览器 url,为了让 App1 嵌套 App2,两个应用同时存在,我们需要在运行时将 App2 的路由改为 memory 类型。

做一个子应用嵌套的demo

umi为我们提供了MicroAppWithMemoHistory组件,我们可以直接使用,这个组件所引用的页面或者应用,使用的就是memory模式 。现在我们根据一个假设的需求,搭建一个嵌套的demo。
“在现有的项目中,我们在房源信息页面中访问合同列表和合同详情”。
于是我们就可以这样去改造我们的项目了。
在app-one“房源信息”应用中,我们需要需要这样修改他的配置项

  qiankun: {
    slave: {},
    master: {
      // 注册子应用信息
      apps: [
        {
          name: 'app-two', // 唯一 id
          entry: '//localhost:9001', // html entry
        },
      ],
    },
  },

这样,就像我们在主应用中注册子应用一样,我们在子应用之间也是可以这样注册的,不同的是,我们在装载的时候,就不能想在主应用中装载那样了,这时候就是我们用MicroAppWithMemoHistory的时候。我们这样改造了我们的"房源信息"列表

...
    <>
      <h1>房源信息</h1>
      <Button onClick={btnClick} type="primary">查看合同列表</Button>
      <Table columns={columns} dataSource={data} />
      <Modal
        title="合同列表"
        visible={visible}
        width="100%"
        onOk={() => setVisible(false)}
      >
// 我们可以在url出书写子应用中的路由,而对应加载模块
        <MicroAppWithMemoHistory name="app-two" url="/"/>
      </Modal>
    </>

通过这样的操作后,我们就可以在/app-one/这一个Url下同时加载两个或者多个子应用了。


当然,我们也可以不让其出现在弹窗中,设想,我们有一个中台的分析页面,这个页面是由多个项目组成的,那么也就可以使用上面的场景了,我们还用我们的demo为例


可以看到,我们只需要简单的代码,就可以实现子应用之间的嵌套了

写在最后

关于qiankun微前端的搭建,社区的知识已经相当泛滥,实际上,我们面向官网就可以解决大部分搭建过程中遇到的坑。这篇文章的目的在于,介绍微应用之间的融合,让大家认识到这种开发模式。同时,知道鲜为人知的‘memory’路由模式。当然了,微应用之间也是可以像react组件那样做通信的,这也是比较基础的知识了,我想社区说的比我更完善吧。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,302评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,232评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,337评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,977评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,920评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,194评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,638评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,319评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,455评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,379评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,426评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,106评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,696评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,786评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,996评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,467评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,043评论 2 341

推荐阅读更多精彩内容

  • 夜莺2517阅读 127,706评论 1 9
  • 我是黑夜里大雨纷飞的人啊 1 “又到一年六月,有人笑有人哭,有人欢乐有人忧愁,有人惊喜有人失落,有的觉得收获满满有...
    陌忘宇阅读 8,519评论 28 53
  • 兔子虽然是枚小硕 但学校的硕士四人寝不够 就被分到了博士楼里 两人一间 在学校的最西边 靠山 兔子的室友身体不好 ...
    待业的兔子阅读 2,582评论 2 9
  • 信任包括信任自己和信任他人 很多时候,很多事情,失败、遗憾、错过,源于不自信,不信任他人 觉得自己做不成,别人做不...
    吴氵晃阅读 6,173评论 4 8