# 前言

single-spa-vue 是一个针对vue项目的初始化、挂载、卸载的库函数,可以实现 single-spa 注册的应用、生命周期函数等功能,详情请查看 single-spa-vue的github (opens new window)

# 安装

# 使用Vue CLI的项目

vue-cli-plugin-single-spa (opens new window) 将会把所有的事情都做好.

vue add single-spa

这个CLI(控制台命令行接口)插件将会做下面的事情:

  • 修改 webpack 配置,从而使你的项目适用于一个 single-spa 项目或是一个子应用。
  • 安装 single-spa-vue.
  • 修改你的 main.jsmain.ts 文件,从而使你的项目适用于一个 single-spa 项目或是一个子应用。
  • 添加 set-public-path.js ,从而有序地使用 systemjs-webpack-interop 来设置你的应用的 public path

# 没有使用Vue CLI的项目

npm install --save single-spa-vue

你可以通过选择引入 <script src="https://unpkg.com/single-spa-vue"> 到你的html文件中,就可以得到 singleSpaVue 全局变量

# 用法

如果没有安装过的话,请安装 systemjs-webpack-interop

npm install systemjs-webpack-interop -S

在和项目目录同级的位置新建 set-public-path.js 文件作为你的 main.js/ts

import { setPublicPath } from 'systemjs-webpack-interop';
setPublicPath('appName');

将你的应用的入口文件改成如下内容:

请注意,如果您使用的是Vue CLI插件 main.ts or main.js 文件将用此代码自动更新并设置为 set-public-path.js 文件将自动创建应用程序名为您的 package.json 的name属性。

如果您想处理您的Vue实例,可以按照下面的步骤修改mount方法。mount方法将在v1.6.0之后使用Vue实例返回Promise。

const vueLifecycles = singleSpaVue({...})
export const mount = props => vueLifecycles.mount(props).then(instance => {
  // 使用 vue 实例做你想做的事情
  ...
})

# Vue2

对于Vue 2,将应用程序的条目文件更改为:

import './set-public-path';
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import singleSpaVue from 'single-spa-vue';
const vueLifecycles = singleSpaVue({
  Vue,
  appOptions: {
    render(h) {
      return h(App);
    },
    router,
  },
});
export const bootstrap = vueLifecycles.bootstrap;
export const mount = vueLifecycles.mount;
export const unmount = vueLifecycles.unmount;

# Vue 3

对于Vue 3,将应用程序的条目文件更改为:

import './set-public-path';
import { h, createApp } from 'vue';
import singleSpaVue from '../lib/single-spa-vue.js';
import App from './App.vue';
const vueLifecycles = singleSpaVue({
  createApp,
  appOptions: {
    render() {
      return h(App, {
        props: {
          // single-spa 属性可以在 this 对象上使用。根据需要将它们转发给您的组件
          // https://single-spa.js.org/docs/building-applications#lifecyle-props
          name: this.name,
          mountParcel: this.mountParcel,
          singleSpa: this.singleSpa,
        },
      });
    },
  },
});
export const bootstrap = vueLifecycles.bootstrap;
export const mount = vueLifecycles.mount;
export const unmount = vueLifecycles.unmount;

出于性能上的考虑,Vue、Vue Router以及其他较大的库,最好使用相同的版本

# 自定义属性

single-spa 自定义属性可以传递到根组件,如下所示:

// main.js
const vueLifecycles = singleSpaVue({
  Vue,
  appOptions: {
    render(h) {
      return h(App, {
        props: {
          mountParcel: this.mountParcel,
          otherProp: this.otherProp,
        },
      });
    },
    router,
  },
});
// App.vue
<template>
  <button>{{ otherProp }}</button>
</template>
<script>
export default {
  props: ['mountParcel', 'otherProp'],
}
</script>

# 依赖共享

要实现不同应用间的依赖共享,添加你想要共享的依赖作为 webpack externals (opens new window)。然后使用 一个工作在浏览器中的模块加载工具,比如 systemjs (opens new window),来为 single-spa 中的每个项目提供这些共享的依赖,将 vue 以及其他库添加到 import map (opens new window)中。

如下案例给出一个 import map 的案例,可以作为参考: coexisting-vue-microfrontends (opens new window)的 index.html 文件。

依赖共享是被强烈建议的。详细的原因可以查看 recommended setup for single-spa (opens new window)

# 使用Vue CLI的情况下共享的配置

// vue.config.js
module.exports = {
  chainWebpack: config => {
    config.externals(['vue', 'vue-router']);
  },
};

# 未使用Vue CLI的情况下共享的配置

// webpack.config.js
module.exports = {
  externals: ['vue', 'vue-router'],
};

# 选项

当调用 singleSpaVue(opts) 时,所有选项都是通过 opts 参数传入 single-spa-vue

  • Vue: (必传项) 主Vue对象, 通常暴露在window对象上,或通过 require('vue') ``import Vue from 'vue' 获得
  • appOptions: (必传项) 类型为Object对象类型,用来实例化Vue应用。appOptions将直接透传为Vue构造函数实例化时的初始化参数 new Vue(appOptions)。需要注意:如果你没有传el选项,插件就会自动创建一个div,并作为一个Vue项目的默认容器附加到DOM中。
  • loadRootComponent: (非必传,用于取代 appOptions.render) 在懒加载时有用,一个以 root component 为成功回调参数的 Promise 对象。

可以用 appOptions.el 配置 single-spa 要挂载到哪个dom元素上:

onst vueLifecycles = singleSpaVue({
  Vue,
  appOptions: {
    render: h => h(App),
    el: '#a-special-container',
  },
});

# 作为一个single-spa应用

想要创建一个single-spa应用,只需要从appOptions中去掉el选项,如此一来,dom元素将需要应用的开发者来指定,除此之外的其他选项都应该和上述案例保持一致

# Parcels

# 创建 parcel

parcel 是一个对象,它表示在Vue、React、Angular或任何其他框架中实现的组件

要创建 VueJS single spa parcel 配置对象,只需从appOptions中省略el选项,因为dom元素将由 Parcel 的用户指定。所有其他选项都应与上面的示例完全相同。

const parcelConfig = singleSpaVue({...});

# 渲染 parcel

要在Vue中呈现 parcel 配置对象,可以使用 single-spa-vue's Parcel 组件:

<template>
  <Parcel
    v-on:parcelMounted="parcelMounted()"
    v-on:parcelUpdated="parcelUpdated()"
    :config="parcelConfig"
    :mountParcel="mountParcel"
    :wrapWith="wrapWith"
    :wrapClass="wrapClass"
    :wrapStyle="wrapStyle"
    :parcelProps="getParcelProps()"
  />
</template>
<script>
// For old versions of webpack
import Parcel from 'single-spa-vue/dist/esm/parcel'
// For new versions of webpack
import Parcel from 'single-spa-vue/parcel'
import { mountRootParcel } from 'single-spa'
const Widget =
export default {
  components: {
    Parcel
  },
  data() {
    return {
      /*
        parcelConfig (object, required)
        parcelConfig是一个对象,或者是一个与parcelConfig对象解析的 promise
        对象可以来自当前项目中,也可以通过跨微前端导入来自不同的微前端。它可以表示Vue组件,也可以表示React/Angular组件。
        https://single-spa.js.org/docs/recommended-setup#cross-microfrontend-imports
        Vanilla js object:
        parcelConfig: {
          async mount(props) {},
          async unmount(props) {}
        }
        // React component
        parcelConfig: singleSpaReact({...})
        // 交叉微前端导入如下所示
      */
      parcelConfig: System.import('@org/other-microfrontend').then(ns => ns.Widget),
      /*
        mountParcel (function, required)
        mountParcel 函数可以是当前Vue应用程序的 mountParcel 属性,也可以是全局可用的mount RootParcel 函数。更多信息请访问
        http://localhost:3000/docs/parcels-api#mountparcel
      */
      mountParcel: mountRootParcel,
      /*
        wrapWith (string, optional)
       	wrapWith字符串确定将为 parcel 提供哪种dom元素。默认为'div'
      */
      wrapWith: 'div'
      /*
        wrapClass (string, optional)
        wrapClass字符串用作提供给 parcel 的dom元素的CSS类。
      */
      wrapClass: "bg-red"
      /*
        wrapStyle (object, optional)
        wrapStyle对象作为CSS样式应用于 parcel 的dom元素容器
      */
      wrapStyle: {
        outline: '1px solid red'
      },
    }
  },
  methods: {
    // These are the props passed into the parcel
    getParcelProps() {
      return {
        text: `Hello world`
      }
    },
    // Parcels mount 安装,因此 parcel 完成安装后将调用此方法
    parcelMounted() {
      console.log("parcel mounted");
    },
    parcelUpdated() {
      console.log("parcel updated");
    }
  }
}
</script>
阅读全文