前端微服务化解决方案3 - 模块加载器

微前端的模块加载器,主要功能为:

  • 项目配置文件的加载
  • 项目对外接口文件的加载(消息总线会用到,后续会提)
  • 项目入口文件的加载

以上也是每一个单模块,不可缺少的三部分

配置文件

我们实践微前端的过程中,我们对每个模块项目,都有一个对外的配置文件. 是模块在注册到 singe-spa 时候所用到的信息.

{
    "name": "name", //模块名称
    "path": "/project", //模块url前缀
    "prefix": "/module-prefix/", //模块文件路径前缀
    "main": "/module-prefix/main.js", //模块渲染出口文件
    "store": "/module-prefix/store.js",//模块对外接口
    "base": true
    // 当模块被定性为baseApp的时候,
    // 不管url怎么变化,项目也是会被渲染的,
    // 使用场景为,模块职责主要为整个框架的布局或者一直被渲染,不会改变的部分
  }

当我们的模块,有多种 url 前缀的时候,path 也可以为数组形式

{
    "path": ["/project-url-path1/","/project-url-path2/"], //项目url前缀
  }

配置自动化

我们每个模块都有上面所描述的配置文件,当我们的项目多个模块的时候,我们需要把所有模块的配置文件聚合起来. 我这里也有写一个脚本.

micro-auto-config

使用方法:

npm install micro-auto-config -g

# 在项目根目录,用pm2启动该脚本,便可启动这个项目的配置自动化
pm2 start micro-auto-config

大概思路是:当模块部署,服务器检测到项目文件发生改变,便开始找出所有模块的配置文件,把他们合并到一起. 以数组包对象的形式输出一个总体的新配置文件 project.config.js. 当我们一个模块配置有更新,部署到线上的时候,项目配置文件会自动更新.

模块加载器

这个文件直接引入到 html 中,也就是上一篇文章中的single-spa-config.js 升级版. 在加载模块的时候,我们使用 SystemJS 作为我们的模块加载工具.

"use strict";
import "../libs/es6-promise.auto.min";
import * as singleSpa from "single-spa";
import { registerApp } from "./Register";

async function bootstrap() {
  // project.config.js 文件为所有模块的配置集合
  let projectConfig = await SystemJS.import("/project.config.js");

  // 遍历,注册所有模块
  projectConfig.projects.forEach((element) => {
    registerApp({
      name: element.name,
      main: element.main,
      url: element.prefix,
      store: element.store,
      base: element.base,
      path: element.path,
    });
  });

  // 项目启动
  singleSpa.start();
}

bootstrap();

Register.js

import "../libs/system";
import "../libs/es6-promise.auto.min";
import * as singleSpa from "single-spa";

// hash 模式,项目路由用的是hash模式会用到该函数
export function hashPrefix(app) {
  return function (location) {
    let isShow = false;
    //如果该应用 有多个需要匹配的路劲
    if (isArray(app.path)) {
      app.path.forEach((path) => {
        if (location.hash.startsWith(`#${path}`)) {
          isShow = true;
        }
      });
    }
    // 普通情况
    else if (location.hash.startsWith(`#${app.path || app.url}`)) {
      isShow = true;
    }
    return isShow;
  };
}

// pushState 模式
export function pathPrefix(app) {
  return function (location) {
    let isShow = false;
    //如果该模块 有多个需要匹配的路径
    if (isArray(app.path)) {
      app.path.forEach((path) => {
        if (location.pathname.indexOf(`${path}`) === 0) {
          isShow = true;
        }
      });
    }
    // 普通情况
    else if (location.pathname.indexOf(`${app.path || app.url}`) === 0) {
      isShow = true;
    }
    return isShow;
  };
}

// 应用注册
export async function registerApp(params) {
  singleSpa.registerApplication(
    params.name,
    () => SystemJS.import(params.main),
    params.base ? () => true : pathPrefix(params)
  );
}

//数组判断 用于判断是否有多个url前缀
function isArray(o) {
  return Object.prototype.toString.call(o) == "[object Array]";
}

未完待续 …

相关文章

前端微服务化解决方案 1 - 思考

前端微服务化解决方案 2 - Single-SPA

前端微服务化解决方案 3 - 模块加载器

前端微服务化解决方案 4 - 消息总线

前端微服务化解决方案 5 - 路由分发

前端微服务化解决方案 6 - 构建与部署

前端微服务化解决方案 7 - 静态数据共享

前端微服务化解决方案 8 - 二次构建

Demo

前端微服务化 Micro Frontend Demo

微前端模块加载器

微前端 Base App 示例源码

微前端子项目示例源码

微信公众号

本文链接:

https://alili.tech/archive/1a60cede/

# 最新文章