别急着上微前端:从 6 个场景倒推 Module Federation 2.0 与 qiankun 2.x 的选型

别急着上微前端:从 6 个场景倒推 Module Federation 2.0 与 qiankun 2.x 的选型

一句话结论:微前端买的是「独立部署」,不是代码复用。

一、从一个卡住的发布日说起

周四下午三点,本周唯一的发布窗口。订单组只改了购物车的一行文案,财务组等着这个窗口发报表修复,客服组因为全量回归没跑完不敢合并。三个团队、一个仓库,谁改代码都得等所有人一起上线。

于是有人提:「上微前端吧。」先别急着挑框架。这里被卡住的是发布流水线 ,不是代码结构。微前端这套东西从 micro-frontends.org 的定义开始,说的就是「让多个团队能独立交付特性」的技术、策略与方法------关键词是多个团队,qiankun 官方文档也直接引用了这个定义。

结论先放三句,后面所有内容都是展开它们:

  1. 独立部署是唯一的硬收益,其余五条常见优点都是它的派生;
  2. Module Federation 在模块级组合,qiankun 在应用级组合,这个粒度差异决定后面所有取舍;
  3. 如果现在没有两个以上团队被同一条发布流水线互相卡住,先别上。收益是零,成本却是实打实的:多套构建、运行时组合、跨应用排障与版本契约。

一句判据:能不能指出两个「上个月因为对方没发版而推迟」的需求?指不出来,就还没到该动架构的时候。

顺手排除三个常见误解:把公共组件抽成 npm 包,那是构建时组合;给页面套一层 iframe,qiankun 官方专门写过 Why Not Iframe;「前端版微服务」也不成立,后端是不是微服务并不构成前置条件,对着一个单体 API 照样能跑微前端。

二、先定边界:你到底在哪一层做组合

先问「组合发生在哪」,再问「用哪个库」。很多选型争论其实是没对齐这一层。

组合位置 发生在 代表做法 独立部署
构建时 CI / 打包 monorepo 内部包、npm 私服 否,随主应用一起发版
浏览器运行时 页面加载后 Module Federation、qiankun、single-spa 是,各应用各自发版
服务端 响应拼装 边缘包含、模板组合、SSR 片段 是,但依赖服务端路由
浏览器硬隔离 iframe 边界 原生 iframe、Web Components 是,隔离最强、体验最割裂

本文聚焦第二行:用户最常拿来对比、也最容易混为一谈的 Module Federation 与 qiankun。

三、两条路线的分野:模块级与应用级

一句话区分:MF 是「把别人仓库里的模块 import 进来」,qiankun 是「在页面里隔出一个带围墙的房间」。

维度 Module Federation 2.0 qiankun 2.x(生产稳定线)
组合粒度 组件、模块、路由页 整个子应用
入口形态 远端清单 mf-manifest.json 或 remoteEntry.js 一个 HTML 地址(HTML entry)
如何接入 import('shop/Cart'),像引本地模块 注册路由规则,框架注入 HTML 与生命周期
依赖处理 主动共享,singleton 保证只加载一份 React 不共享运行时,各子应用自带依赖
隔离能力 无 JS 沙箱,共用一个全局环境 Proxy 沙箱 + 样式隔离
技术栈自由度 形式上自由,实际要求 shared 版本收敛 高,React / Vue / Angular / jQuery 可同页
主要成本 共享依赖版本治理、远端不可达的降级 全局副作用、样式污染、路由与鉴权传递

最容易选错的一处 :「技术栈无关」在两条路线里含义完全不同。qiankun 允许同一页面跑两个 React 大版本;MF 的 shared 恰恰要求大家协商出同一个版本。如果真实需求是「老 AngularJS 与新 Vue 并存」,别拿 MF 当隔离方案。

所以选型的第一性问题不是「哪个更火」,而是:要拆的是模块,还是整个应用?

四、Module Federation 2.0:把跨仓库复用推迟到运行时

MF 的原始定义是「多个 JavaScript 应用之间共享代码与资源」。webpack 5 内置的是基础能力,而 Module Federation 2.0 是 module-federation/core 这套独立实现,在此之上加了 Federation Runtime、Runtime Plugins、Manifest、动态类型提示与 Chrome Devtool。

4.1 三个角色,四步运行时

  • Host(消费者):页面主体,声明要用哪些远端。
  • Remote(生产者) :独立构建、独立部署,用 exposes 导出模块。
  • Shared(共享依赖):双方约定复用的库,例如 React、设计系统。

运行时四步:拉取远端清单 → 建立 share scope 并协商版本 → 按需下载远端 chunk → 执行模块并接入模块图。

关键在于「推迟」:构建期只约定名字与共享依赖,版本协商与加载全部留到运行时,远端重新部署后 host 无需重新构建 。这就是独立部署能落地的技术原因。2.0 推荐用 mf-manifest.json 而不是 remoteEntry.js,前者额外提供动态类型提示、资源预加载与 Devtool 调试能力。

4.2 最小可跑配置(Webpack 5 / Rspack)

版本口径:@module-federation/enhanced 2.9.3(2026-11-03)、webpack 5.112.2、React 19。这个包在 exports 中提供 .、./webpack、./rspack、./runtime 四个入口,Rspack 只需把 ./webpack 换成 ./rspack,其余一致。

远端 webpack.config.js:

js 复制代码
// remote / webpack.config.js
const { ModuleFederationPlugin } = require('@module-federation/enhanced/webpack');

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'shop',
      // 2.0 会同时生成 mf-manifest.json,供 host 使用
      filename: 'remoteEntry.js',
      exposes: {
        './Cart': './src/Cart.tsx',
        './ProductCard': './src/ProductCard.tsx',
      },
      shared: {
        react: { singleton: true, requiredVersion: '^19.0.0' },
        'react-dom': { singleton: true, requiredVersion: '^19.0.0' },
      },
    }),
  ],
};

宿主侧配置与消费:

jsx 复制代码
// host / webpack.config.js
const { ModuleFederationPlugin } = require('@module-federation/enhanced/webpack');

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'portal',
      remotes: {
        // 值 = 远端名@入口地址;键 shop 是 import 时用的别名
        shop: 'shop@https://shop.example.com/mf-manifest.json',
      },
      shared: {
        react: { singleton: true, requiredVersion: '^19.0.0' },
        'react-dom': { singleton: true, requiredVersion: '^19.0.0' },
      },
    }),
  ],
};

// host / src/routes.tsx ------ 远端模块像普通模块一样被 import
import { lazy, Suspense } from 'react';
const Cart = lazy(() => import('shop/Cart'));

export function CartRoute() {
  return (
    <Suspense fallback={<div>购物车加载中</div>}>
      <Cart />   {/* 远端不可达时这里需要 ErrorBoundary 兜底 */}
    </Suspense>
  );
}

4.3 Vite 8 项目要多做两件事

@module-federation/vite 1.23.11 用 federation() 插件。Vite 的 remote 是原生 ESM,host 的 remotes 必须显式写 type: 'module',省略时插件会退回旧的 var 格式并给出警告。

ts 复制代码
// vite.config.ts(远端)
import { defineConfig } from 'vite';
import { federation } from '@module-federation/vite';

export default defineConfig({
  plugins: [
    federation({
      name: 'shop',
      filename: 'remoteEntry.js',
      exposes: { './Cart': './src/Cart.vue' },
      shared: ['vue'],
    }),
  ],
  // 远端必须声明 origin,否则 host 解析不到 chunk
  server: { origin: 'http://localhost:3001' },
});

// host 侧 remotes 示例:
// shop: { type: 'module', name: 'shop',
//         entry: 'http://localhost:3001/remoteEntry.js',
//         entryGlobalName: 'shop', shareScope: 'default' }

4.4 三个高频坑,以及怎么验自己没踩

现象 原因 验收动作
页面偶发双 React、Hook 报错 singleton: true 只保证同一 share scope 里加载一份,不替代版本纪律;requiredVersion 不满足时官方行为是「警告 + 使用现有最小版本」 用 2.0 的 Chrome 扩展 Shared 面板看实际生效版本与依赖图
远端一挂整块区域白屏 manifest 404 时远端不是「慢」,而是不可用;MF 也不提供 JS 沙箱,远端与 host 共用 window 断掉远端 CDN 演练一次,页面应显示降级 UI 而非白屏
dev 正常、生产构建样式丢失 Vite 插件的生产构建 CSS 处理是高频问题;codeSplitting 关掉会破坏 bootstrap 顺序 必须跑 production build 验收 CSS 与 chunk 路径,不能只看 dev server

再补两条容易忽略的:eager: true 会把共享依赖打进入口,官方明确标注 rarely recommended;远端必须配 CORS 与稳定的 CDN 路径,host 侧要写 ErrorBoundary 与超时降级。

4.5 2.0 稳定版到底多了什么

官方博客标注的稳定日期是 2026-02-10 (npm 上 2.0.0 为 02-06)。这一版无破坏性变更,library.type 默认值由 var 改为 global,runtimePlugins 开始支持传参。对选型真正有用的增量是三项:

  • 共享依赖 Tree Shaking :runtime-infer 开箱即用,server-calc 由服务端汇总全局用量。官方以 Ant Design 为例,只用 Badge / Button / List 时共享包从 1404.2 KB 降到 344.0 KB,减少约 75.5%。
  • SSR 与同构数据预取 :在 Modern.js 中可用,提供统一的 prefetch / cache API,补的是首屏与 SEO 那块短板。
  • 可观测性:manifest 生成与 AsyncStartUp 用 Rust 重写;Side Effect Scanner 静态扫描产物里的全局变量、事件监听与 CSS 影响范围;Chrome 扩展可直接看到 React 是否共享成功。

生态上 2.0 已经解绑打包器:Webpack / Rspack / Rollup / Rolldown,Rsbuild / Vite / Metro,Modern.js / Next.js / Rspress / Rslib / Storybook 都在官方列表里,也支持 Node.js 侧的 SSR、BFF。

五、qiankun 2.x:把整个应用装进带围墙的房间

qiankun 基于 single-spa,把「多应用同页」收敛成几个 API。三个关键词:HTML entry、生命周期、隔离。

5.1 原理:URL 就是集成契约

主应用只给一个 URL,不维护资源清单:qiankun 抓取子应用 HTML,解析其中的 script / style,注入容器节点。子应用导出 bootstrap、mount、unmount 三个函数,路由切换时由框架调用。这个契约简单到 jQuery 页面也能接入------把三个函数挂到 window 上就行。

隔离靠 Proxy 沙箱:把子应用对 window 的写入、定时器与事件监听圈在自己的代理对象里,卸载时统一回收。所以它比 MF 重,但能容纳完全异构的老系统。

5.2 最小可跑配置(2.10.16)

版本口径:npm i qiankun 当前装到的是 2.10.16(2023-11-15) ;3.0 仍是 RC,需要显式 npm i qiankun@rc。下面按 2.x 稳定线写,2.x 文档在 v2.qiankun.umijs.org。

主应用 main.js:

js 复制代码
import { registerMicroApps, start } from 'qiankun';

registerMicroApps([
  { name: 'shop',    entry: '//localhost:3001',
    container: '#subapp', activeRule: '/shop' },
  { name: 'finance', entry: '//localhost:3002',
    container: '#subapp', activeRule: '/finance' },
]);

start({ prefetch: 'all' });   // 2.x:空闲时预取子应用资源

子应用 public-path.js 与入口:

js 复制代码
// src/public-path.js ------ 必须在入口最顶部引入
if (window.__POWERED_BY_QIANKUN__) {
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

// src/main.jsx(React 18,Vue 同理)
import './public-path';
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

let root = null;
function render(props) {
  const box = props.container
    ? props.container.querySelector('#root')
    : document.querySelector('#root');
  root = createRoot(box);
  // 作为子应用时路由 base 必须与 activeRule 一致
  root.render(<App basename={window.__POWERED_BY_QIANKUN__ ? '/shop' : '/'} />);
}

if (!window.__POWERED_BY_QIANKUN__) render({});      // 独立运行
export async function bootstrap() {}
export async function mount(props) { render(props); }
export async function unmount() { root.unmount(); root = null; }

子应用 webpack 输出配置:

js 复制代码
const name = require('./package.json').name;
module.exports = {
  output: {
    library: name + '-[name]',
    libraryTarget: 'umd',
    // webpack 5 用 chunkLoadingGlobal;webpack 4 才是 jsonpFunction
    chunkLoadingGlobal: 'webpackJsonp_' + name,
    globalObject: 'window',
  },
  devServer: {
    headers: { 'Access-Control-Allow-Origin': '*' },  // 主应用跨域抓 HTML
    historyApiFallback: true,
  },
};

5.3 四个高频坑

现象 原因 验收动作
子应用 chunk 404 静态资源未开 CORS,或没注入 public path 子应用既能独立访问、也能嵌入访问
刷新子路由跳回主应用 路由 base 与 activeRule 不一致;根 DOM id 没限制在 container 内查找 直接刷新 /shop/detail 应停在子应用
来回切换后页面越来越卡 unmount 没真正释放实例与定时器,二次进入重复挂载 来回切换十次,全局定时器与 DOM 节点数量不增长
弹窗或全局 reset 串样式 2.x 的样式隔离不是硬边界,第三方库注入的样式仍可能越界 把弹窗挂到子应用容器内,验收全局 reset 不污染主应用

照抄旧教程还容易踩一个:webpack 5 要写 chunkLoadingGlobal,jsonpFunction 是 webpack 4 的写法。

5.4 3.0 先放观察清单,别进生产计划

3.0 把能力拆成 @qiankunjs/loader、@qiankunjs/sandbox、@qiankunjs/shared 等包:HTML entry 改为流式注入、解析时自动预加载脚本与样式,JS 沙箱默认开启,提供 React / Vue 的 <MicroApp /> 绑定与 webpack / Vite 插件,样式隔离改成基于 CSS @scope 的 opt-in 能力。

注意两个边界:不支持 @scope 的浏览器会整块丢弃 这条 at-rule,需要普通回退样式;原生 ESM 沙箱依赖动态注入 import maps,官方在 Firefox 上标注了预期失败。这些都是 RC 线当前的实现,不要写成 2.x 已经有的能力。

六、六个场景:什么信号出现时才该动架构

下面每个场景都按「信号 → 为什么 → 路线 → 一条可验证判据」写。判据比结论重要,因为它能在你们自己的环境里被证伪。

S1 多业务域的企业中后台

  • 信号:一个门户里挂着订单、财务、客服、报表,三支以上团队各自排期。
  • 为什么:域边界天然清晰,发布互相卡是最贵的成本。
  • 路线 :MF(同技术栈)或 qiankun(异构)。用 monorepo 分目录不解决发布耦合,exposes 或 registerMicroApps 才是把交付边界切开的那一刀。
  • 判据:能否指出两个上个月因对方未发版而推迟的需求。

S2 遗留系统渐进迁移

  • 信号:jQuery / AngularJS 老系统要换 Vue 或 React,但不能停机重写。
  • 为什么:按路由一颗颗替换,老页面继续跑,新页面用新栈,随时可回退。
  • 路线:qiankun 应用级。HTML entry 对老页面侵入最小,子应用连打包器都不用换;MF 要求 shared 协商,老系统很难配合。
  • 判据:新旧页面共存期间,登录态与权限是否都能正确传递。

S3 多团队独立发布

  • 信号:每周只有一个发布窗口,任何一个团队改一行都要全量回归。
  • 为什么:把发布流水线拆开,回归范围收敛到自己的域。
  • 路线:MF + manifest。远端入口是一个 URL,可以做灰度切远端版本,回滚时换回旧 manifest 即可,不用重建主站。
  • 判据:单个团队发版时,其他团队是否还需要陪跑回归。

S4 并购或跨部门系统集成

  • 信号:两套现成系统要出现在同一导航下,技术栈与框架版本都不同。
  • 为什么:不改对方代码,只补生命周期与隔离,接入成本最低。
  • 路线:qiankun;隔离要求极高时直接用 iframe 换硬边界。
  • 判据 :两个系统的全局 CSS 与 window 变量是否互不干扰。

S5 大型组件 / 营销位按域发布

  • 信号:设计系统、活动页、插件市场由独立团队维护,主站不希望跟着重发。
  • 为什么:远端重新部署后主站无需重新构建,配合 manifest 可做 A/B 与按时段切换。
  • 路线:MF 2.0,模块粒度最贴合;这类需求用应用级方案等于为了挂一个 Banner 起一个子应用。
  • 判据:远端发版后,主站是否真的不需要重新构建。

S6 多租户与白标投放

  • 信号:不同客户看到不同功能模块,且要按租户灰度。
  • 为什么:运行时按租户解析不同的 remote 地址,不必为每个客户打一份包。
  • 路线:MF + runtime plugin / manifest 动态解析。注意这要求把「租户 → 模块版本」做成可下发配置,并给解析失败留默认模块。
  • 判据:切换租户配置后,不重新构建就能换掉模块。

六个场景里有一条共同的技术底座:入口必须是一个可版本化、可回滚的 URL。做不到这点,独立部署就只是把发布窗口从主站挪到了 CDN。

七、三个反例:这时候请叫停

  • 单团队小产品:一个仓库、一个团队、发布从来不排队。没有协作问题可解,只多几套构建和一份运行时,多份框架运行时还会重复下发到浏览器。
  • 只想复用几个组件:诉求是「别把 Button 复制三遍」。内部 npm 包或 monorepo 共享包足够,构建时确定版本,类型、Tree Shaking、调试都更省事;运行时协商反而让这三项变差。
  • 首屏性能是硬指标:LCP 有硬约束、弱网用户占比高、页面几乎无交互。运行时组合带来额外请求瀑布与重复依赖,收益抵不过延迟。

更简单的替代方案:

如果真实需求是 更简单做法 为什么
复用组件 / 设计系统 monorepo 内部包 + 私服 npm 构建时确定版本,类型、Tree Shaking、调试都最省事
彻底隔离一个第三方页面 原生 iframe 硬隔离零配置,代价是布局、通信与登录态体验
按域独立部署但同技术栈 MF + manifest 共享一份框架运行时,比整应用沙箱更轻
框架无关的标准组件 Web Components / import maps 浏览器原生,不绑定打包器
按路由在服务端拼页面 服务端组合 / 边缘包含 首屏 HTML 完整,SEO 与性能更可控

行业文章常写「三支以上独立团队才值得上微前端」,这类数字来自工程实践总结而非官方规范,只当参考,别当准入标准。真正该量的是发布冲突次数。

八、上手就会踩的版本坑

数据核实于 2026-11-13,只认 npm dist-tag 与官方站点:

项目 当前版本 / 状态 说明
Module Federation 2.0 稳定版 2026-02-10 官方博客宣布稳定;npm 上 enhanced 2.0.0 为 02-06
@module-federation/enhanced 2.9.3(2026-11-03) latest;提供 ./webpack、./rspack、./runtime 子路径
@module-federation/vite 1.23.11 Vite ESM remote 需显式 type: 'module'
webpack / Rspack / Vite 5.112.2 / 2.3.2 / 8.3.8 npm dist-tag latest
qiankun latest 2.10.16(2023-11-15) 默认 npm i qiankun 装到的就是它
qiankun 3.0 rc 3.0.0-rc.22(2026-08-30) 仍在 active development;安装需 npm i qiankun@rc
single-spa 6.0.3(2024-09-29) qiankun 2.x 的底座

五条最容易写错的旧说法:

常见旧说法 当前口径
「qiankun 3.0 已经稳定,直接用最新版」 3.0 仍是 RC;latest 是 2.10.16,装 3.0 要显式 @rc
「Module Federation 是 Webpack 5 专属功能」 2.0 已解绑打包器,覆盖 Webpack / Rspack / Rollup / Rolldown / Vite / Metro
「MF 2.0 是 2026 年才发布的新东西」 2024-04-26 首发公告,2026-02-10 才宣布 stable,两件事不能混
「MF 自带 JS 沙箱,可以放心接第三方」 MF 不隔离全局;沙箱是 qiankun 的能力
「singleton: true 就解决了版本冲突」 只保证加载一份;版本不满足时是警告 + 取现有最小版本

两条时间线值得单独记住:MF 2.0 2024-04-26 首发、2026-02-10 稳定 ;qiankun 2.10.16 停在 2023-11-15 ,3.0 从 2023 年就在 RC,直到 2026 年(rc.21 于 02-04、rc.22 于 08-30)才重新推进,latest 至今仍指向 2.x。写方案时把「首发」当「稳定」、把 rc 当 latest,评审会上很容易被问住。

九、上线前清单与三问决策表

上线前把这几项过一遍,比选对框架更能决定结果:

  1. 先选一个低风险、有真实流量、有明确负责人的页面做试点,不要拿登录、结算、支付开刀。
  2. 把设计系统、鉴权、路由、埋点、错误处理、无障碍六项的归属写清楚,再拆代码。
  3. 共享依赖版本范围写成显式契约,CI 里加一条「版本漂移」检查。
  4. 每个远端 / 子应用都要有超时、重试、降级 UI 与错误边界,并在演练中真的断一次网。
  5. 监控按应用名 + 版本号打标,能回答「这次报错是哪个团队哪个版本引入的」。
  6. CDN 路径与 manifest / HTML entry 要可版本化、可回滚,禁止覆盖式发布。
  7. 跑一遍生产构建(不是 dev server):CSS、chunk 路径、CORS 头、缓存策略逐项验收。
  8. 量一次改造前后的首屏 JS 体积与 LCP,确认重复依赖没有把性能吃掉;再准备一份「退回单体」的预案。

三问定路线:

  1. 要拆的是模块还是整应用? 模块级走 MF,应用级走 qiankun。
  2. 必须让不同框架或同一框架的两个大版本同页跑吗? 必须,就选带沙箱的应用级方案;不必须,MF 更轻。
  3. 能约束所有团队用同一套共享依赖版本吗? 能,MF 的 shared 才有意义;不能,别假装 singleton 能替你做版本治理。
情况 建议路线 理由
统一技术栈、按域独立发版 Module Federation 2.0 共享一份运行时,粒度最贴合模块与页面片段
异构存量系统渐进迁移 qiankun 2.10.16 HTML entry 对老页面侵入最小,沙箱容纳不同框架
完全不可信的第三方页面 iframe 硬隔离优先,接受通信与布局代价
只需要复用组件 monorepo 内部包 构建时组合更简单,类型与调试都更好
Vite 8 新项目、必须联邦 @module-federation/vite 先在生产构建验收 CSS 与 chunk 路径
暂无独立部署诉求 不要上微前端 先量团队数与发布冲突,再谈架构

2026 年的务实建议:新项目、统一技术栈、多团队要独立发版,优先 MF 2.0 + Rspack 或 Webpack 5 ,用 mf-manifest.json 并打开共享依赖 Tree Shaking;Vite 项目可用 @module-federation/vite,但先在生产构建里验一遍 CSS 与 chunk。存量异构系统要渐进迁移、或者必须混跑框架,用 qiankun 2.10.16 稳定线,把 3.0 放进观察清单而不是生产计划。

十、小结

微前端不是「更先进的前端架构」,而是一笔组织协作的投资:用运行时复杂度换发布自主权。买之前先量发布冲突,买之后先把 URL 契约、版本治理、降级与监控做完。

  • 拆模块、同技术栈、要独立发版:MF 2.0。
  • 拆整应用、异构存量、要隔离:qiankun 2.10.16。
  • 没有独立部署诉求:monorepo,别折腾。

参考资料(均为官方来源,2026-11-13 核实):

你们团队的发布窗口是怎么卡的、最后选了哪条路线?评论区聊聊,尤其是踩过 shared 版本漂移和样式越界的,那两个坑最值得交换经验。

相关推荐
verohh11 分钟前
Cesium 多业务页面切换的资源隔离与架构设计:单 Viewer 复用还是多实例隔离
前端
律宏阔12 分钟前
微信小程序 + Taro 从 ESLint 迁移到 Oxlint 让项目代码检查速度提升近 12 倍
前端·typescript·eslint
用户308169987648514 分钟前
复制的代码为什么报错?BOM 等 10 种隐形字符 40 组实测
前端
Swot17 分钟前
SvelteKit 踩坑记:+page.js 里忘了 ...data,服务端数据全没了
前端·后端·svelte
律宏阔21 分钟前
微信小程序 + Taro + 多语言实现与国际化检查实录
前端·微信小程序·taro
deli0072 小时前
Redis 缓存穿透、击穿、雪崩:9 组流量实测数据库怎么被打穿
前端
江华森2 小时前
5Mbps 小水管也能扛:K3s 上 k6 压测 + HPA 弹性伸缩实测
前端
知守观2 小时前
一个方法两个触发口:反编译 XXL-Job 后,我看清了 inner/job 子包的裸奔现状
java·spring boot·微服务
shuangyueliao2 小时前
103 个小游戏的商店素材,我一张图都没手动做:截图 / 封面 / 打包 / 提审全自动
前端