乐趣区

关于react.js:微模块前端业务模块化探索拆解巨石应用的又一利器

大家好,我是 Eluxjs 的作者,Eluxjs 是一套基于“微模块 ”和“ 模型驱动”的跨平台、跨框架『同构计划』,欢送理解 …

文前申明,以下推断和论断纯属集体摸索,鉴于自己常识程度所限,舛误在劫难逃,恳请各位大佬不吝赐教 …

什么是前端“微模块”?

Elux 中的『微模块』是指在 Web 前端工程中,将 代码 和相干 资源 依照不同的 业务性能 进行归类和模块化。

依据 业务性能 进行模块化始终以来都是后端的广泛做法,而 Web 前端则通常都是依照 UI 界面的视图区块 View 来进行模块化,这样的模块实际上只是Component 组件,不具备独立自治的能力。究其原因我想是因为在晚期 Web1.0 的时代,前端的职能就是仅仅作为后端 API 数据的一个 Render 渲染器,所以前后端的视线和格局呈现了分化,也导致很多人说前端基本无架构之说。

然而 web 生态倒退到明天,浏览器越来越弱小,赋能越来越多,甚至不亚于一个小型操作系统,这时候的 Web 前端早已不是当初简略的数据渲染器,状态治理、会话维持、数据长久化、文件缓存、通信协议 … 随着 PWA、小程序、快利用的推广,WebAPP 不再是瘦客户端,慢慢成长为大胖小子。

此时的咱们该当跳出“渲染器”的井口,而从一个残缺的软件工程来思考咱们的前端架构,Web 前端不只是一层 View、一个 GUI,咱们须要回归到与后端统一的以 业务畛域 为驱动的模块化视角。

为什么前端须要“微模块”?

  • 从开发角度来说 :咱们须要 高内聚、低耦合 的涣散构造体,而不是牵一发而动全身的巨石利用,这不论是对于开发、保护、还是前期渐进式重构,都至关重要。

    前端 Leader:通过一年多的迭代和人员变动,咱们代码曾经凌乱不堪了,开发越来越吃力,必须要重构,否则玩不上来了!产品经理:嗯,我了解,这外面也有很多是咱们需要变更频繁引起的,我反对你们重构!前端 Leader:感激大佬了解,那新需要先停下来,等咱们重构好了再迭代吧?产品经理:你们重构要多久?前端 Leader:产品这么简单了,预计至多要 3 个月左右吧。产品经理被吓出一身冷汗:大佬,你要 3 天还能够思考,停下来 3 个月预计公司都要关门了...
    前端 Leader:可是产品这么简单,几天工夫实现重构是天方夜谭。产品经理想了想:这样把,我每个迭代少安顿几个需要,这样你们每个月就能够留几天工夫重构了。前端 Leader:这可不是 1 +1= 2 的问题,而是 0 与 1 的问题,大佬你不理解!产品经理:谁说我不理解,你们就不能渐进式重构吗?前端 Leader:...

    此时如果咱们的前端工程是基于“微模块”,一来能够轻松的找到“部分重构”的 边界,二来也能够通过维持“微模块”的对外接口来无极替换。

  • 从产品角度来说 :软件架构永远是服务于业务需要的。咱们心愿咱们的产品能像搭积木一样 按需组合,能够疾速包装出各种灵活多样的套餐,以满足客户越来越精细化的定制需要。

    某个大型利用蕴含 A,B,C,D,E,F,G 等若干性能,原来始终是整体打包发售...
    
    随着用户需要的多样化,有的用户仅须要局部性能,于是聪慧的前端架构师“小李”利用时下风行的微前端技术,将利用拆分成了的 3 个子利用:-【根底利用】蕴含性能:A
    -【子利用 A】蕴含性能:B,C,D
    -【子利用 B】蕴含性能:E,F,G
    
    这样等于有 3 个套餐能够供客户抉择:- 套餐 A:根底利用 + 子利用 A
    - 套餐 B:根底利用 + 子利用 B
    - 套餐 C:根底利用 + 子利用 A + 子利用 B
    
    然而用户的需要越来越精细化,有的须要 ABCD,有的须要 ACEG,有的须要 ABDF...
    而且同一个性能可能还存在需要版本的不同,这让“小李”无可适从。

    当初咱们利用“微模块”来帮忙小李解决问题:

    • 将各种独立的业务性能封装成不同的微模块:A,B,C,D,E,F,G
    • 将各种微模块按需要迭代版本,公布成 NPM 包
    • 某客户须要 A,C1( C 性能的某个版本),E2( E 性能的某个版本),G 性能,咱们独自为该客户创立一个聚合工程分支,装置相应版本的微模块:npm install A C@1 E@2 G

咱们晓得世界上有一款建站神器wordpress,已经号称世界上 50% 的网站都是由它创立的,我认为它的胜利秘诀就是社区模版机制和性能插件化,你要什么性能都总能找到“前端 + 后端”一起打包装置的插件,这也相似于“微模块”的概念。

  • 从工程的角度来说 :“微模块”是跨工程、跨我的项目共享 通用业务代码 的现实决计划,对于跨端、跨平台复用 业务逻辑 尤其有用。

前端“微模块”的划分准则与边界

  • 领有 高内聚、低耦合 的工程构造。
  • 领有 独立自治 的子域逻辑。

从图中能够看到,每个微模块负责定义和保护本人畛域内的事务,并且 麻雀虽小,五脏俱全,领有独立的路由解析、状态治理、数据模型、控制器、视图、组件、资源、业务实体、API 治理等等 … 总之,所有与本人畛域相干的资源都被内聚到了一起。

以下是某巨石利用的 SRC 目录,其特点是以“文件职能 ”作为一级分类、“ 功能模块”作为次级分类:

├─ src
│  ├─ api                 # API 接口治理
│  ├─ assets              # 动态资源文件
│  ├─ components          # 全局组件
│  ├─ config              # 全局配置项
│  ├─ enums               # 我的项目枚举
│  ├─ hooks               # 罕用 Hooks
│  ├─ language            # 语言国际化
│  ├─ layout              # 框架布局
│  ├─ routers             # 路由治理
│  ├─ store               # store
│  ├─ styles              # 全局款式
│  ├─ typings             # 全局 ts 申明
│  ├─ utils               # 工具库
│  ├─ views               # 我的项目所有页面
│  ├─ App.vue             # 入口页面
│  └─ main.ts             # 入口文件

以下是 Elux 中基于微模块的 SRC 目录,其改良是将“功能模块 ”作为一级分类,“ 文件职能”作为次级分类:

src
├── modules
│      ├──  ModuleA
│      │     ├── entities
│      │     ├── assets
│      │     ├── api
│      │     ├── utils
│      │     ├── language
│      │     ├── components
│      │     ├── views
│      │     ├── model.ts
│      │     └── index.ts
│      │ 
│      ├── ModuleB
│      ├── ModuleC

微模块的台前与幕后

前端开发最终出现的是 UI 界面,但这只是表象,撑持 UI 界面渲染和交互的是背地一系列 state、model、controller 等幕后英雄,它们依据本人所属不同畛域被封装在各个 微模块 中,UI 既然与它们唇齿相依,必然也将追随它们内聚在一起。

View 和 Component

实质上说 View 就是一个 Component,但咱们从架构的思维来辨别它们:

  • View:业务视图,它用来体现业务规定与逻辑,通常可能较为独立和残缺的解决某一畛域问题。
  • Component:UI 组件,它用来体现渲染规定与交互逻辑,通常不与具体业务间接相干,可复用在各种不同业务场景中。

所以在“微模块”的架构中,丰富多彩的 UI 界面由一个个繁多职责的 View聚合 而成,每个 View 同样根据本身所解决的畛域问题而被 扩散 在各个微模块中,这外面有几个留神点:

  • 畛域性 :View 被归属到不同 微模块 的准则是其解决的问题畛域,而不是视觉上的几何空间。View 能够在视觉上被拆装、聚合、嵌套,这并不影响它们所属微模块。
  • 完整性:一个 View 通常能解决一个较为独立和残缺的问题,View 与 View 之间是较为涣散的关系,如果 2 个 View 之间分割严密,那就不该当拆分它们。

不以视觉延长和几何空间作为 View 的微模块归属准则 :如下图所示,假如有一个 View 用来展现 用户材料 ,咱们将其放在 UserModule 这个微模块中,称其为UserModule.DetailView,但你发现其中又蕴含一个 该用户发表文章的列表 ,你当然能够把这个列表独自提取进去作为一个新的 View。从视觉上来看,它仿佛和用户材料是连在一起的,仿佛能够和UserModule.DetailView 放在同一个微模块中;但咱们从它解决的问题来看,它属于文章畛域,而与用户畛域关系并不大,所以咱们最好将其放在 ArticleModule 中,称其为ArticleModule.ListView

前端“微模块”的实现计划

  1. 定义和创立微模块,可借助于 Eluxjs 框架,当然你发现了其它框架也能够。
  2. 治理微模块,可借助于 NPM 仓库。
  3. 应用微模块,可借助于打包工具:

    • 动态编译:微模块作为一个 NPM 包被装置到工程中,通过打包工具(如 webpack)失常编译打包即可。这种形式的长处是代码产物失去打包工具的各种去重和优化;毛病是当某个模块更新时,须要整体从新打包。
    • 动静注入:利用Module Federation,将微模块作为子利用独立部署,与时下风行的微前端相似。这种形式的长处是某子利用中的微模块更新时,依赖该微模块的其它利用无需从新编译,刷新浏览器即可动静获取最新模块;毛病是没有打包工具的整体编译与优化,代码和资源容易反复加载或抵触。

微模块 vs 微前端

从本意上来说,微模块只是一种工程构造和模块化计划,而 微前端 只是它的一种利用场景之一。微模块架构不仅能够用来构建简单的单体利用,也能够联合 Module Federation 实现多子利用独立部署的“微前端”。

如果独自就 微模块 + ModuleFederation形式实现的微前端,与传统意义上的 qiankun、icestark 等微前端计划相比,微模块形式 胜在粒度更细、更灵便、更笨重,而 传统形式 则胜在隔离性更好。

想到一个十分形象的比喻:

IFrame vs 微前端 vs 微模块 可类比于 过程 vs 线程 vs 协程

从左至右:越来越轻量化,隔离性逐步变弱,灵活性逐步减少。所以鱼与熊掌不可兼得,具体哪种计划最适宜还得看不同的产品需要。

微模块之间的通信

  • 微模块之间依照某些规定和约定共享同一个 Runtime,强制隔离性较弱,所以它们之间的通信是轻量级的,能够互相援用与调用。
  • 倡议观察者模式,或者应用事件总线模式来放弃微模块之间的涣散关系,这是另一个故事,可参考 Eluxjs 中的ActionBus
  • 微模块 高内聚、低耦合 的划分准则,也意味着微模块之间不会呈现特地简单的互动与交换(互动亲密的微模块该当合并)。

落地与实战

光练不说傻把式,光说不练假把式,这里先把思路概念要说的说完,上面就要开始出实例了。先喝口水,请听下回分解 …🤩🤩急性子也能够间接去 Eluxjs 官网,看看 Demo,不吝赐教 …

退出移动版