项目背景
目前项目中需要将A ,B 两个项目进行融合,A ,B 项目有各自的用户token与自己的交互方式,技术栈一致。之前是用跳链的方式,新开一个页面进行操作,给用户的感官是两个项目较为割裂,目前期望让两个项目看起来像一个整体,减少割裂感。
解决方案
- iframe内嵌 + 菜单融合
- 微前端融合
iframe内嵌 + 菜单融合
iframe内嵌有两种方式:
第一种是将整个B 项目作为一个页面融入A 项目,这样做的好处是切换页面不会重复请求,session,local等存储便于管理,不会出现样式覆盖。缺点是依旧有一些割裂感,且B 项目是整体作为一个页面,感官上很奇怪。
第二种是将B 项目的多个页面融入A 项目的菜单,整体感官上会是一个整体,但是切换页面的时候容易造成重复请求,白屏等情况,用户体验较差
微前端融合------qiankun
qiankun 是一个由阿里巴巴(蚂蚁金服)团队开源的微前端解决方案。它基于Single-SPA进行二次开发和增强,旨在帮助开发者将单体前端应用改造为由多个独立"微应用"聚合而成的架构。
如果你是使用
umi框架进行开发,我建议使用qiankun,因为umi提供开箱即用的微前端支持,通过简单的配置即可实现微前端融合
qiankun 使用 Proxy沙箱拦截全局变量,通过重写CSS选择器,添加前缀实现样式隔离,依赖 import-html-entry解析HTML。通过 props 传递和 globalState 进行全局状态管理。基于数据流传递内容
微前端融合------wujie
Wujie 是由京东物流技术团队自主研发的微前端框架,用于解决大型前端应用在模块解耦、独立部署、技术栈兼容等方面的挑战。
如果你是使用 vue 进行页面开发,同时期望用尽可能少的改动实现简单的微前端融合,我推荐使用 wujie。
无界使用iframe 作为JS运行沙箱,通过 WebComponent 将渲染内容映射到主应用上,既享受了iframe对JS的绝对隔离,又避免了iframe的UI通信和样式局限性。CSS通过样式作用域和 Shadow-DOM机制,隔离性优于qiankun的Proxy模式。原生支持子应用保活,预加载等功能,开发改动量小。
Wujie 接入
详细代码见:wujie/examples at master · Tencent/wujie · GitHub
主应用改造
main.js 改造
javascript
// main.js
import WujieVue from "wujie-vue2";
const isProduction = process.env.NODE_ENV === "production";
const { setupApp, preloadApp, bus } = WujieVue;
bus.$on("click", (msg) => window.alert(msg));
// 在 xxx-sub 路由下子应用将激活路由同步给主应用,主应用跳转对应路由高亮菜单栏
bus.$on("sub-route-change", (name, path) => {
const mainName = `${name}-sub`;
const mainPath = `/${name}-sub${path}`;
const currentName = router.currentRoute.name;
const currentPath = router.currentRoute.path;
if (mainName === currentName && mainPath !== currentPath) {
router.push({ path: mainPath });
}
});
const degrade = window.localStorage.getItem("degrade") === "true" || !window.Proxy || !window.CustomElementRegistry;
const props = {
jump: (name) => {
router.push({ name });
},
};
/**
* 大部分业务无需设置 attrs
* 此处修正 iframe 的 src,是防止github pages csp报错
* 因为默认是只有 host+port,没有携带路径
*/
const attrs = isProduction ? { src: hostMap("//localhost:8000/") } : {};
/**
* 配置应用,主要是设置默认配置
* preloadApp、startApp的配置会基于这个配置做覆盖
*/
setupApp({
name: "react16",
url: hostMap("//localhost:7600/"),
attrs,
exec: true,
props,
fetch: credentialsFetch,
plugins,
prefix: { "prefix-dialog": "/dialog", "prefix-location": "/location" },
degrade,
...lifecycles,
});
子应用渲染组件实现
js
//Vue2Sub
<template>
<!--单例模式,name相同则复用一个无界实例,改变url则子应用重新渲染实例到对应路由 -->
<WujieVue width="100%" height="100%" name="vue2" :url="vue2Url"></WujieVue>
</template>
<script>
import hostMap from "../hostMap";
export default {
computed: {
vue2Url() {
return hostMap("//localhost:7200/") + `#/${this.$route.params.path}`;
},
},
methods: {
jump(name) {
this.$router.push({ name });
},
},
};
</script>
<style lang="scss" scoped></style>
编写路由!!!
js
//router.js
{
path: "/vue2-sub/:path",
name: "vue2-sub",
component: Vue2Sub,
},
总结
wujie 的优势很明显,代码改动量小,无需配置子应用前缀,适合快速接入开发的项目,数据传递主要依赖于事件系统,例如我本来想传递一个全局用户信息给子应用,结果发现数据变化时,不会触发任何内容,并且子应用的 props 也没有更新,所以我自己基于事件传递实现了一套自动化获取token然后登陆的逻辑,实现相关功能。此外wujie的文档较为简单,有些功能函数的具体作用也不是很清晰,只能根据自己去猜(也可能是笔者能力较弱,叠个甲,哈哈哈)