微前端之实践子应用的注册、路由拦截和获取首个子应用

一、微前端之实践子应用的注册、路由拦截和获取首个子应用
  1. 子应用的注册,如下所示:
  • 首先需要一个子应用注册的列表,在主应用 mainstore 文件夹中,新建 sub.js,对外暴露 subNavList 这个子应用的列表。subNavList 是一个数组,每一个子元素都是一个子应用,name 是子应用名称,唯一 IDentry 是子应用的入口,获取到子应用的文件;container 是子应用的渲染内容,显示在哪个容器中;activeRule 是子应用激活路由,根路由内容,激活的规则,loading 是子应用的加载状态;loading 是每一个子应用的信息,sub.js,代码如下:
import { loading } from '../store'
import * as appInfo from '../store'

export const subNavList = [
  {
    name: 'react15',
    entry: '//localhost:9002/', 
    loading,
    container: '#micro-container', 
    activeRule: '/react15', 
    appInfo,
  },
  {
    name: 'react16',
    entry: '//localhost:9003/',
    loading,
    container: '#micro-container',
    activeRule: '/react16',
    appInfo,
  },
  {
    name: 'vue2',
    entry: '//localhost:9004/',
    loading,
    container: '#micro-container',
    activeRule: '/vue2',
    appInfo,
  },
  {
    name: 'vue3',
    entry: '//localhost:9005/',
    loading,
    container: '#micro-container',
    activeRule: '/vue3',
    appInfo,
  },
];
  • 在主应用 mainstore 文件夹中,有 header.js、loading.js、nav.js、sub.jsindex.js,loading.js,代码如下:
import { ref } from 'vue';
export let loadingStatus = ref(true);
export const changeLoading = type => loadingStatus.value = type;

  • header.js,代码如下:
import { ref } from 'vue';
export const headerStatus = ref(true);
export const changeHeader = type => headerStatus.value = type;

  • nav.js,代码如下:
import { ref } from 'vue';
export const navStatus = ref(true);
export const changeNav = type => navStatus.value = type;

  • index.js,代码如下:
export * as loading from './loading';
export * as header from './header';
export * as nav from './nav';
  • 在主应用 main 中,main.js,通过 registerAppsubNavList 注册到 util 下的 index.js,微前端框架中,main.js,代码如下:
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { subNavList } from './store/sub'
import { registerApp } from './util'

registerApp(subNavList);
createApp(App).use(router()).mount('#micro_web_main_app');
  • 在主应用 main 中,util 下的 index.js,对外暴露 registerApp,通过将传入的子应用注册列表 list 注册到微前端框架里,registerMicroApps,同时也传入了主应用的生命周期,beforeLoad 开始加载,mounted 渲染完成,destoryed 卸载完成。通过调用 start 开启微前端框架,util 下的 index.js,代码如下:
import { registerMicroApps, start, createStore } from '../../micro'

import { loading } from '../store'

const store = createStore()
// const storeData = store.getStore()

window.store = store
store.subscribe((newValue, oldValue) => {
  console.log(newValue, oldValue, '---')
})
// store.update({ ...storeData, a: 1 })

export const registerApp = (list) => {
  registerMicroApps(list, {
    beforeLoad: [
      () => {
        loading.changeLoading(true)
        console.log('开始加载')
      }
    ],
    mounted: [
      () => {
        loading.changeLoading(false)
        console.log('渲染完成')
      }
    ],
    destoryed: [
      () => {
        console.log('卸载完成')
      }
    ]
  })

  start()
}

  • micro 微前端框架中,之前调用的,registerMicroAppsmicro 下的 start.js 中,它需要使用到 micro 下的 constsubApps.js,其中 list 是统一管理子应用列表,getList 是获取子应用列表,setList 是将子应用列表注册到 list 中,subApps.js,代码如下:
let list = [] 
export const getList = () => list 
export const setList = appList => list = appList 
  • micro 下的 start.js 中,对外暴露 registerMicroApps 这个方法,传入子应用注册列表 appList 和主应用的生命周期 lifeCycle,分别调用 setListsetMainLifecycle 进行注册设置,代码如下:
export const registerMicroApps = (appList, lifeCycle) => {
  setList(appList);
  setMainLifecycle(lifeCycle);
  // window.appList = appList;
}
  1. 路由拦截,如下所示:
  • 首先需要在 micro 微前端框架中的 start.js,调用 rewriteRouter 方法实现路由拦截。在 router 下的 rewriteRouter.js 中,对外暴露出 rewriteRouter,重写 window 的路由跳转。patchRouter 接收两个参数,原生的事件、事件的名称,所有触发 pushStatereplaceState 的事件都会被监听到,window.history.pushStatewindow.history.replaceState。通过 window.addEventListener 事件绑定 micro_pushmicro_replace,通过 window.onpopstate 事件监听,监听返回事件,当点了返回按钮后 turnApp 就会执行,rewriteRouter.js,代码如下:
import { patchRouter } from '../utils'
import { turnApp } from './routerHandle'

export const rewriteRouter = () => {
  window.history.pushState = patchRouter(window.history.pushState, 'micro_push');
  window.history.replaceState = patchRouter(window.history.replaceState, 'micro_replace');

  window.addEventListener('micro_push', turnApp);
  window.addEventListener('micro_replace', turnApp);

  window.onpopstate = async function () {
    await turnApp()
  }
}
  • patchRouter 是在 micro 下的 util 中的 index.js,给当前的路由跳转打补丁。通过 new Event 创建新的事件,通过 globalEvent.apply 传递过来的原生事件 globalEvent,代替函数的执行,通过 window.dispatchEvent 触发创建的事件,代码如下:
export const patchRouter = (globalEvent, eventName) => {
  return function () {
    const e = new Event(eventName)
    globalEvent.apply(this, arguments) 
    window.dispatchEvent(e) 
  }
}
  • turnApp 是在 micro 下的 router 中的 routerHandle.js,如果子应用发生改变在里面可以执行微前端的生命周期执行,代码如下:
import { isTurnChild } from '../utils'
import { lifecycle } from '../lifeCycle'
export const turnApp = async () => {
  if (isTurnChild()) {
    await lifecycle()
  }
}
  1. 获取首个子应用,如下所示:
  • registerApp 中调用 micro 下的 start.js,对外暴露的 start 方法启动微前端框架。通过 getList 首先验证当前子应用列表是否为空,如果子应用列表为空,抛出异常。如果子应用列表非空,有子应用的内容, 查找到符合当前路由的子应用。如果当前没有在使用的子应用, 抛出一个错误,请访问正确的连接,访问一个默认的路由,通常为首页或登录页面,window.history.pushState 跳转到对应的 url 上。若查找到子应用的内容并且 hash 值存在,进行标记,将当前 app 的内容设置为 app.activeRulestart.js,代码如下:
import { setList, getList } from './const/subApps'
import { currentApp } from './utils'
import { rewriteRouter } from './router/rewriteRouter'
import { setMainLifecycle } from './const/mainLifeCycle'
import { prefetch } from './loader/prefetch'
import { Custom } from './customevent'

const custom = new Custom()
custom.on('test', (data) => {
  console.log(data)
})

window.custom = custom;

rewriteRouter();

export const registerMicroApps = (appList, lifeCycle) => {
  setList(appList);
  setMainLifecycle(lifeCycle);
  // window.appList = appList;
}

export const start = () => {
  const apps = getList();
  if (!apps.length) {
    throw Error('子应用列表为空, 请正确注册');
  }
  const app = currentApp();
  const { pathname, hash } = window.location;

  if (!hash) {
    window.history.pushState(null, null, '/vue3#/index'); 
  }

  if (app && hash) {
    const url = pathname + hash;
    window.__CURRENT_SUB_APP__ = app.activeRule;
    window.history.pushState('', '', url);
  }

  prefetch();
}
  • 调用 currentApp 时,是在 microutils 下的 index.js,通过 window.location.pathname 获取到当前子应用的路由 activeRule,调用 filterApp 子应用在命中后就会返回,isTurnChild 是判断子应用是否做了切换,index.js,代码如下:
import { getList } from "../const/subApps";
export const patchRouter = (globalEvent, eventName) => {
  return function () {
    const e = new Event(eventName); 
    globalEvent.apply(this, arguments); 
    window.dispatchEvent(e); 
  }
}

export const currentApp = () => {
  const currentUrl = window.location.pathname;
  return filterApp('activeRule', currentUrl);
}

export const findAppByRoute = (router) => {
  return filterApp('activeRule', router);
}

export const filterApp = (key, value) => {
  const currentApp = getList().filter(item => item[key] === value);
  return currentApp && currentApp.length ? currentApp[0] : {};
}

export const isTurnChild = () => {
  const { pathname, hash } = window.location;
  const url = pathname + hash;
  const currentPrefix = url.match(/(\/\w+)/g);

  if (
    currentPrefix &&
    (currentPrefix[0] === window.__CURRENT_SUB_APP__) &&
    hash === window.__CURRENT_HASH__
  ) {
    return false;
  }
  window.__ORIGIN_APP__ = window.__CURRENT_SUB_APP__;
  const currentSubApp = window.location.pathname.match(/(\/\w+)/);

  if (!currentSubApp) {
    return false
  }
  window.__CURRENT_SUB_APP__ = currentSubApp[0];
  window.__CURRENT_HASH__ = hash;
  return true;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值