微前端: single-spa

一个前端微应用的框架介绍与使用笔记
更新于: 2023-03-24 09:35:16

single-spa

是一个用于实现前端微应用的框架。

利用 cli 可以看到这几种选择
  • single-spa-application/parcel: 微前端应用,可以使用 vue/react/angular 等框架
  • single-spa root config: 创建微应用的容器应用(container)
  • utilty modules: 公共模块应用,非渲染组件,用于跨应用共享的 js 逻辑微应用(module)

安装

类似于 react-cra 一样的 cli 工具。

yarn add global create-single-spa

最终的项目结构如下

创建容器应用

  • create-single-spa
  • 选择 single-spa-root config 这个
  • 项目名称: container

调试工具

开发的时候,可以把 registerApp 的地址,替换成本地,方便开发。

chrome调试工具: single-spa inspector

<script src="https://cdn.jsdelivr.net/npm/import-map-overrides@2.2.0/dist/import-map-overrides.js"></script>
<import-map-overrides-full show-when-local-storage="devtools" dev-libs></import-map-overrides-full>

开发与生产

  • isLocal: 区分环境
  • 加载不同的 js 文件
<% if (isLocal) { %>
<script src="https://cdn.jsdelivr.net/npm/systemjs@6.8.3/dist/system.js"></script>
<script src="https://cdn.jsdelivr.net/npm/systemjs@6.8.3/dist/extras/amd.js"></script>
<% } else { %>
<script src="https://cdn.jsdelivr.net/npm/systemjs@6.8.3/dist/system.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/systemjs@6.8.3/dist/extras/amd.min.js"></script>
<% } %>

注册一个应用

  • name: 唯一名称
  • app: 反应一个应用,得是一个 Promise
  • activeWhen: 路由
  • 这个是在 container 里注册应用
registerApplication({
  name: "@single-spa/welcome",
  app: () =>
    System.import(
      "https://unpkg.com/single-spa-welcome/dist/single-spa-welcome.js"
    ),
  activeWhen: ["/"],
});

启动应用

  • urlRerouteOnly 是否可以用 history 相关的触发路由有关
start({
  urlRerouteOnly: true,
});

创建一个微应用(子)

  • 创建目录 lagou
  • 创建 package.json
  • 另外:没有用他的脚手架
yarn add @babel/core single-spa  webpack webpack-cli webpack-dev-server webpack-merge webpack-config-single-spa

目录结构

.
├── package.json
├── src
│   └── jswork-lagou.js
├── webpack.config.js
└── yarn.lock

wepback.config.js

const spaDefaults = require("webpack-config-single-spa");
const { merge } = require("webpack-merge");

module.exports = () => {
  const defaultConfig = spaDefaults({
    orgName: "jswork",
    projectName: "lagou",
  });

  return merge(defaultConfig, {
    devServer: {
      port: 9001,
    },
  });
};

子应用注册 importmap

这个就是我们的不基于任何框架的微应用 "@jswork/lagou":"http://localhost:9001/jswork-lagou.js"

<script type="systemjs-importmap">
  {
    "imports": {
      "single-spa": "https://cdn.jsdelivr.net/npm/single-spa@5.9.0/lib/system/single-spa.min.js",
      "@jswork/lagou":"http://localhost:9001/jswork-lagou.js"
    }
  }
</script>

子应用入口

src/jswork-lagou.js (scope - projectName 这里需要与webpack一至)

// filename 必须与 scope-name 一致

// bootstrap mount unmout

let lagouEl = null;

export async function bootstrap() {
  console.log("react app bootstraped");
}

export async function mount() {
  console.log("props from main framework");
  lagouEl = document.createElement("div");
  lagouEl.id = "lagou";
  lagouEl.innerHTML = "<h1>lagou works!</h1>";
  document.body.appendChild(lagouEl);
}

export async function unmount() {
  console.log("react app unmount");
  document.body.removeChild(lagouEl);
}

路由冲突

在 container 应用注册的时候处理 loc.pathname 为精确匹配,即可 (loc) => loc.pathname === "/"

  • container 应用: /
  • 子应用: /lagou
import { registerApplication, start } from "single-spa";

registerApplication(
  "@single-spa/welcome",
  () =>
    System.import(
      "https://unpkg.com/single-spa-welcome/dist/single-spa-welcome.js"
    ),
  (loc) => loc.pathname === "/"
);

registerApplication({
  name: "@jswork/lagou",
  app: () => System.import("@jswork/lagou"),
  activeWhen: ["/lagou"],
});

start({
  urlRerouteOnly: true,
});

参考