微前端的特性

  • 多个前端子工程组合在一起,每个子工程可以独立开发、运行、发版
  • 主工程只在合适的生命周期挂载、卸载对应的子工程
  • 小工程的框架不限,如 vue、react、angular

上手 single-spa

1. 父级容器工程

  1. 声明各种子工程
  2. 引入single-spa,使用registerApplicationstart方法注册并启动微前端服务
  3. 使用systemjs,去拉取子工程的 manifest 描述文件
  4. 使用systemjs,在合适的时候(子路由命名空间命中)去拉取子工程的 js 入口文件

2. 子工程

  1. 引入当前框架在 single-spa 的辅助程序,如当前框架是 vue,则引入single-spa-vue
  2. single-spa的规则,在入口导出bootstrapmountunmount方法
  3. 打包的时候,需要输出包的变量名字,如 webpack 配置 output.library 为 child1
  4. 打包的时候,输出要改为 umd 或者 window。如 webpack 配置 output.libraryTarget 为’umd’
  5. 打包的时候,webpack 配置中的 optimization.splitChunks 得改为 false。(原因可能是主包需要正确引入入口文件)
  6. 打包的时候,使用 stats-webpack-plugin 插件额外构建 manifest.json 文件,其中包含了所有产物的文件名(便于父容器拉取资源)
  7. 开发的时候,子工程的热更新服务需要开启跨域,devServer.headers 加一条”Access-Control-Allow-Origin”: ”*“

demo 文件

主工程 子工程

其他

关于动态路由:

  • 主工程是动态路由时,子工程路由中带有额外标识 meta.xxx

  • 所有子工程的路由,在主工程对应的页面都是 single-spa 挂载页面

  • single-spa 页面注册子路由的时候需要使用当前路由中的 meta.xxx 去拉取对应的子工程

  • 子工程在路由中处理路由,如果子工程的路由信息并非是去匹配 location,而是来自主工程,则需要动态加载对应的 component。「关键代码是动态加载component: (resolve) => require.ensure([], () => resolve(require(`@/views${item.componentPath}`)))」:

    // router.js
    export function importSubAppRouter(subAppRouter) {
      return subAppRouter.map((item) => {
        return {
          path: item.path,
          name: item.name,
          component: (resolve) =>
            require.ensure([], () =>
              resolve(require(`@/views${item.componentPath}`))
            ),
        };
      });
    }
    export function importDevRouter(devRouter) {
      return devRouter.reduce((result, devRouterItem) => {
        return devRouterItem.children
          ? [...result, ...importDevRouter(devRouterItem.children)]
          : [
              ...result,
              {
                ...devRouterItem,
                path: devRouterItem.path,
              },
            ];
      }, []);
    }
     
    export const routes =
      ENV_VUE_APP_ISSINGLE === "true"
        ? importSubAppRouter(
            window.parentStore.state.singleSpa.subAppRouters[pkg.microName]
          )
        : importDevRouter(devRouter);
    export default new VueRouter({ routes });