基于qiankun落地部署微前端爬”坑“记
❝ 前沿:前半年微前端火得一踏糊涂,刚好业务需求上有这样的应用场景,针对目前的微前端解决方案做了技术选型,qiankun作为蚂蚁金服内部孵化出来的微前端解决方案,经过线上应用充分检验及打磨最后开源,最终选择qiankun作为我们云产品架构入口。不过官方文档上关于上线部署文档较少,很多童鞋也可能只是在本地玩玩,没有到真正走通整个闭环,于是结合自身,在将qiankun落地过程中遇到的“那些坑”做个梳理。希望对你有所帮助 ❞
1.? 茶前点心
❝ 本文不大篇幅介绍关于qiankun的“前世今生”,更多的设计理念介绍请看文档 如果有想了解其他微前端解决方案的童鞋,也可以回顾下之前树酱分享的微前端那些事 ❞
举个例子:我们有个这样的场景,我有个门户Portal的登陆界面(主应用基座),登陆成果后可以切换不同的子应用,如下有两个子应用A和B,且都在之前是独立部署的,单独可以访问,但是我们现在想借助qiankun把他们“嵌”到基座来加载,往下看实操
上面是一个通过域名访问子应用的示意图,接下来我们看看一个view视图,header头部和sideMenu左侧菜单是属于portal门户的,而右侧区域则是显示切换子应用的视图,预期效果:当我们访问dev.portal.com/a
该域名时(即切换到子应用A),左侧菜单也会根据不同应用切换不同数据
你可能会问直接用iframe不香吗?真不香,主要几个局限问题
- 父子应用之间通信问题
- cookie共享问题(可做单点登陆SSO)
- 交互视图效果不佳
1.1 注册子应用时应该注意哪些问题?
❝ ? 啊明同学:qiankun是如何注册子应用的呢? ❞
qiankun是通过registerMicroApps(apps, lifeCycles)
API来注册子应用的,详细文档请看点我? 用来实现当浏览器 url 发生变化时,自动加载相应的子应用的功能,结合上面的例子我们试着在基座main.js注册子应用
主要包括:
-
entry
: 子应用的 entry 地址,比如我们现在有两个子应用A和B,那么这里配置的就是他们的资源访问域名或ip -
render
:本质上是container的转换,container用来定义子应用的容器节点的选择器或者 Element 实例,这里使用的是实际例子 -
activeRule
:子应用的激活规则,即什么路由访问才会去fetch entry配置的域名或ip,我们用了getActiveRule
来完成匹配,我们看看getActiveRule
的实现,该函数通过传入当前 location 作为参数,然后根据函数返回数值来看,若返回值为 true 时则表明当前子应用会被激活,则去调用entry入口配置
匹配如下
✅ https://dev.portal.com/a
✅ https://dev.portal.com/a/anything/everything
? https://dev.portal.com/c
复制代码
匹配成功后,qiankun 通过 fetch 去获取所匹配子应用的静态资源
1.2 资源访问跨域如何解决?
❝ ? 啊呆同学:你这样不会跨域吗?基座 https://dev.portal.com/ 获子应用a的资源 https://dev.monitor.com/a的资源 ,根据浏览器同源策略(浏览器采用同源策略,禁止页面加载或执行与自身来源不同的域的任何脚本)应该获取不到吧,明显跨域 ❞
答案:是,由于 qiankun 是通过 fetch 去获取子应用注册时配置的静态资源url,所有静态资源必须是支持跨域的,那就得设置允许源了,简单的设置可以看下面
-
Access-Control-Allow-Origin
:跨域在服务端是不允许的。只能通过给Nginx配置Access-Control-Allow-Origin *
后,才能使服务器能接受所有的请求源(Origin) -
Access-Control-Allow-Headers
: 设置支持的Content-Type
1.3 子应用加载失败是什么问题?
❝ ? 啊明同学:跨域解决了,可还是fetch不到子应用a的静态资源?是什么问题咋搞? ❞
出现这个报错:Application died in status LOADING_SOURCE_CODE: You need to export the functional lifecycles in xxx entry
答案:你的打包姿势不对
❝ vue-cli 3x项目中需要通过在vue.config.js配置output来配置输出的方式,如下?所示 ❞
-
pubilcPath
: 主要解决的是子应用动态载入的 脚本、样式、图片 等地址不正确的问题 -
output.library
:需要与主应用注册子应用时的name一致且唯一 -
output.libraryTarget:umd
: 导出umd格式,可以支持inport、require和script引入
然后创建一个publichPath文件,并在main.js 引入
1.4 子应用的publichPath到底应该怎么配置?
❝ ? 啊明同学:打包output配置改好了,但是为什么publichPath路径配置为/a? ❞
拓展: 沿用上文提到的a应用的访问域名 dev.monitor.com/a
现在浏览器要正确获取a应用的静态资源中的css文件,则会去访问 dev.monitor.com/a/css/common.css
主要分两种情况:
- publichPath如果默认配置或者配置为/,则生成的index.html 访问的资源是则不正确,因为将访问的是dev.monitor.com/css/common.css并不是a应用的资源
- 配置为/a,则生成的index.html 访问的资源是 就可以
❝ ? 啊呆同学:那publichPath路径配置为
./
相对路径可以吗? ❞
答案:也是可以的,跟配置为/a
访问一样
1.5 如何保障原来的应用运行正常,但能集成到基座portal中
❝ ? 啊明同学:我之前a应用是单独运行部署的,我通过qiankun集成到基座portal中会有影响吗? ❞
答案:使用这个全局变量来区分当前是否运行在 qiankun 的主应用中
那就是: window.__POWERED_BY_QIANKUN_
那可以用来干嘛?请看下面?
- 独立运行:
window.__POWERED_BY_QIANKUN__
为false,执行mount创建vue对象 - 运行在qiankun:
window.__POWERED_BY_QIANKUN__
为true,则不执行mount
1.6 父应用如何共享util和data给子应用
❝ ? 隔壁老王同学:如果我想把门户登陆应用登陆成功获取到的个人数据共享给子应用还有一些公用的方法,我该怎么做? ❞
答案:可以在注册子应用的时候,把定义好要共享的msg,通过props共享出去
-
msg.data
: 把store状态管理数据共享给子应用 -
msg.prototype
: 定义一些原型数据,比如是否为qiankun上下文中
父应用定义完,那子应用是如何获取呢?是通过在子应用挂载前,将props数据导到子应用通过遍历赋值给到子应用vue原型中
1.7 history路由模式,需要如何配置ngnix,才能正常访问?
❝ ? 啊宇同学:我看你访问的路由模式不是hash,而是history模式,那你是怎么解决当页面刷新404问题? ❞
答案:通过nginx配置加入try_files,history 模式同样会有一个问题,就是当页面刷新时,如果没有合适的配置,会出现404错误,针对这种请看,需要额外在nginx配置,对于找不到url的,将首页html返回
-
try_files
:用来解决nginx找不到client客户端所需要的资源时访问404的问题 -
proxy_pass
:主要是用来配置接口网关反向代理,可以使得父子应用下访问的api是一致的,防止接口跨域问题
了解更多ngnix配置学习,请看树酱之前写的ngnix那些事
1.8 建议:针对刚学习微前端的童鞋
第一次学习微前端的童鞋可以学习Peter老师的chunchao
微前端框架,会比较容易理解上手 github链接
- 学习zepto.js(对象方法)[5]
- js 停止事件冒泡 阻止浏览器的默认行为
- vue-cli生成的项目配置开发和生产环境不同的接口
- 【52ABP实战教程】0.1-- Devops如何用VSTS持续集成到Github仓库!
- 学习zepto.js(对象方法)[4]
- 事件绑定的几种常见方式
- vue的iview列表table render函数设置DOM属性值的方法
- js焦点轮播图
- vue父组件中获取子组件中的数据
- 学习zepto.js(对象方法)[3]
- bootstrap 标签页tab切换js(含报错原因)
- 学习zepto.js(对象方法)[2]
- vue-router 2 跳转失败原因
- 学习zepto.js(对象方法)[1]
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android实现IP地址输入框的方法示例代码
- Node.js 搭建 HTTPS 服务器
- Android布局之表格布局TableLayout详解
- 简单实现Android倒计时效果
- Android实现单页面浮层可拖动view的一种方法
- 排查 Node.js 服务内存泄漏,没想到竟是它?
- Android判断网络状态的代码
- Android开发实现布局中为控件添加选择器的方法
- Android控制文本输入框最多输入10个字符长度
- Elasticsearch 内部数据结构深度解读
- 关于 Elasticsearch 段合并,这一篇说透了!
- 解了这十道C语言题,你敢说你精通C语言?
- 微服务中使用Maven BOM来管理你的服务版本
- 设计模式之代理模式(文末赠书)
- 使用Spring IoC容器:选BeanFactory还是ApplicationContext?