🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付

Modern.js 3.0 發布:聚焦 Web 框架,擁抱生態發展

ByteDance Web Infra

WebInfra
前端 @字節跳動 Web Infra

前言

Modern.js 2.0 發布 至今,已過去三年時間,感謝社區開發者們對 Modern.js 的使用和信任。Modern.js 一直保持穩定的迭代,累計發布了超過 100 個版本。

在字節內部,Modern.js 已成為 Web 開發的核心框架。在全公司活躍的 Web 項目中,使用佔比已從 2025 年初的 40% 增長至目前接近 70%。

這三年中,我們不斷擴充新特性,持續進行代碼重構與優化,也收到了非常多的反饋,這些經驗成為了 3.0 版本改進的重要參考。經過慎重考慮,我們決定發布 Modern.js 3.0,對框架進行一次全面的升級。

Modern.js 2.0 到 3.0 的演變

從 Modern.js 2.0 到 3.0,有兩個核心轉變:

更聚焦,專注於 Web 框架

  • Modern.js 2.0:包含 Modern.js App、Modern.js Module、Modern.js Doc
  • Modern.js 3.0:只代表 Modern.js App,Modern.js Module 和 Modern.js Doc 已孵化為 RslibRspress

更開放,積極面向社區工具

  • Modern.js 2.0:內置各類工具、框架獨特的 API 設計
  • Modern.js 3.0:強化插件體系,完善接入能力,推薦社區優質方案

Modern.js 3.0 新特性

React Server Component

TL;DR:Modern.js 3.0 集成 React Server Component,支持 CSR 和 SSR 項目,並支持漸進式遷移。

React Server Component

什麼是 React Server Component

React Server Components(服務端組件)是一種新的組件類型,它允許組件邏輯完全在服務端執行,並直接將渲染後的 UI 流式傳輸到客戶端。與傳統的客戶端組件相比,服務端組件帶來了以下特性:

特性 說明
零客戶端包體積 組件代碼不包含在客戶端 JS Bundle 中,僅在服務端執行,加快首屏加載與渲染速度
更高的內聚性 組件可直接連接資料庫、調用內部 API、讀取本地文件,提高開發效率
漸進增強 可與客戶端組件無縫混合使用,按需下放交互邏輯到客戶端,在保持高性能的同時,支持複雜交互體驗

需要明確的是,RSC 和 SSR 是截然不同的概念

  • RSC:描述的是組件類型,即組件在哪裡執行(服務端 vs 客戶端)
  • SSR:描述的是渲染模式,即 HTML 在哪裡生成(服務端 vs 客戶端)

兩者可以組合使用:Server Component 可以在 SSR 項目下使用,也可以在 CSR 項目下使用。在 Modern.js 3.0 中,我們同時支持這兩種模式,開發者可以根據需求選擇。

RSC 示例

開箱即用

在 Modern.js 3.0 中,只需在配置中啟用 RSC 能力:

modern.config.ts

export default defineConfig({
  server: {
    rsc: true,
  }
});

配置啟用後,所有的路由組件都會默認成為 Server Component。項目中可能存在無法在服務端運行的組件,你可以先為這些組件添加 'use client' 標記,以保持原有行為,再逐步遷移。

RSC 效果演示視頻:lf3-static.bytednsdoc.com/obj/eden-cn…

Modern.js 3.0 的 RSC 特性

Modern.js 一直選擇 React Router 作為路由解決方案。去年,React Router v7 宣布支持 React Server Component,這為 Modern.js 提供了在 SPA 應用下實現 RSC 的基礎。

相比於社區其他框架,Modern.js 對 RSC 做了幾點優化:

  • 使用 Rspack 最新的 RSC 插件構建,顯著提升 RSC 項目構建速度;並進一步優化了產物體積。
  • 不同於社區主流框架只支持 RSC + SSR,Modern.js 3.0 的 RSC 同樣支持 CSR 項目
  • 在路由跳轉時,框架會自動將多個 Data Loader 和 Server Component 的請求合併為單個請求,並流式返回,提升頁面性能
  • 在嵌套路由場景下,路由組件類型不受父路由組件類型的影響,開發者可以從任意路由層級開始採用 Server Component

漸進式遷移

基於靈活的組件邊界控制能力,Modern.js 3.0 提供了漸進式的遷移方式。Modern.js 3.0 允許基於路由組件維度的 Server Component 遷移,無需遷移整條組件樹鏈路。

漸進式遷移示例

更多 React Server Component 的詳細內容,可以參考:React Server Component


擁抱 Rspack

TL;DR:Modern.js 3.0 移除了對 webpack 的支持,全面擁抱 Rspack,並升級到最新的 Rspack & Rsbuild 2.0。

在 2023 年,我們開源了 Rspack,並在 Modern.js 中支持將 Rspack 作為可選的打包工具。在字節內部,超過 60% 的 Modern.js 項目已經切換到 Rspack 構建。

經過兩年多發展,Rspack 在社區中的月下載量已超過 1000 万次,成為行業內被廣泛使用的打包工具;同時,Modern.js 的 Rspack 構建模式也得到持續完善。

Rspack 下載量

在 Modern.js 3.0 中,我們決定移除對 webpack 的支持,從而使 Modern.js 變得更加輕量和高效,並能更充分地利用 Rspack 的新特性。

更順暢的開發體驗

Modern.js 3.0 在移除 webpack 後,能夠更好地遵循 Rspack 最佳實踐,在構建性能、安裝速度等方面均有提升:

底層依賴升級

Modern.js 3.0 將底層依賴的 Rspack 和 Rsbuild 升級至 2.0 版本,並基於新版本優化了默認構建配置,使整體行為更加一致。

參考以下文檔了解底層行為變化:

更快的構建速度

Modern.js 通過 Rspack 的多項特性來減少構建耗時:

  • 默認啟用 Barrel 文件優化:構建組件庫速度提升 20%
  • 默認啟用持久化緩存:非首次構建的速度提升 50%+

更快的安裝速度

移除 webpack 相關依賴後,Modern.js 3.0 的構建依賴數量和體積均明顯減少:

  • npm 依賴數量減少 40%
  • 安裝體積減少 31 MB

更小的構建產物

Modern.js 現在默認啟用 Rspack 的多項產物優化策略,能夠比 webpack 生成更小的產物體積,例如:

增強 Tree shaking

增強了 tree shaking 分析能力,可以處理更多動態導入語法,例如解構賦值:

// 參數中的解構訪問
import('./module').then(({ value }) => {
  console.log(value);
});

// 函數體內的解構訪問
import('./module').then((mod) => {
  const { value } = mod;
  console.log(value);
});

常量內聯

對常量進行跨模塊內聯,有助於壓縮工具進行更準確的靜態分析,從而消除無用的代碼分支:

// constants.js
export const ENABLED = true;

// index.js
import { ENABLED } from './constants';
if (ENABLED) {
  doSomething();
} else {
  doSomethingElse();
}

// 構建產物 - 無用分支被消除
doSomething();

全鏈路可擴展

TL;DR:Modern.js 3.0 正式開放完整插件體系,提供運行時、服務端插件,同時支持靈活處理應用入口。

Modern.js 2.0 提供了 CLI 插件與內測版本的運行時插件,允許開發者對項目進行擴展。但在實踐過程中,我們發現現有的能力不足以支撐複雜的業務場景。

Modern.js 3.0 提供了更靈活的定制能力,允許為應用編寫全流程的插件,幫助團隊統一業務邏輯、減少重複代碼:

  • CLI 插件:在構建階段擴展功能,如添加命令、修改配置
  • Runtime 插件:在渲染階段擴展功能,如數據預取、組件封裝
  • Server 插件:在服務端擴展功能,如添加中間件、修改請求響應

運行時插件

運行時插件在 CSR 與 SSR 過程中都會運行,新版本提供了兩個核心鉤子:

  • onBeforeRender:在渲染前執行邏輯,可用於數據預取、注入全局數據
  • wrapRoot:封裝根組件,添加全局 Provider、佈局組件等

你可以在 src/modern.runtime.ts 中註冊插件,相比在入口手動引入高階組件,運行時插件可插拔、易更新,在多入口場景下無需重複引入:

src/modern.runtime.tsx

import { defineRuntimeConfig } from "@modern-js/runtime";

export default defineRuntimeConfig({
  plugins: [
    {
      name: "my-runtime-plugin",
      setup: (api) => {
        api.onBeforeRender((context) => {
          context.globalData = { theme: "dark" };
        });
        api.wrapRoot((App) => (props) => <App {...props} />);
      },
    },
  ],
});

更多 Runtime 插件使用方式,請查看文檔:Runtime 插件

服務端中間件

在實踐過程中我們發現,部分項目需要擴展 Web Server,例如鑒權、數據預取、降級處理、動態 HTML 腳本注入等。

在 Modern.js 3.0 中,我們使用 Hono 重構了 Web Server,並正式開放了服務端中間件與插件的能力。開發者可以使用 Hono 的中間件來完成需求:

server/modern.server.ts

import { defineServerConfig, type MiddlewareHandler } from "@modern-js/server-runtime";

const timingMiddleware: MiddlewareHandler = async (c, next) => {
  const start = Date.now();
  await next();
  const duration = Date.now() - start;
  c.header('X-Response-Time', `${duration}ms`);
};

const htmlMiddleware: MiddlewareHandler = async (c, next) => {
  await next();
  const html = await c.res.text();
  const modified = html.replace(
    "<head>",
    '<head><meta name="generator" content="Modern.js">'
  );
  c.res = c.body(modified, { status: c.res.status, headers: c.res.headers });
};

export default defineServerConfig({
  middlewares: [timingMiddleware],
  renderMiddlewares: [htmlMiddleware],
});

更多服務端插件使用方式,可以查看文檔:自定義 Web Server

自定義入口

在 Modern.js 3.0 中,我們重構了自定義入口,相比於舊版 API 更加清晰靈活:

src/entry.tsx

import { createRoot } from '@modern-js/runtime/react';
import { render } from '@modern-js/runtime/browser';

const ModernRoot = createRoot();

async function beforeRender() {
  // 渲染前的異步操作,如初始化 SDK、獲取用戶信息等
}

beforeRender().then(() => {
  render(<ModernRoot />);
});

更多入口使用方式,請查看文檔:入口


路由優化

TL;DR:Modern.js 3.0 內置 React Router v7,提供配置式路由能力與 AI 友好的調試方式。

內置 React Router v7

在 Modern.js 3.0 中,我們統一升級到 React Router v7,並廢棄了對 v5 和 v6 的內置支持。這一決策基於以下考慮:

版本演進與穩定性

React Router v6 是一個重要的過渡版本,它引入了許多新特性(如數據加載、錯誤邊界等)。而 v7 在保持 v6 API 兼容性的基礎上,進一步優化了性能、穩定性和開發體驗。隨著 React Router 團隊將 Remix 定位為獨立框架,React Router 核心庫可能會在 v7 版本上長期維護,使其成為更可靠的選擇。

升級路徑

  • 從 v6 升級:React Router v7 對 v6 開發者來說是無破壞性變更的升級。在 Modern.js 2.0 中,我們已提供了 React Router v7 插件支持,你可以通過插件方式漸進式升級,驗證兼容性後再遷移到 Modern.js 3.0。
  • 從 v5 升級:v5 到 v7 存在較大的 API 變化,建議參考 React Router 官方遷移指南 進行遷移。

配置式路由

在 Modern.js 中,我們推薦使用約定式路由來組織代碼。但在實際業務中,開發者偶爾遇到以下場景:

  • 多路徑指向同一組件
  • 靈活的路由控制
  • 條件性路由
  • 遺留項目遷移

因此,Modern.js 3.0 提供了完整的配置式路由支持,可以與約定式路由一起使用,或兩者分別單獨使用。

src/modern.routes.ts

import { defineRoutes } from "@modern-js/runtime/config-routes";

export default defineRoutes(({ route, layout, page }) => {
  return [
    route("home.tsx", "/"),
    route("about.tsx", "about"),
    route("blog.tsx", "blog/:id"),
  ];
});

更多配置式路由使用方式,請查看文檔:配置式路由

路由調試

運行 npx modern routes 命令即可在 dist/routes-inspect.json 文件中生成完整的路由結構分析報告。

報告中會顯示每個路由的路徑、組件文件、數據加載器、錯誤邊界、Loading 組件等完整信息,幫助開發者快速了解項目的路由配置,快速定位和排查路由相關問題。結構化的 JSON 格式也便於 AI agent 理解和分析路由結構,提升 AI 輔助開發的效率。

具體使用方式,請查看文檔:路由調試


服務端渲染

TL;DR:Modern.js 3.0 重做了 SSG 能力,提供了靈活的緩存能力,對降級策略也進行了進一步的完善。

靜態站點生成(SSG)

在 Modern.js 2.0 中,我們提供了靜態站點生成的能力。這個能力非常適合用在可以靜態渲染的頁面中,能極大的提升頁面首屏性能。

在新版本中,我們對 SSG 進行了重新設計:

  • 數據獲取使用 Data Loader,與非 SSG 場景保持一致
  • 簡化了 API,降低理解成本
  • 與約定式路由更好地結合

在新版本中,你可以通過 data loader 進行數據獲取,與非 SSG 場景保持一致。然後在 ssg.routes 配置中即可直接指定要渲染的路由:

modern.config.ts

export default defineConfig({
  output: {
    ssg: {
      routes: ['/blog'],
    },
  },
});

routes/blog/page.data.ts

export const loader = async () => {
  const articles = await fetchArticles();
  return { articles };
};

更多 SSG 的使用方式,請查看文檔:SSG

緩存機制

Modern.js 3.0 中提供了不同維度的緩存機制,幫助項目提升首屏性能。所有緩存均支持靈活配置,比如可以支持類似 HTTP 的 stale-while-revalidate 策略:

渲染緩存

支持將 SSR 結果進行整頁的緩存,在 server/cache.ts 中配置:

server/cache.ts

import type { CacheOption } from '@modern-js/server-runtime';

export const cacheOption: CacheOption = {
  maxAge: 500, // ms
  staleWhileRevalidate: 1000, // ms
};

使用渲染緩存,請查看文檔:渲染緩存

數據緩存

我們在新版本中提供了 cache 函數,相比渲染緩存它提供了更精細的數據粒度控制。當多個數據請求依賴同一份數據時,cache 可以避免重複請求:

server/loader.ts

import { cache } from "@modern-js/runtime/cache";
import { fetchUserData, fetchUserProjects, fetchUserTeam } from "./api";

// 緩存用戶數據,避免重複請求
const getUser = cache(fetchUserData);

const getProjects = async () => {
  const user = await getUser("test-user");
  return fetchUserProjects(user.id);
};

const getTeam = async () => {
  const user = await getUser("test-user"); // 复用緩存,不會重複請求
  return fetchUserTeam(user.id);
};

export const loader = async () => {
  // getProjects 和 getTeam 都依賴 getUser,但 getUser 只會執行一次
  const [projects, team] = await Promise.all([getProjects(), getTeam()]);
  return { projects, team };
};

更多數據緩存的使用方式,請查看文檔:數據緩存

靈活的降級策略

在實踐過程中,我們沉澱了多維度的降級策略:

類型 觸發方式 降級行為 使用場景
異常降級 Data Loader 執行報錯 觸發 ErrorBoundary 數據請求異常兜底
組件渲染報錯 服務端渲染異常 降級到 CSR,複用已有數據渲染 服務端渲染異常兜底
業務降級 Loader 抛出 throw Response 觸發 ErrorBoundary,返回對應 HTTP 狀態碼 404、權限校驗等業務場景
配置 Client Loader 配置 Client Loader 繞過 SSR,直接請求數據源 需要在客戶端直接獲取數據的場景
強制降級 Query 參數 ?__csr=true 跳過 SSR,返回 CSR 頁面 調試、臨時降級
強制降級 請求頭 x-modern-ssr-fallback 跳過 SSR,返回 CSR 頁面 門檻層控制降級

輕量 BFF

TL;DR:Modern.js 3.0 基於 Hono 重構了 Web Server,提供基於 Hono 的一體化函數,同時支持跨項目調用。

Hono 一體化函數

在 Modern.js 3.0 中,我們使用 Hono 作為 BFF 的運行時框架,開發者可以基於 Hono 生態擴展 BFF Server,享受 Hono 輕量、高性能的優勢。

通過 useHonoContext 可以獲取完整的 Hono 上下文,訪問請求信息、設置響應頭等:

api/lambda/user.ts

import { useHonoContext } from '@modern-js/server-runtime';

export const get = async () => {
  const c = useHonoContext();
  const token = c.req.header('Authorization');
  c.header('X-Custom-Header', 'modern-js');
  const id = c.req.query('id');

  return { userId: id, authenticated: !!token };
};

跨項目調用

在過去,Modern.js BFF 只能在當前項目中使用,而我們陸續收到開發者反饋,希望能夠在不同項目中使用。這大多數情況是由於開發者的遷移成本、運維成本造成的,相比於抽出原有代碼再部署一個,顯然複用已有服務更加合理。

為了保證開發者能得到與當前項目一體化調用類似的體驗,我們提供了跨項目調用的能力。

更多 BFF 的使用方式,請查看文檔:BFF


Module Federation 深度集成

TL;DR:Modern.js 3.0 與 Module Federation 2.0 深度集成,支持 MF SSR 和應用級別模塊導出。

MF SSR

Modern.js 3.0 支持在 SSR 應用中使用 Module Federation,組合使用模塊聯邦和服務端渲染能力,為用戶提供更好的首屏性能體驗。

modern.config.ts

export default defineConfig({
  server: {
    ssr: {
      mode: 'stream',
    },
  },
});

結合 Module Federation 的數據獲取能力,每個遠程模塊都可以定義自己的數據獲取邏輯:

src/components/Button.data.ts

export const fetchData = async () => {
  return {
    data: `Server time: ${new Date().toISOString()}`,
  };
};

src/components/Button.tsx

export const Button = (props: { mfData: { data: string } }) => {
  return <button>{props.mfData?.data}</button>;
};

應用級別模塊

不同於傳統的組件級別共享,Modern.js 3.0 支持導出應用級別模塊——具備完整路由能力、可以像獨立應用一樣運行的模塊。這是微前端場景中的重要能力。

生產者導出應用

src/export-App.tsx

import '@modern-js/runtime/registry/index';
import { render } from '@modern-js/runtime/browser';
import { createRoot } from '@modern-js/runtime/react';
import { createBridgeComponent } from '@module-federation/modern-js/react';

const ModernRoot = createRoot();
export const provider = createBridgeComponent({
  rootComponent: ModernRoot,
  render: (Component, dom) => render(Component, dom),
});

export default provider;

消費者加載應用

src/routes/remote/$.tsx

import { createRemoteAppComponent } from '@module-federation/modern-js/react';
import { loadRemote } from '@module-federation/modern-js/runtime';

const RemoteApp = createRemoteAppComponent({
  loader: () => loadRemote('remote/app'),
  fallback: ({ error }) => <div>Error: {error.message}</div>,
  loading: <div>Loading...</div>,
});

export default RemoteApp;

通過通配路由 $.tsx,所有訪問 /remote/* 的請求都會進入遠程應用,遠程應用內部的路由也能正常工作。

更多 Module Federation 的使用方式,請查看文檔:Module Federation


技術棧更新

TL;DR:Modern.js 3.0 升級 React 19,最低支持 Node.js 20。

React 19

Modern.js 3.0 新項目默認使用 React 19,最低支持 React 18。

如果你的項目仍在使用 React 16 或 React 17,請先參考 React 19 官方升級指南 完成版本升級。

Node.js 20

隨著 Node.js 不斷推進版本演進,Node.js 18 已經 EOL。在 Modern.js 3.0 中,推薦使用 Node.js 22 LTS,不再保證對 Node.js 18 的支持。

Storybook Rsbuild

在 Modern.js 3.0 中,我們基於 Storybook Rsbuild 實現了使用 Storybook 構建 Modern.js 應用。

通過 Storybook Addon,我們將 Modern.js 配置轉換合併為 Rsbuild 配置,並通過 Storybook Rsbuild 驅動構建,讓 Storybook 調試與開發命令保持配置對齊。

更多 Storybook 使用方式,請查看文檔:使用 Storybook


從 Modern.js 2.0 升級到 3.0

主要變更

升級 Modern.js 3.0 意味著擁抱更輕量、更標準的現代化開發範式。通過全面對齊 Rspack 與 React 19 等主流生態,徹底解決歷史包袱帶來的維護痛點,顯著提升構建與運行性能。

未來,我們也會基於 Modern.js 3.0 提供更多的 AI 集成與最佳實踐,配合靈活的全棧插件系統,讓開發者能以極低的學習成本複用社區經驗,實現開發效率的質變與應用架構的現代化升級。

更多改進與變更,請查看文檔:升級指南

反饋和社區

最後,再次感謝每一位給予我們反饋和支持的開發者,我們將繼續與大家保持溝通,在相互支持中共同成長。

如果你在使用過程中遇到問題,歡迎通過以下方式反饋:


原文出處:https://juejin.cn/post/7610321700097884187


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝22  
635
🥈
我愛JS
💬3  
10
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付