micro-app 手把手实践

1,078 阅读3分钟

那天,我正在奋笔疾书的写代码,看着那四六不通的代码在我手里逐渐光芒四射,不知不觉嘴角扬起了一丝的微笑。 突然,电脑右上角飘来一条钉钉消息: 来我工位一趟!这时大领导的消息,我内心有点忐忑,我战战兢兢的把鼠标移到钉钉上,然后又迅速移开。

(内心独白:)”如果贸然点击,消息由未读成了已读,那就得马上放下手里的鱼,这样会不会显得我工作不饱和啊。不行,先等等再说!“

这时 “钉。。!"的一声,刺耳的提示音像箭一样射进了我的耳朵。于是点开消息,迅速回复道:好的,马上到!

于是,便有了我对6年老项目在微前端改造上摩擦,踩坑的经历!

项目背景

老项目是用vue + iview2写的,由于时间长,iview2早已不维护,加之又是我们公司初创时做的第一个后台系统,功能多,代码糙,有的页面多达4000多行代码,维护成本十分之高,编译项目十分之慢,病入膏肓,所以微前端改造势在必行。

这次改造,代号称之为 - 屎壳郎计划(搬运s山)

关于micro-app

在做微前端框架选型的时候,也做了一番考虑,研究了竞品qiankun,但是qiankun复杂的配置让我有些望而却步。自己也亲身搭建了一套qiankun的demo, 从上手的难易程度来讲,micro-app真的很合适。配置简单、文档细致,虽然目前1.0版本还没有发布,但是已经足以满足了我们这个老项目对于微前端的改造。总之,任何框架都有亮点,也有不足,选择适合自己的就是最好的!

主、子框架选型

主应用: vue + iview2 + vite + microApp hash路由

子应用: vue + ivew2 + webapck hash路由

这里需要解释一下为什么子应用仍然采用了iview2的UI框架?因为页面太多了,如果换了UI框架,样式全部都要改,这个时间太长,耗不起。为了做到子应用和主应用的样式无缝兼容,只好再度采用iview2!至于子应用为什么不用vite 而是用webpack ,是因为对于micro-app来说,适配vite成本是巨大的,需要关闭沙箱功能,这对我们来说显然有点得不偿失。

最近也在研究micro-app的原理,后续也会对为什么不能用vite 做一次分析

主应用

 安装依赖

npm i @micro-zoe/micro-app --save

在入口处引入 main.js

import microApp from '@micro-zoe/micro-app'
microApp.start()

分配一个路由给子应用

{
        path: '/pages/page1',
        meta: {
            mgSite: 'iview2'
        },
        component: ()=>import('../pages/_microApp/index')
 }

进入_microApp/index.vue

<template>
  <div style="height: 100%; background: white">
    <Spin v-if="isShowSkeleton" fix> 加载中... </Spin>
    <micro-app
      @error="error"
      @aftershow="aftershow"
      @mounted="mounted"
      @created="created"
      :name="appName"
      url="http://localhost:8080"
      baseroute="/pages"
      :data="microAppData"
      keep-alive
      style="height: 100%"
    ></micro-app>
  </div>
</template>
<script>
import { removeDomScope } from '@micro-zoe/micro-app'
export default {
  data() {
    return {
      isShowSkeleton: true,
      appName: 'appName',
      microAppData: {
        pushState: (path) => {
          removeDomScope()
          this.$router.push(path)
        },
      },
    }
  },
  created() {
    this.initAppName()
  },
  watch: {
    $route() {
      this.initAppName()
      this.initMicroData()
    },
  },
  methods: {
    initMicroData() {
      this.isShowSkeleton = true
      this.microAppData = {
        pushState: (path) => {
          removeDomScope()
          this.$router.push(path)
        },
      }
    },
    initAppName() {
      const { hash } = location
      if (hash.split('#')[1] && this.appName !== hash.split('#')[1]) {
        this.appName = hash.split('#')[1]
      }
    },
    created() {
      this.isShowSkeleton = true
    },
    mounted() {
      this.isShowSkeleton = false
    },
    aftershow() {
      console.log('微前端页面' + this.appName)
      this.isShowSkeleton = false
    },
    error() {
      this.isShowSkeleton = false
    },
  },
}
</script>

注意: 是一个web components 组件,无需单独引入。

子应用

在vue.config.js-> devServer 中设置headers 支持跨域请求

headers: {
      'Access-Control-Allow-Origin': '*'
}

由于我的主应用和子应用均是hash路由,为了防止冲突,基座设置了基础路由给子应用。子应用可以在这个路由下完成渲染。可以看下官网的解释:micro-zoe.github.io/micro-app/d…

新建一个layout页面作为parent component, 其他页面放在该路由下的children数组中

export default {
  path: window.__MICRO_APP_BASE_ROUTE__ || '/pages',
  component: () => import('@/views/layout.vue'),
  name: 'layout',
  children: [
  // 其他的路由都写到这里
  ],
};

layout 页面内容如下:

<template>
  <div
    style="height: 100%; overflow: hidden"
    class="light-wrap"
  >
    <div
      style="height: 100%; overflow: hidden"
      class="uu-layout-content"
    >
      <router-view class="child-view primary-u light" />
    </div>
  </div>
</template>

<script>
export default {
  name: 'Layout',
};
</script>

至此,一个简单的微前端应用便已搭建完成。

遇到的问题(踩坑笔记):

1. 主应用如何控制子应用的权限?

子应用中的页面只是主应用中的一个web component组件,他们共享一个域名,在子应用获取到的缓存数据和主应用是一样的。所以像token、session的获取、权限的控制,子应用都应该和主应用框架保持一致。

2. 传递给micro-app的name如何确保唯一?

在主应用中,为了方便复用,微前端页面封装成了一个组件。根据micro框架要求,传入的name必须唯一。我这边是通过获取页面路由的方式,把路由作为name传给子应用

  1. 跳转微前端页面会出现 pushstate 为null 的报错信息

在主应用中,如果微前端页面被keep-alive组件 包裹,在进行跳转的时候会出现pushstate为null 的报错信息。解决方式是在路由中添加mgSite

meta: {
	mgSite: 'iview2'//必填
}

然后在keep - alive 中添加isMicroApp字段用来判断当前页面是不是微前端。

<keep-alive v-if="!isMicroApp">
  <router-view
  ></router-view>
</keep-alive>

 <router-view
 v-else
 ></router-view>

如果是微前端页面的话,就不要用keep-alive 包裹

这应该是micro-app 官方的一个bug。github上已经有人提了issue,预计会在1.0版本解决 issue: github.com/micro-zoe/m…

微前端页面如果需要缓存 可以这样添加。

4. 如何在子应用通过this.$router跳转主应用

micro-app针对路由跳转给了很多方法,但是如果你的微前端应用作为基建向外提供的话,就要考虑其他小伙伴的使用感受了。为了最大限度的减少小伙伴学习微前端的成本,针对子应用的路由的push方法做了一定改造

子应用:

VueRouter.prototype.push = function push(location, onResolve, onReject) {
  window.microApp.getData().pushState(location);
}

主应用则需要通过data属性传值的方式来进行跳转,具体代码请看上面的_microApp/index.vue

总结

至此,屎壳郎任务圆满完成。