微前端的特性
- 多个前端子工程组合在一起,每个子工程可以独立开发、运行、发版
- 主工程只在合适的生命周期挂载、卸载对应的子工程
- 小工程的框架不限,如 vue、react、angular
上手 single-spa
1. 父级容器工程
- 声明各种子工程
- 引入
single-spa,使用registerApplication、start方法注册并启动微前端服务 - 使用
systemjs,去拉取子工程的 manifest 描述文件 - 使用
systemjs,在合适的时候(子路由命名空间命中)去拉取子工程的 js 入口文件
2. 子工程
- 引入当前框架在 single-spa 的辅助程序,如当前框架是 vue,则引入
single-spa-vue - 按
single-spa的规则,在入口导出bootstrap、mount、unmount方法 - 打包的时候,需要输出包的变量名字,如 webpack 配置 output.library 为 child1
- 打包的时候,输出要改为 umd 或者 window。如 webpack 配置 output.libraryTarget 为’umd’
- 打包的时候,webpack 配置中的 optimization.splitChunks 得改为 false。(原因可能是主包需要正确引入入口文件)
- 打包的时候,使用 stats-webpack-plugin 插件额外构建 manifest.json 文件,其中包含了所有产物的文件名(便于父容器拉取资源)
- 开发的时候,子工程的热更新服务需要开启跨域,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 });