前端实现流程图效果_微前端在58商业的探索实践

2023-10-30

导读

商业FE部门分四个业务组,每个组内迭代着多个中后台应用,其中有跨组协作项目。这些项目的特点是UI、UE相似,有较多的组件、逻辑复用场景,技术栈统一为VUE,且项目在不断迭代中。实践微前端架构能提高业务复用性,让各团队更高效的分治项目。

认识

1、简述

微前端是一种方案并非技术,凡是方案都是为适应特定应用场景而生的。微前端的方案类似微服务。它是对大而复杂的应用进行分治,形成各自的单一体系并能通过一套规则联系在一起。

2、特点

微前端架构有以下特点:

a)每个应用都应该是一个完整的沙盒环境,具有独立的开发、测试、部署流程;

b) 多个应用间有相同依赖,可以是模块、可以是路由、可以是业务组件。

3、微前端的应用场景

什么场景可以考虑引用微前端架构?

a)App大且含有多条业逻辑;

b) App是对应到不同的组织架构中;

c)多App间复用逻辑较多且有组合入口出现;

d) 不断迭代的App。

4、范式图

范式图

基座式的微前端架构基本是这样一个简单逻辑组成,基座工程 + 子应用*n。

基于Vue的基座式微前端

1、运行时

有了对微前端的基本认识,我们梳理一下微前端项目在运行时基座与子应用的基本协作流程图:

运行流程图

运行时基座扮演组织者角色,因此基座应用应具备以下特点:

a)按需加载子应用静态资源。

b) 动态注册子应用,并注册子应用关键模块:router、store等。

2、部署时

我们强调子应用的自治,所以子应用部署应该是独立的。

部署流程图

在子应用部署测试和生产环境的时候,都应该将注册信息同步反映到注册表上。注册表模块是解耦基座和子应用的关键,它可以简单的是一个json文件,也可以复杂的是一个子应用管理服务,具体尺度根据业务来衡量。由于我们的子应用数量及部署更新频率还不高,所以先简化注册表模块,以文件加手动更新方式来实现。

注册表模块应该具备以下特点:

a)信息映射:维护全量子应用注册信息,部署时间、版本、静态资源、scopeID等。

b) 动态性,随着不断部署来更新注册表信息。

c)可读性,暴露一个可读的对象或返回对象的自执行函数。

我们期望降低子应用开发者的心智负担,因此后续会将注册表模块服务化并接入上线系统部署钩子,用来管理子应用的信息。

3、开发时

开发时基座与子应用的协作是相对复杂的,并且也包含了运行时的基本逻辑,下文将围绕开发状态下各技术点展开阐述。

注意:此处是给出的是实现思路,具体代码实现细节可自行实现。

1、基座运行环境

开发时基座的运行环境有以下两种方式。

a) 本地服务式 - 子应用开发者将基座拉取到本地,运行到本地服务中。

本地服务式是将基座和子应用放置于子应用开发者本地,由于同处于一个物理空间(磁盘)上,基座可以通过直接引用子应用的构建出口来解决引用热更新问题。这种做法解耦不是很彻底,需要子应用开发者本地启动基座服务,并且没法提供多个子应用同周期协调开发的环境。

b) 集中服务式 - 将基座维护在一个专供开发时应用的服务器容器中。

集中服务式开发体验较好且可以多方协调开发,但需要维护专供开发时的服务节点,并通过网络解决基座与子应用间的资源加载及热更新问题,设计难度较大,由于是网络传输资源所以耗时需要优化。

2、选型

为快速落地框架,我们选用本地服务式方案。下面主要介绍本地服务式各点实现。不过为了后续转集中服务式做准备,这里也会列出一部分实现思路。为提高代码可读性,我们命名基座为Voo。

3、本地服务式

开发时-本地服务式关系图

a) 子应用入口文件的包装

export default (Voo) => {    Voo.Vue.prototype[appName] =vuePrototypeExtension    return { router,  store, App }}释:子应用暴露一个方法并返回router、store、App模块,供基座注册调用和回传基座对象(Voo);原型扩展方法需要增加namespace,方便提供给子应用本身和其他应用复用。

b) 应用路由、数据流的包装

export default [  { 'path': '/', 'redirect': 'home' },  { 'path': 'home', 'name': 'Home', 'component': Home }]释:暴露一个routes数组,供基座动态插入,由于基座与子应用运行在同一个router对象中,所以遵循“以 / 开头的嵌套路径会被当作根路径。这可以让你充分的使用嵌套组件而无须设置嵌套的路径”的规则。export default {state, mutations, actions, modules }释:暴露一个store module对象,供基座动态注册。

c) 应用脚手架的修改

webpackConfig.output.library ='[name]'webpackConfig.output.libraryTarget= 'umd'释:修改输出目标为umd便于runtime、开发时基座的引用webpackConfig.output.jsonpFunction= appName释:如果在同一网页中使用了多个来自不同编译过程(compilation)的webpack runtime,则需要修改此选项。
webpackConfig.plugins.push(newwebpack.BannerPlugin({   'banner': '/* eslint-disable */', 'raw': true}))释:向bundle中追加eslint注释,防止基座eslint校验不通过。'devServer': {    'writeToDisk': true,    before(app,server) {       request.post('http://localhost:7777/__dev_subApp_register', {            'form': {                'id': [appName],                'resourcePath':path.resolve(__dirname, `dist/${[appName]}.js`)            }        }, (error, response, body) => {            if (error) {                console.error('[ error ]请先启动基座工程Voo')            } else {console.log(body)}        })    }}释:将构建目标写入到物理磁盘中,devServer默认是写入在虚拟内存中的,基座工程无法import。

在devServer启动前向基座工程的本地服务发送构建目标的物理路径,注意:此处路径是磁盘绝对路径,是可以通过import载入的。` 7777`是基座工程固定的端口,__dev_subApp_register是基座工程固定的子应用注册路由。

d) 基座脚手架的修改

'devServer': {    before(app, server) {                                    app.post('/__dev_subApp_register',(req, res) => {            const params =Object.assign(req.query, req.body)            const devSubAppRegisterInfo = `/* eslint-disable */export default (regiestSubApp, opts) => {import('${params.resourcePath}').then((res) => {    const subApp =res.default(opts)    regiestSubApp({        id: '${params.id}',      subApp    })})}`           fs.writeFileSync(`${__dirname}/__dev__subApp_register_info.js`,devSubAppRegisterInfo)            res.json({ 'code': 0, 'message': '开发时注册成功' })        })    }}释:编写子应用注册接口,在接收到子应用的注册请求后,将基座引用逻辑写入到__dev__subApp_register_info.js。

4、集中服务式

0650cc69ba9300383c1faa702152d9bd.png

开发时-集中服务式关系图

集中服务式,设计中需要注意以下几个点:

a) 配置服务,将nginx反向代理到基座服务;

b) 基座服务提供与子应用交互的接口,此处我们选用webpack的devServer进行描述。

1、在基座根目录创建 /subApps。

2、在before编写注册接口。当注册请求进入后将子应用/dist文件写入到/subApps中,如果/dist文件太大,可以采用压缩解压,如果子应用文件夹存在则更新。

c) /subApps目录结构

├── App.vue

├── main.js

├── ...其他目录

├── subApps

├── appA

│ ├── appA.js

│ ├── ...各chunk

│ └── appA.css

├── appB

│ ├── appB.js

│ ├── ...各chunk

│ └── appB.css

├── ...其他subApp

└── index.js

d) 动态读取 /subApps下所有文件,暴露出去

const requireSubApps =require.context('./', true, /.js|.css$/)export defaultrequireSubApps.keys().map((fileName) => {  return requireSubApps(fileName).default})

e) 热更新

由于基座入口引用的是基座服务本地文件,所以,我们只需要在子应用代码发生改变时触发基座注册接口就行。实现如下:

before(app, server) {registe();app.post('/__dev_update', (req, res) => {registe()})}

由于需要监听main.js入口以内所有模块的变化,所以将监听逻辑放到main.div.js,代码实现如下:

if (module.hot) {  module.hot.accept('./main.js', ()=> {    fetch('/__dev_update')   });}同时修改dev和prod环境下的打包入口。configureWebpack(webpackConfig){        webpackConfig.entry =process.env.NODE_ENV === 'development'      ? { [appName]: ['./main.dev.js'] }      : { [appName]: ['./src/main.js'] }}

f) 优化

热更新时按需上传[hash].hot-updage.json,降低网络耗时。

4、联调|测试

联调阶段:需要关注的问题是mock、proxy,这两点都可以沿用spa应用原有的开发方案。

测试阶段:基座和子应用都是通过上线系统管理所以可以利用其提供的测试环境。关键需要注意一下注册表的测试环境提供。

5、关键点

1、子应用生命周期

由于子应用会被当做一个路由组件注册到基座中,所以子应用可以利用其root组件的vue生命周期。

2、沙盒化router、store、css、vue原型扩展

路由,将子应用注册到其ID为根的路由上,并将其暴露的路由注册到children上。

Voo.$router.addRoutes([      {'path': `/${ subApp.id}`, 'children': subApp.router,     'component': subApp.App}])

Store module, 动态注册的store module本来就是具有作用域的,依照vuex文档即可。

Voo.$store.registerModule(id,{ 'namespaced': true, ...subApp.store })。

Css module, 通过postcss给子应用追加作用空间。

constpostcssNamespaceGlobal = postcss.plugin('postcss-namespace-global', ({namespace= ''}) => (root) => {    root.walk((node) => {        if(node.selector){            node.selector =(node.selector.split(',').map((selector) => {                if(selector.match(/^(s*)(html|body)(s*)$/)) {                    return selector                }                return `${namespace}${selector}`            }).join(','))        }    })})module.exports ={'plugins': [postcssNamespaceGlobal({namespace: `.${appName}`})]}

Vue原型扩展,给子应用用到的原型扩展方法规定到其ID对应的对象中。

3、 复用层

复用层比较复杂。有着较多种类的使用场景。下面分析一下在微前端架构中会出现哪些复用的东西,怎么去选型及管理。总体来说复用层的内容可以分为两种:

a) 类性质。调用时创建实例,因此runtime时互不干扰。但要根据是否需要锁定版本来确定复用内容的管理方案。

需要锁定版本,采用npm scope,管理在公司内部的npm服务上。注意:要规范好子应用npm安装重复问题。

不需要锁定版本,采用全局注册,例如全局注册的业务组件。注意:子应用注册需要scope。

b) 函数性质。调用和执行是同一组代码,这种复用内容性质是脆弱的。所以对设计者要求较高,且迭代应向下兼容。

4、 复用内容

a) UI组件库等第三方依赖:类性质,在基座中规定并回传给子应用。

b) ajax库及统一接口处理:函数性质,在基座中规定并回传给子应用。

c) 业务类组件:类、函数性质,采用全局注册回传给子应用。

d) 子应用特色业务组件:如chart、workflow等:类性质。托管npm。

5、 子应用拆分粒度

太粗、太细的子应用粒度都不利于App的维护,所以要根据业务和组织架构合理拆分。我们可以参考两个原则:与服务对应形成前端微服务化、与团队对应。

6、Q/A

Q: 在“开发时-本地服务式”中,既然是在本地启动基座和子应用,为什么基座不直接import子应用入口而是import子应用的bundle。

A: 因为框架的目的是尽量解耦应用,如果直接import子应用入口,那么子应用就相当于是在基座环境中构建的。这样即增加了子应用开发、生产两个环境的构建差异,又限定了子应用的开发依赖。

总结和展望

1、总体来说这一套架构解决了以下问题:

a) 解决业务繁多的项目分治;

b) 多团队协作开发,且团队内项目自治;

c) 对敏捷迭代的项目构建良好的基础;

d) 子应用开发者无需关注基座及其历史子应用业务,直接依赖基座预览开发效果,体验提升;

e) 子应用开发几乎无异于SPA,无学习门槛。

2、思考

此次是我们在微前端道路上的初探,输出也只是基于Vue技术栈的单一形态。所以围绕微前端概念我们还有很多事情要做。

在设计此架构前我也调研过很多应用微前端的文章,得出的结论竟然让我自己觉得有些矛盾。微前端的理念是为了解耦,但是往往很多使用者还希望通过微前端实现业务的高度复用。那么矛盾来了,复用就伴随着耦合。所以说没有银弹,我们要做的是解耦子应用的同时,尽可能的对复用层进行分类管理,结合业务场景定制化适合的微前端架构。

3、优势

a) 基于同技术栈的微前端,可以快速抽离复用层并无侵入性的投入使用;

b)基于vue,有效的利用了store、router动态注册特性,贴合58商业目前技术栈及存量项目;

c) 在本地式开发流程中子应用脚手架和基座脚手架之间的合作可以提供稳定的热更新方案。

4、规划

将目光放的再长远一些,那么我们还应该做以下规划。

d) 子应用跨技术栈,解耦更彻底,让微前端能应用到更大的聚合App上和组织架构中,当然复用层将变为一个挑战。

e) 为增加开发体验,基座采用集中服务化方案,例如:有子应用需求接入时就将基座部署到沙箱节点上,或者可以将基座应用设计为服务端渲染并提供一套开发专用带权限的接口。这样就可以解决专门为开发提供服务的问题,同时还可以封装注册表相关逻辑以管理子应用。

f) 注册表模块服务化,此项主要是为规范工程化管理。

作者简介:

张军,58集团前端工程师。

参考文献:

康威定律:http://www.melconway.com/Home/Conways_Law.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

前端实现流程图效果_微前端在58商业的探索实践 的相关文章

  • Java 一个简单的线程定时 (守护线程)

    守护线程也叫精灵线程 当程序只剩下守护线程的时候 程序就会退出 守护线程的作用类似在后台静默执行 比如JVM的垃圾回收机制 应用场景 实例化socket连接时需要判断连接成功与否 一般来说 如果连接成功就是片刻的事 如果不成功的话jvm会判
  • python如何读取文件夹下的子文件夹

    import os 创建文件夹 def mkdir path if os path exists path False os mkdir path else pass 根目录 root path C Users man wang Deskt
  • Js(一)Error [ERR_MODULE_NOT_FOUND]: Cannot find package ‘uuid‘ imported

    前言 本章主要讲述js报错之 Error ERR MODULE NOT FOUND Cannot find package uuid imported from xxx的解决 一 问题描述 问题 顾名思义 根据错误信息翻译 该文件路径下找不
  • 【华为OD机试真题 JS】贪吃蛇

    标题 贪吃蛇 时间限制 1秒 内存限制 262144K 语言限制 不限 贪吃蛇是一个经典游戏 蛇的身体由若干方格连接而成 身体随蛇头移动 蛇头触碰到食物时 蛇的长度会增加一格 蛇头和身体的任一方格或者游戏版图边界碰撞时 游戏结束 下面让我们
  • 一个java源文件中只能有一个public类

    java文件只有一个public类 1 每个编译单元 文件 只能有一个public 类 这么做的意思是 每个编译单元只能有一个公开的接口 而这个接口就由其public 类来表示 2 java程序的入口是main方法 所以被定为public的
  • Spring Cloud Sleuth 2.0概要使用说明

    Sleuth Zipkin和Brave 先说一下Spring Cloud Sleuth Zipkin和Brave三者之间的关系 首先 对于Spring Cloud Sleuth来说 大家或许接触的比较多 它是Spring框架家族 在这里为什
  • maven可执行jar及加载文件

    1 使用maven assembly plugin插件打包 1 1 代码 地址 https gitee com xixingzhe2 learn tree master jar exe test demo pom xml
  • CommonJS, AMD 和 RequireJS之间的关系(转载)

    先说说CommonJS CommonJS 大家是不是觉得JavaScript仅仅是一个客户端的编译语言 其实JavaScript设计之初不仅仅是针对客户端设计的语言 后来只是由于Web的迅速流行 加之Netscape和微软之间之争过早的将J
  • 每年等额本金,计算复利的方法

    最近正在学理财 就顺手写了个复利的计算方法 小记一下 public class CompoundInterestCalculation public static void main String args 计算公式V P 1 i 1 i
  • springmvc+mybatis+maven项目框架搭建

    项目的目录 1 配置web xml
  • Unity游戏开发之游戏动画(Unity动画系统)

    文章目录 Unity动画系统 动画片段 Animation Clip 在Unity中制作动画 动画复用 替身系统 Avatar Animator 组件 Animator Component 动画状态机 Animator Controller
  • vue axios 上传文件 xhr.addEventListener is not a function(…) 问题修复 不用卸载 mockjs

    vue axios 上传文件 xhr addEventListener is not a function 问题修复 不用卸载 mockjs 无法上传的原因是因为 mockjs 将 axios请求 的 XhrRequest 修改成了Mock
  • 解决”/bin/rm: Argument list too long”删除文件错误

    执行rm后出现 bin rm Argument list too long 报错 应该是目录中的文件太多 处理时间太长导致的 ls xargs n 10000 rm rf 通过xargs命令 将文件10个分成一组 然后分批删除 这样就不会出
  • 5.3 分布式 Git - 维护项目

    5 3 分布式 Git 维护项目 版本说明 版本 作者 日期 备注 0 1 loon 2019 3 28 初稿 目录 文章目录 5 3 分布式 Git 维护项目 版本说明 目录 维护项目 1 在特性分支中工作 2 应用来自邮件的补丁 3 使
  • 字节跳动视频编解码面经

    引言 本文主要是记录一下面试字节跳动的经历 三四月份投了字节跳动的实习 图形图像岗位 然后hr打电话过来问了一下会不会opengl c shador 当时只会一点c 其他两个都不会 也就直接被拒了 七月初内推了字节跳动的提前批 因为内推没有
  • MybatisPlus中的的BaseMapper接口的认识

    接上一篇博客 还是在接手别人的代码的时候 看到了如下的代码 当时自己只是查询了以下wrapper构造器的用法 这里其实还有一个MybatisPlus中的BaseMapper接口的用法 上面这句代码的意思其实就相当于 SELECT COUNT
  • Spring boot+Vue3博客平台:文章发布与编辑功能的技术实现

    本文将详细介绍如何实现一个博客平台中的文章发布与编辑功能 包括前端的Vue组件设计和后端的Spring Boot接口实现 在阅读本文后 您将了解如何设计和实现高效 易用的文章发布与编辑功能 一 发布文章 设计思路 在设计文章发布功能时 我们
  • 【Python脚本进阶】1.2、python脚本基础知识(中)

    目录 一 异常处理 1 1 简介 1 2 示例 二 函数 2 1 简介 2 2 示例1 2 3 示例2 三 迭代 3 1 简介 3 2 示例1 3 3 示例2 3 4 示例3 3 5 示例4 我所用为python3 pycharm 一 异常
  • 利用FFmpeg进行视频文件进行分割

    利用FFMPEG命令进行文件分割 ffmpeg ss 00 00 00 i input mp4 c copy t 60 output mp4 ss 表示视频分割的起始时间 t 表示分割时长 同时也可以用 00 01 00表示 注意 ss 要

随机推荐

  • 爬虫之模拟登录

    方法一selenium from selenium import webdriver from selenium webdriver common keys import Keys import time driver webdriver
  • unity 纯C# android 端PC端 热更方案研究

    首先说明我们的使用场景 不涉及IOS只是android端 最好实现全部更新 项目只有热更空壳 其他逻辑代码场景全部热更 1 首先代码热更 DLL在转为btyes 2 场景热更 3 资源热更 4 加载后代码和资源关联关系 首先说下实现 全部是
  • 软件的静态测试和动态测试【软件测试分类】

    软件的静态测试和动态测试 软件测试一般分为静态测试和动态测试 1 静态测试 静态测试时指测试程序不在机器上运行 而是采用人工检测和计算机辅助静态分析的手段对程序进行检测 静态分析中进行人工测试的主要方法有桌前检查 Desk Checking
  • Latex多个子图进行组合

    20210413 0 引言 最近在用latex写论文 这种半路出家的 很多内容都要现场查 没办法 谁让平时用的少呢 本篇文章针对的内容是放置子图 也就是在文章中放置多个图片 这几个图片本质上同属于一个大图片 假设是3个 那就是3个子图 横向
  • websocket 发送内容长度设置(默认8192字节)

    https blog csdn net zhaotian19871204 article details 84801915
  • 2020年7月6日-7月26日(算每天1小时,合计215小时,剩9785小时)

    主要进行了二次开发 熟练度提高 新东西不多 大象无形也看过一些 但是没定计划磨洋工了 就算每天1小时 合计215小时 剩9785小时
  • Centos7下安装无头浏览器(headless Chrome) puppeteer 截图

    目录 第一步 添加repo源 第二步 安装chrome 第三步 安装驱动chromedriver puppeteer 是一个基于nodejs 调用chrome进行网页截图的库 chrome并不需要UI界面 一般访问网页 模拟点击操作等 在自
  • stable diffusion实践操作-大模型介绍-SDXL1大模型

    系列文章目录 大家移步下面链接中 里面详细介绍了stable diffusion的原理 操作等 本文只是下面系列文章的一个写作模板 stable diffusion实践操作 提示 写完文章后 目录可以自动生成 如何生成可参考右边的帮助文档
  • 在Unity中使用OOP

    以Main Camera 为例 所有组件 Transform Camera Audio Listener Scirpts 在Unity中都是类 单击Play按钮时 这些组件都会成为内存中的对象 1 获取组件 使用GameObject类的Ge
  • Scala简介与安装

    目录 一 Scala简介 一 Scala概述 二 函数式编程 三 Scala特性 1 一切都是对象 2 一切都是函数 3 一切都是表达式 四 在线运行Scala 二 选择Scala版本 三 Windows上安装Scala 一 到Scala官
  • /var/run/yum.pid 已被锁定,PID 为 xxxx 的另一个程序正在运行的问题解决

    问题 解决 root localhost rm f var run yum pid
  • openGL阴影实现(软阴影)

    openGL系列文章目录 文章目录 openGL系列文章目录 前言 一 现实世界中的柔和阴影 二 生成柔和阴影 百分比邻近滤波 PCF 1 引入库 代码 运行效果 源码下载 前言 目前我们所展示的阴影生成方法都仅限于生成硬阴影 即带锐边的阴
  • R语言绘制箱线图并添加相关显著性

    箱线图 又称盒须图 通过绘制连续型变量的五数总括 即最小值 下四分位数 第25百分位数 中位数 第50百分位数 上四分位数 第75百分位数 以及最大值 描述了连续型变量 的分布 它主要用于反映原始数据分布的特征 还可以进行多组数据分布特征的
  • 网络爬虫CSS选择器详细讲解

    网络爬虫CSS选择器详细讲解 前言 使用步骤 1 解析的HTML代码 2 逐层选择节点 3 获取文本 string和get text 4 获取节点的属性值 5 选择单个和多个节点 6 通过class和id选择节点 class id 7 选择
  • PLY 模型文件简析

    PLY 模型文件简析 参考链接 wiki需要FFFQQQ TIPS 主要是一些英文的简析 但是一句话 网上讲的不清楚 特此说明property list uchar int vertex indices 应该是面的顶点数量使用 uchar
  • 苹果 / Apple iCloud 教育版 200GB 云盘账号自助注册教程

    因为教育机构的管理式 Apple ID 可以单点登录 SSO 登录 因此自助注册苹果 Apple iCloud 教育版 200GB 云盘账号就可以顺利进行了 自助注册苹果 Apple iCloud 教育版 200GB 云盘账号分为两步 自助
  • STM32速成笔记—IWDG

    文章目录 一 IWDG简介 二 STM32的IWDG 2 1 STM32的IWDG简介 2 2 喂狗 2 3 IWDG框图 三 IWDG配置步骤 四 IWDG配置程序 4 1 IWDG初始化程序 4 2 喂狗 五 应用实例 一 IWDG简介
  • 恶意代码的分类

    计算机病毒 注意这是狭义上的定义 和网络蠕虫都可以进行自我传播 那他们的本质区别是什么 计算机病毒 一组能进行自我传播 需要用户干预来触发执行的破坏性程序或代码 网络蠕虫 一组能进行自我传播 不需要用户干预即可触发执行的破坏性程序或代码 1
  • [007]爬虫系列

    一 找到首次加载的js 用源码面板打开 二 第一行打断点 格式化 三 刷新网页 成功断点 执行hook 代码如下 function use strict var cookieTemp Object defineProperty docume
  • 前端实现流程图效果_微前端在58商业的探索实践

    导读 商业FE部门分四个业务组 每个组内迭代着多个中后台应用 其中有跨组协作项目 这些项目的特点是UI UE相似 有较多的组件 逻辑复用场景 技术栈统一为VUE 且项目在不断迭代中 实践微前端架构能提高业务复用性 让各团队更高效的分治项目