postmessage 游戏窗口内无效_前端的微前端在交通项目内的应用实践
项目背景
业务的快速发展,越来越多的接入渠道(百度、快应用等等),人员增加,开发成本与管理成本都上升,效率反而越来越低,团队的人员重复造轮子,毫无挑战,当然市面上也有多端解决方案,但是不太适用目前的业务,所以前端聚合成“微前端”的概念。
单体应用与「微前端」架构
在传统的软件开发当中,大多数软件都是单体式应用架构的。为了适应我们这个时代的不确定性。快速试验,快速失败。更快地推出新产品和有效地改进当前产品,从而为客户提供有意义的数字体验。
而单体应用这种软件架构对于企业来说的致命缺点就是,对于市场的响应速度变慢。由于依赖关系,其响应周期往往会变得非常漫长。此时前端工程化越来越复杂,每次的整站编译耗时严重,根据用户漏斗原则,有一部分无效的消耗。
「微前端」的好处
安全高效:服务分离,降低整体风险与测试等成本;
灵活扩展:为每一个服务选择最合适的技术和基础架构;
独立敏捷:每一个服务可以独立开发,测试和部署。
「微前端」的四种可选实践方案
使用后端模板引擎插入 HTML (方案A
)
方案会增加后端复杂度,并且又将前端的渲染控制权交回了后端服务器。作为一个前端开发人员,一般不会选择该方案,但并不是完全没用,需要看使用场景,如果只是纯展示的,可以适当考虑。
客户端 JavaScript 异步加载 (方案B
)
这种方式可以通过前端模块化方式在开发整个「微前端」方案(AMD,CMD等),对开发效率有一定影响。
WebComponents 整合所有功能模块 (方案C
)
使用了较新的技术方案,在较老的浏览器上,可能兼容性欠缺。我们需要在整个 Web 应用程序上做出改变,把它们全部转换成 Web Components。
使用 iframe 隔离运行 (方案D
)
优点
最强大的是隔离了组件和应用程序部分的运行时环境,因为每个模块都可以独立开发,并且可以与其他部分的技术无关;
因为每个模块都可以独立开发,我们可以使用我们最熟悉的框架开发「微前端」模块;
可以各自使用完全不同的前端框架,可以在 React 中开发一部分,在 Vue 中开发一部分,然后使用原生 JavaScript 开发其他部分或任何其他技术;
消息传递也就相当强大。(Window.postMessageAPI)。
缺点
Bundle的大小非常明显,因为应用程序是分开的,所以在构建时也不能提取公共依赖关系;
考虑到浏览器性能问题,尽量避免iframe的多层调用;
处理移动端中的iframe时,将变得相当痛苦。
「微前端」的技术选型
需要接入的「微前端」的应用
「微前端」开发要求
准:快速找出当前应用的痛点,给出「微前端」解决方案;
快:快速迭代开发上线;
稳:保证线上代码兼容性以及稳定运行。
「微前端」的演变过程
前端单体应用痛点
每个站点中,都有一个通用的模块《常旅》;
这么多站点也不是一次性完成,每次需要开发新的站点,都通过Copy的方式,将《常旅》复制一份;
当站点积累到一定程度时,突然《常旅》有个新的需求需要开发;
此时,我们的开发量就成倍增加(1 x N),同时测试的工作量也一样。
单体应用到的「微前端」进化
「微前端」优劣
前端单体应用中的常旅模块分离,增加通用的「微前端」服务模组;
常旅模块独立为一个单独的「微前端」应用,为每个站点提供”常旅模块“服务;
此时,如果需要做「常旅功能迭代」,工作量永远只需要一份就可以;
我们需要保证「微前端」稳定运行,一旦奔溃,将影响所有引入的单体应用。
「微前端」架构方案
主框架:vue + vue-router + vuex
通讯方案:url入参 + postMessage + vue-unicom(内部广播机制)
滚屏:iscroll
布局方案:px2rem (750)布局
承载方式:iframe + webview
静态资源加载优化:asset-cache-webpack-plugin(h5离线缓存)
宿主(iframe)优化方案 「微前端」预加载
「微前端」数据通讯方案
「微前端」数据通讯方案
「微前端」部分数据通讯代码
// routerimport router from '../../router' // 获得一个唯一的值import getSole from 'rimjs/sole'// 全局unicom事件触发import { unicomEmit } from 'rimjs/vueUnicom' // 当前环境的变量数据,这里主要是设置身份import { env } from '../env'let win = window// 默认为 iframe 需要兼容其他的,可以这里兼容function postMessage(data, source = window.parent) {source.postMessage(data, '*')}// 临时存放 Promise 的resolve的对象let postMessageResolveFns = {} // 信息输入数据let messageInData = null// 信息输出数据 暂存let messageOutData = {}let query = router.queryif (query._in != null) { messageInData = {}}if (messageInData) {// 是否为类微信小程序内(webview和小程序无法实时双向通讯)// 传入的参数需要一次性输入try {Object.assign(messageInData, JSON.parse(query._in) || {})} catch (e) {}}// 用户同步用户身份信息的函数function setUser(inEnv) {if (!inEnv) {return}env.userId = inEnv.userId || ''}// 从宿主获取用户身份async function getUser() {let inEnv = await bridge.postMessage('env:get')setUser(inEnv)}// 接收消息// type:类型 fnKey:回调方法 insruct:指令 data:数据function onMessage({ type, insruct, data } = {}, source) {let backData = nullif (type == 'unicom') { backData = data == null ? {} : data// 事件广播, 通过这个方法,宿主环境可以向「微前端」端发送广播消息unicomEmit(insruct, backData)return}if (type == 'system') {// 一些系统设置if (insruct == 'font') {// rem布局,根节点 字体大小// 「微前端」如果为预加载,无法正确计算出根节点字体大小,需要宿主通知document.documentElement.style.fontSize = datareturn}if (insruct == 'href') {setUser(data.env)// iframe 预加载,重新定位 当前路由if (data.replace) {window.location.replace(window.location.pathname + '#' + data.href)} else {window.location.href = window.location.pathname + '#' + data.href}return}if (insruct == 're_init') {// 重新初始化页面,一般在iframe中,返回宿主环境后使用window.location.replace(window.location.pathname + '#/init?re=1')return}return}if (type == 'back') {// 找到回调的Promiselet resolveFn = postMessageResolveFns[insruct]if (resolveFn) {// 运行 resolveFnresolveFn(data)// console.log("data", data)delete postMessageResolveFns[insruct]}return}if (fnKey) {// 数据回调postMessage({ type: 'back', from: 'microservice', insruct: fnKey, data: backData }, source)}}// 注册接收的消息// 如果需要兼容其他渠道,可以这里写不同的window.addEventListener('message', function (ev) {let opt = ev.data || {}let { from } = optif (from != 'microservice') {// 非目标,舍弃return}onMessage(opt, ev.source)})// 对象定义export let bridge = {// postMessage 发送消息// 可以通过 Promise 获取到宿主的回调函数的值// bridge.postMessage("xxx") 同 bridge.postMessage("unicom:xxx")// data 为发送的参数postMessage(opt, data) {if (typeof opt == 'string') {let x = opt.match(/^([^:]*):*(.*)$/)if (!x) { x = [opt]}if (!x[2]) {// type 默认为 unicom x[2] = x[1] x[1] = 'unicom'} opt = { data,type: x[1],insruct: x[2]}}return new Promise(function (resolve) {// 回调唯一的keylet fnKey = 'ms:' + getSole() postMessageResolveFns[fnKey] = resolveif (messageInData) {// 此处无法直接使用 postMessagelet key = opt.type + ':' + opt.insruct// 寄存需要发送出去的数据 messageOutData[key] = data// console.log(messageOutData, key, args)setTimeout(function () {// 模拟接收到 back 事件onMessage({type: 'back',insruct: fnKey,data: messageInData[key] || null})}, 0)return}// 发送下次postMessage(Object.assign({ from: 'microservice', fnKey }, opt))})},// 结束「微前端」endBack(opt, data) {if (opt) {// 如果在「微前端」中有对身份信息做一些修改,需要同步到宿主this.postMessage(opt, data)}// 退出「微前端」界面// 不同载体,需要不同的方法// 比如 微信小程序,就需要 win.wx.miniProgram.navigateBack()if (messageInData) {// 此处微信小程序内嵌webview处理// 微信返回win.wx.miniProgram.navigateBack()// 微信发送寄存的数据win.wx.miniProgram.postMessage({ data: messageOutData })return}// 历史记录后退window.history.back()}}// 初始化时,尝试获取用户身份信息getUser()
滚屏为什么要使用iscroll?
问题 iframe引入时,body上的滚屏失效。
解决方案: 引入 iscrollbar5.2 实现自定义滚动条。
"less">.cp-layout {position: fixed;left: 0;right: 0;top: 0;bottom: 0;z-index: 2;overflow: hidden;
.iScrollVerticalScrollbar {position: absolute;z-index: 9999;width: 3px;bottom: 2px;top: 2px;right: 4px;overflow: hidden;pointer-events: none; }.iScrollIndicator {box-sizing: border-box;position: absolute;border-radius: 3px;width: 100%;transition-duration: 0ms;transform: translate(0px, 0px);transition-timing-function: cubic-bezier(0.1, 0.57, 0.1, 1);background-color: rgba(0, 0, 0, 0.2); } }
class="cp-layout">
通讯方案使用了postMessage为什么还要使用url入参?
问题:小程序webview对postMessage受限。解决方案: 使用url传参一次性传入所有参数。完成后,通过postMessage将数据一次性全部发送到宿主。
布局中,为什么要使用px2rem?
问题:iframe引入时,viewport 设置布局模式无效。解决方案: 引入 px2rem ,用rem来做整体布局。
「微前端」优化
如何减少「微前端」加载白屏时间?
通过骨架屏,减少白屏时间;
通过引入 asset-cache-webpack-plugin 插件,为资源加载做离线缓存(减少或消除白屏时间);
iframe时,使用单列模式开发,避免同时初始化多个「微前端」,造成性能损耗;
iframe加载微服务时,对「微前端」预加载。当需要使用时,可以快速展现。
引入 px2rem
和 asset-cache-webpack-plugin
vue.config.js
const AssetCachePlugin = require('asset-cache-webpack-plugin')module.exports = {devServer: {port: 9001 },// 样式配置css: {// css不单独一个文件编译extract: false,loaderOptions: {postcss: {plugins: [require('postcss-plugin-px2rem')({rootValue: 100, //换算基数, 默认100 ,这样的话把根标签的字体规定为1rem为50px,这样就可以从设计稿上量出多少个px直接在代码中写多上px了。exclude: /(node_module)/,mediaQuery: false, //(布尔值)允许在媒体查询中转换px。minPixelValue: 0 //设置要替换的最小像素值(3px会被转rem)。默认 0 }) ]conf.plugin('asset-cache').use(AssetCachePlugin, [
总结与思考:「微前端」的优缺点
优点
敏捷性 - 独立开发和更快的部署周期;
快捷测试 - 每一个小的变化不必再触碰整个应用程序的回归测试;
有助于持续集成、持续部署以及持续交付;
维护简单,每个团队都熟悉所维护特定的区域。
缺点
复杂的集成,「微前端」需要面对的多种环境,需要做多种兼容性验证;
第三方模块重叠,依赖冗余增加了管理的复杂性。在团队之间共享公共资源的机制;
避免影响最终用户的体验,「微前端」初始化可能会增加不必要的等待时间。
使用场景
在单体应用中那些重复试用的模块可以抽取为「微前端」;
这些模块也较稳定,代码迭代相对较少;
在单体应用中的那些主流程,不适用于「微前端」。
postmessage 游戏窗口内无效_前端的微前端在交通项目内的应用实践相关推荐
- 前端怎么使用jsessionid_前端搞微前端 | 侑夕 - 如何落地微前端一体化运营工作台...
下期预告 前端早早聊大会目标成为用得上.听得懂.抄得走的技术大会,计划 2020 年办 >= 15 期,由前端早早聊与掘金联合举办,前端早早聊大会行程动态.录播视频/PPT/讲稿资料下载请关注 ...
- 闲庭信步聊前端 - 见微知著微前端
笔者初次接触微前端在2020年7月,是从同事的口中听说的.虽然不算是一个早期接触者,但是也确实的推动和跟进了内部某大型项目的开发和落地.也希望能把一些走过的坑和一些思考分享给大家.文内所指应用均为PC ...
- web 折线图大数据量拉取展示方案_对比多种微前端方案
转自原文:帮你对比多种微前端方案 一.写在前面 在之前的文章中,我们已经深入剖析了微前端究竟是什么,可以带来什么收益,现在让我们复习一下微前端的概念: Techniques, strategies a ...
- qiankun 传统项目配置_飞猪微前端实践:统一运营工作台的解决方案-阿里云开发者社区...
作者:侑夕 飞猪一体化运营工作台一期工作经过 3 个月的开发终于内部开始使用,期间我们面向运营场景,基于微前端与 SDK 化的一体化集成方案,完成 4 大场景 10 余个平台的接入和配置打通,并对数百 ...
- 前端 - 用微前端前应该了解的一些知识点
一.微前端 微前端(Micro-Frontends)是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将 Web 应用由单一的单体应用转变为多个小型前端应用聚合为一的应用.微前端不是单纯的前 ...
- qiankun 微前端_qiankun 微前端应用实践与部署(二)
下面是两种方案的简要描述. 传统部署 方式 通过配置 nginx 端口到目录的转发. 具体可查看上一篇文章 特点 需要对外开放子应用对应的端口,将编译好的应用文件放到对应的配置目录. docker 部 ...
- gg修改器修改内购_如何用GG修改器修改应用内文字
首先说明 这里修改不是用的mt管理器查看这个应用的安装包 因为这样会被支付宝检测禁止交易 我们选用GG修改器 示例应用-支付宝 我们进入GG修改器 选择目标应用支付宝 应用内存范围不需要动 我们进入搜 ...
- qiankun 微前端_微前端方案 qiankun(实践及总结)
❝ 作者:沉末_ 链接:https://juejin.im/post/5ed73b73e51d4578724e3fa4 ❞ 什么是微前端? 我们先来看两个实际的场景: 1. 复用别的的项目页面 通常, ...
- bootstrap跟vue冲突吗_知道微服务,但你知道微前端吗?
在 toB 的前端开发工作中,我们往往就会遇到如下困境: 工程越来越大,打包越来越慢 团队人员多,产品功能复杂,代码冲突频繁.影响面大 内心想做 SaaS 产品,但客户总是要做定制化 不同的团队可能有 ...
最新文章
- adb.exe: more than one device/emulator
- 发表国外期刊注意事项
- HBase与时空索引技术
- 第12讲:Ajax 的原理和解析
- 2017一季度JAVA面试题锦集
- Alictf 2015决赛题目设计和解题思路
- linux view查看日志命令,【Linux】linux查看日志文件内容命令tail、cat、tac、head、echo...
- IOS开发中的弹框综述
- mysql重置密码报错,吐血整理
- 文件太大打不开,可以使用LogViewer
- SVN和git的优缺点比较
- c语言简单计算器减编程,C语言实现简单的计算器(加、减、乘、除)
- QQ聊天记录统计可视化分析
- java 项目中遇到的问题 和解决方案_java开发常见的问题及解决办法 - java开发中遇到的难点有哪些_java开发常见的问题及解决办法...
- 在微软工作365天,还你一个我眼中更加真实的微软
- 雷石 扩展文件服务器,雷石发布KTV软硬件新品:智能手表可点歌
- 鸿蒙系统运行内存为啥只有8g,明明8G内存,系统却显示只有4G!为啥会这样?
- Activiti bpmn流程图设计工具 分享
- 写论文的神仙网站推荐!
- 苹果电脑拷贝文件到u盘很慢_防病毒入侵防误删,爱国者这款U盘值得种草
热门文章
- metadata in OData and png
- SAP Fiori Launchpad里加载abap.js的具体代码位置
- Tomcat和搜索引擎网络爬虫的攻防
- 船体6自由度英文名称
- code block怎样导入整个文件夹_按需分配随时可用的在线开发环境:弹性容器+code-server踩坑记...
- mysql 5.5.39 安装_CentOS7.2安装mysql5.5.39
- 没有收到回复的同学注意了,用它一键查询!
- 大闹天竺里的机器人_在《大闹天竺》中哪一位演员是你喜欢的
- 美团flutter_ggtalk 聊聊跨平台:后起之秀 Flutter
- python 列表加入_加入python中的列表列表