Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: 为什么构建后的资源访问 html 静态文件,显示 404?约定式路由在 CSR 客户端渲染模式中无效?有文档说明吗? #6339

Closed
cheng4kn opened this issue Oct 9, 2024 · 26 comments
Labels
bug Something isn't working

Comments

@cheng4kn
Copy link

cheng4kn commented Oct 9, 2024

版本信息

System:
OS: macOS 15.0.1
CPU: (8) arm64 Apple M2
Memory: 116.98 MB / 16.00 GB
Shell: 5.9 - /bin/zsh
Browsers:
Chrome: 129.0.6668.90
Safari: 18.0.1
npmPackages:
@modern-js/app-tools: 2.60.1 => 2.60.1
@modern-js/runtime: 2.60.1 => 2.60.1
@modern-js/tsconfig: 2.60.1 => 2.60.1

问题详情

参考 https://modernjs.dev/guides/topic-detail/module-federation/application.html 这个文档,学习和了解了这些配置的参数和意义,使用 https://github.com/web-infra-dev/modern-js-examples/tree/main/examples/module-federation/app-export 这个项目作为参考,完成 PoC 验证 modern.js 应用的 约定式路由 + 模块联邦 + 微应用化 在构建后能够正常工作,以便实现独立部署

Q:为什么构建后的资源访问 html 静态文件,显示 404?约定式路由在 CSR 客户端渲染模式中无效?有文档说明吗?

当我访问 http://localhost:3050/html/main/ 时如图:
Image

当我访问 http://localhost:3051/html/main/ 时如图:
Image

复现链接

https://github.com/web-infra-dev/modern-js-examples/tree/main/examples/module-federation/app-export

复现步骤

  1. app-export 跟 remote 中的 modern.config.ts 文件改动如下:

modern-js-examples/examples/module-federation/app-export/host/modern.config.ts:

import { appTools, defineConfig } from '@modern-js/app-tools';
import { moduleFederationPlugin } from '@module-federation/modern-js';

// https://modernjs.dev/en/configure/app/usage
export default defineConfig({
  dev: {
    port: 3050,
  },
  runtime: {
    router: true,
  },
  output: {
    assetPrefix: 'http://localhost:3050/', // publichPath
  },
  plugins: [
    appTools({
      bundler: 'rspack', // Set to 'webpack' to enable webpack
    }),
    moduleFederationPlugin(),
  ],
});

modern-js-examples/examples/module-federation/app-export/remote/modern.config.ts:

import { appTools, defineConfig } from '@modern-js/app-tools';
import { moduleFederationPlugin } from '@module-federation/modern-js';

// https://modernjs.dev/en/configure/app/usage
export default defineConfig({
  dev: {
    port: 3051,
  },
  runtime: {
    router: true,
  },
  output: {
    assetPrefix: 'http://localhost:3051/', // publichPath
  },
  plugins: [
    appTools({
      bundler: 'rspack', // Set to 'webpack' to enable webpack
    }),
    moduleFederationPlugin(),
  ],
});

PS: module-federation.config.ts 文件没有做任何修改

  • 使用 yarn run dev 命令在开发环境下一切正常,应用 host 跟 remote 都能独立开发独立运行,且模块联邦一切正常

构建后的资源如下:
modern-js-examples/examples/module-federation/app-export/host/dist:

❯ tree ./dist -L 4
./dist
├── html
│   └── main
│       └── index.html
├── mf-manifest.json
├── mf-stats.json
├── modern.config.json
├── nestedRoutes.json
├── route.json
├── routes-manifest.json
└── static
    ├── css
    │   └── async
    │       └── page.18f1c79a.css
    └── js
        ├── 413.03c6e789.js
        ├── 413.03c6e789.js.map
        ├── async
        │   ├── 379.fc933bea.js
        │   ├── 379.fc933bea.js.LICENSE.txt
        │   ├── 379.fc933bea.js.map
        │   ├── 482.ec55b6ed.js
        │   ├── 482.ec55b6ed.js.LICENSE.txt
        │   ├── 482.ec55b6ed.js.map
        │   ├── 562.3ccc7823.js
        │   ├── 562.3ccc7823.js.map
        │   ├── 879.7c8c4893.js
        │   ├── 879.7c8c4893.js.LICENSE.txt
        │   ├── 879.7c8c4893.js.map
        │   ├── 88.5650ac19.js
        │   ├── 88.5650ac19.js.LICENSE.txt
        │   ├── 88.5650ac19.js.map
        │   ├── 983.8a10fff3.js
        │   ├── 983.8a10fff3.js.map
        │   ├── async-main.8d6487fa.js
        │   ├── async-main.8d6487fa.js.map
        │   ├── lib-router.ea4ce0b4.js
        │   ├── lib-router.ea4ce0b4.js.LICENSE.txt
        │   ├── lib-router.ea4ce0b4.js.map
        │   ├── page.eb280f3d.js
        │   ├── page.eb280f3d.js.map
        │   └── remote
        ├── lib-polyfill.4d3f0e49.js
        ├── lib-polyfill.4d3f0e49.js.map
        ├── main.e5bbdd9e.js
        └── main.e5bbdd9e.js.map

9 directories, 37 files

modern-js-examples/examples/module-federation/app-export/remote/dist:

❯ tree ./dist -L 4                  
./dist
├── @mf-types.d.ts
├── @mf-types.zip
├── html
│   └── main
│       └── index.html
├── mf-manifest.json
├── mf-stats.json
├── modern.config.json
├── nestedRoutes.json
├── remoteEntry.js
├── remoteEntry.js.map
├── route.json
├── routes-manifest.json
└── static
    ├── css
    │   └── async
    │       └── page.18f1c79a.css
    └── js
        ├── 807.732d02f2.js
        ├── 807.732d02f2.js.map
        ├── async
        │   ├── 482.f234dbde.js
        │   ├── 482.f234dbde.js.LICENSE.txt
        │   ├── 482.f234dbde.js.map
        │   ├── 562.9fb54bc4.js
        │   ├── 562.9fb54bc4.js.map
        │   ├── 879.5734d967.js
        │   ├── 879.5734d967.js.LICENSE.txt
        │   ├── 879.5734d967.js.map
        │   ├── 88.c27d7ad5.js
        │   ├── 88.c27d7ad5.js.LICENSE.txt
        │   ├── 88.c27d7ad5.js.map
        │   ├── 947.1b0daf35.js
        │   ├── 947.1b0daf35.js.LICENSE.txt
        │   ├── 947.1b0daf35.js.map
        │   ├── __federation_expose_app.a87cdb49.js
        │   ├── __federation_expose_app.a87cdb49.js.map
        │   ├── async-main.59b7af7f.js
        │   ├── async-main.59b7af7f.js.map
        │   ├── lib-router.06a48089.js
        │   ├── lib-router.06a48089.js.LICENSE.txt
        │   ├── lib-router.06a48089.js.map
        │   ├── next
        │   ├── page.88067cc8.js
        │   └── page.88067cc8.js.map
        ├── lib-polyfill.f0730c98.js
        ├── lib-polyfill.f0730c98.js.map
        ├── main.8f9f9284.js
        └── main.8f9f9284.js.map

9 directories, 41 files
  • 安装 http-server
npm -g http-server
  • 使用 yarn run build 命令分别先后构建 host 和 remote,然后分别部署静态资源
http-server ./dist -p 3050 --cors
http-server ./dist -p 3051 --cors

Image
Image

@cheng4kn cheng4kn added the bug Something isn't working label Oct 9, 2024
@cheng4kn cheng4kn changed the title [Bug]: [Bug]: 为什么构建后的资源访问 html 静态文件,显示 404?约定式路由在 CSR 客户端渲染模式中无效?有文档说明吗? Oct 9, 2024
@zllkjc
Copy link
Member

zllkjc commented Oct 12, 2024

这是因为你直接访问 .html 文件的时候,React Router 无法匹配你的路由,这和「约定式路由」没有直接关系。

@cheng4kn
Copy link
Author

我的需求是用 Modern.js 完成 约定式路由 + 模块联邦 + 微应用化 在构建后能够正常工作,以便实现独立开发和独立部署。但目前看起来,我没办法直接部署到静态的CDN,必须使用 modern serve?或者有其他更好的方式吗?

如果没有解决办法,我考虑迁移到其他技术栈,或者不使用 Modern.js ,只是用 rspack。

感谢回复。🙏

@cheng4kn
Copy link
Author

能提供一个 Modern.js + Module Federation 2.0 的部署案例吗?

@cheng4kn
Copy link
Author

  1. 部署

上述部署方式只是最简单的实践,在真实场景还有很多限制,例如版本管理、发布时序等。在字节跳动内部,我们在部署平台上搭建了 Module Federation 应用的部署流程,可以帮助开发者解决这些问题。

我们会持续关注社区里类似功能的平台,在未来完善 Modern.js + Module Federation 在这些类平台上的部署文档。

  1. 部署应用

目前 Modern.js 仅支持在 Node.js 环境中运行,未来将提供更多运行时环境的支持。

怪我没有仔细看文档,QAQ,为什么我总觉得哪里不对?感觉坑埋得很隐晦?

BTW,感谢开源。

@lanmingle
Copy link

模块联邦,怎么说呢,我是花了一两天时间研究和测试,最终放弃了,无需多言,特别是MF2,个人建议别折腾,用:micro-app,另外:modern.js 别用得太重太依赖,可以随时切换到其它(自身维护路由)

@cheng4kn
Copy link
Author

我觉得 MF2 设计上没问题,Modern.js 设计上也没问题。但是文档不严谨,没说明白有哪些坑,进而给开发者留坑。按照文档一顿输出下来,给人带来无端的挫败感,导致体验很糟。

模块联邦,怎么说呢,我是花了一两天时间研究和测试,最终放弃了,无需多言,特别是MF2,个人建议别折腾,用:micro-app,另外:modern.js 别用得太重太依赖,可以随时切换到其它(自身维护路由)

@zllkjc
Copy link
Member

zllkjc commented Oct 15, 2024

我没办法直接部署到静态的CDN,必须使用 modern serve

不是这样的,这是因为默认情况下创建的 Modern.js 项目使用的是「约定式路由」,它会自动转换成基于 React Router 的浏览器端路由。
我们换成一个普通 React Router 的例子来举例,也就是任意的 SPA 应用,你的 basename 是 /,但是你访问的路径其实是 /index.html,所以此时肯定会出现 React Router 匹配不到路由的问题。

这个是任何 SPA 应用都会出现的问题,包括你说「只是用 rspack」,只要你用了浏览器端路由就会出现这个问题。

如果你希望直接通过 /index.html 来访问这个文件,你不能使用浏览器端路由(大概率你要通过 .html 访问也不需要浏览器端路由)。在 Modern.js 里,可以通过 自控式路由 作为入口,你可以不使用任何浏览器端路由的库,直接导出一个根组件。

对于你的应用,我猜测最简单的方式,应该是将 src/routes/page.tsx 的内容复制到 src/App.tsx.

@zllkjc
Copy link
Member

zllkjc commented Oct 15, 2024

我们会持续关注社区里类似功能的平台,在未来完善 Modern.js + Module Federation 在这些类平台上的部署文档。

这里并不是说 Modern.js + Module Federation 无法部署,只是说在「版本管理、发布时序」等方面,外部是没有现成的平台的。
Modern.js + Module Federation 是可以正常部署的,其实就是单独部署 Modern.js 应用和 Module Federation 的 JS,如果你能理解我上面回复的内容,应该就可以完成部署了。现在你的问题主要应该还是 404 这个。

@zllkjc
Copy link
Member

zllkjc commented Oct 15, 2024

目前 Modern.js 仅支持在 Node.js 环境中运行,未来将提供更多运行时环境的支持。

这个是指 Modern.js 暂时不支持跑在例如 Worker 的环境中,和是否可以部署在 CDN 上没关系,CDN 不涉及「服务运行环境」的问题。你永远可以将一个 HTML 部署到 CDN 上去,还是前面说的,你的问题来源于「用 /index.html 去访问 SPA 应用」。

@lanmingle
Copy link

我觉得 MF2 设计上没问题,Modern.js 设计上也没问题。但是文档不严谨,没说明白有哪些坑,进而给开发者留坑。按照文档一顿输出下来,给人带来无端的挫败感,导致体验很糟。

模块联邦,怎么说呢,我是花了一两天时间研究和测试,最终放弃了,无需多言,特别是MF2,个人建议别折腾,用:micro-app,另外:modern.js 别用得太重太依赖,可以随时切换到其它(自身维护路由)

哈哈哈,只能说都还没有准备好,MF2 你不用服务器端渲染好像玩不了,其实前端我个人更加关注还是 CSR 部分,SSR 是非常需要但不重要,如某些情况下大批量渲染,或安全性等,对于微前端呢,其实一开始我是以MF为主的,但隔离、共享等,MF只能满足某部分需求,虽然远程组件等很好,真实情况下完全可以用其他方式替代,我还是推荐你用 micro-app 这个,对于 Modern.js (2.60.3)在最近迭代中无端端出现 ts-node 错误,问题还是有一些的,但还是不错的,目前我这边项目都是基于这个构建的,但不会使用很多功能(能随时替换),总而言之就是还没有准备好。

@zllkjc
Copy link
Member

zllkjc commented Oct 15, 2024

对于 Modern.js (2.60.3)在最近迭代中无端端出现 ts-node 错误

额,这个是我们疏忽了,已经 MR 修复了。

@zllkjc
Copy link
Member

zllkjc commented Oct 15, 2024

MF2 你不用服务器端渲染好像玩不了

不会的,不知道你指的是什么功能,MF2 是可以 CSR 使用的。

@lanmingle
Copy link

MF2 你不用服务器端渲染好像玩不了

不会的,不知道你指的是什么功能,MF2 是可以 CSR 使用的。

按照这个文档:https://module-federation.io/zh/practice/frameworks/modern/index.html 我关了 SSR 就好像跑不起来了,目前我这边的需求使用 micro-app 已能满足,但是 MF2 还是有需求的,能完善是最好的。

@zllkjc
Copy link
Member

zllkjc commented Oct 15, 2024

可以看下 modern 的文档,应该更加详细一些,也有例子。https://modernjs.dev/guides/topic-detail/module-federation/usage.html

@cheng4kn
Copy link
Author

我没办法直接部署到静态的CDN,必须使用 modern serve

不是这样的,这是因为默认情况下创建的 Modern.js 项目使用的是「约定式路由」,它会自动转换成基于 React Router 的浏览器端路由。 我们换成一个普通 React Router 的例子来举例,也就是任意的 SPA 应用,你的 basename 是 /,但是你访问的路径其实是 /index.html,所以此时肯定会出现 React Router 匹配不到路由的问题。

这个是任何 SPA 应用都会出现的问题,包括你说「只是用 rspack」,只要你用了浏览器端路由就会出现这个问题。

如果你希望直接通过 /index.html 来访问这个文件,你不能使用浏览器端路由(大概率你要通过 .html 访问也不需要浏览器端路由)。在 Modern.js 里,可以通过 自控式路由 作为入口,你可以不使用任何浏览器端路由的库,直接导出一个根组件。

对于你的应用,我猜测最简单的方式,应该是将 src/routes/page.tsx 的内容复制到 src/App.tsx.

你的意思是说约定式路由无法使用 CSR?官网有给出提示吗?我找了一下,找到了如下这句话 自控式路由

我们推荐开发者使用约定式路由,Modern.js 默认对约定式路由做了一系列资源加载及渲染上的优化,并且提供了开箱即用的 SSR 能力。而在使用自控路由时,这些能力都需要开发者自行封装。

不饶这么一圈,真不知道 "约定式路由" 是 SSR 才有的能力,CSR 是没有这个能力。Modern.js 跟 MF2 的文档都没有明确的提到这个坑,得开发者自己踩。

而且文档也并不明确,有刻意绕过和遗漏的嫌疑。结论,Modern.js 在文档上跟顶流还有一段距离。

@cheng4kn
Copy link
Author

我觉得 MF2 设计上没问题,Modern.js 设计上也没问题。但是文档不严谨,没说明白有哪些坑,进而给开发者留坑。按照文档一顿输出下来,给人带来无端的挫败感,导致体验很糟。

模块联邦,怎么说呢,我是花了一两天时间研究和测试,最终放弃了,无需多言,特别是MF2,个人建议别折腾,用:micro-app,另外:modern.js 别用得太重太依赖,可以随时切换到其它(自身维护路由)

哈哈哈,只能说都还没有准备好,MF2 你不用服务器端渲染好像玩不了,其实前端我个人更加关注还是 CSR 部分,SSR 是非常需要但不重要,如某些情况下大批量渲染,或安全性等,对于微前端呢,其实一开始我是以MF为主的,但隔离、共享等,MF只能满足某部分需求,虽然远程组件等很好,真实情况下完全可以用其他方式替代,我还是推荐你用 micro-app 这个,对于 Modern.js (2.60.3)在最近迭代中无端端出现 ts-node 错误,问题还是有一些的,但还是不错的,目前我这边项目都是基于这个构建的,但不会使用很多功能(能随时替换),总而言之就是还没有准备好。

Modern.js 的理念是 OK 的,而且完成度也 OK。基于 Rust 的工具集效率也 OK,但是文档建设烂尾了。确实没准备好,可能字节内部还没用到 MF 2.0 + CSR 😜 场景 ?!

我决定放弃 Modern.js 能力,切成 Vite + vite-plugin-pages 魔改成 Next.js app-router + MF 2.0。

对 Modern.js 的 DX 体验寄予厚望,但终究是错付了,哎~~~ 无语....

我不用 Micro-App 的原因是,我不需要微前端的跨技术栈能力,我需要的是微应用能力。做到同一技术栈,Mono-repo 多应用之间共享依赖,再加上独立开发,独立构建,独立部署的能力。

@lanmingle
Copy link

我觉得 MF2 设计上没问题,Modern.js 设计上也没问题。但是文档不严谨,没说明白有哪些坑,进而给开发者留坑。按照文档一顿输出下来,给人带来无端的挫败感,导致体验很糟。

模块联邦,怎么说呢,我是花了一两天时间研究和测试,最终放弃了,无需多言,特别是MF2,个人建议别折腾,用:micro-app,另外:modern.js 别用得太重太依赖,可以随时切换到其它(自身维护路由)

哈哈哈,只能说都还没有准备好,MF2 你不用服务器端渲染好像玩不了,其实前端我个人更加关注还是 CSR 部分,SSR 是非常需要但不重要,如某些情况下大批量渲染,或安全性等,对于微前端呢,其实一开始我是以MF为主的,但隔离、共享等,MF只能满足某部分需求,虽然远程组件等很好,真实情况下完全可以用其他方式替代,我还是推荐你用 micro-app 这个,对于 Modern.js (2.60.3)在最近迭代中无端端出现 ts-node 错误,问题还是有一些的,但还是不错的,目前我这边项目都是基于这个构建的,但不会使用很多功能(能随时替换),总而言之就是还没有准备好。

Modern.js 的理念是 OK 的,而且完成度也 OK。基于 Rust 的工具集效率也 OK,但是文档建设烂尾了。确实没准备好,可能字节内部还没用到 MF 2.0 + CSR 😜 场景 ?!

我决定放弃 Modern.js 能力,切成 Vite + vite-plugin-pages 魔改成 Next.js app-router + MF 2.0。

对 Modern.js 的 DX 体验寄予厚望,但终究是错付了,哎~~~ 无语....

我不用 Micro-App 的原因是,我不需要微前端的跨技术栈能力,我需要的是微应用能力。做到同一技术栈,Mono-repo 多应用之间共享依赖,再加上独立开发,独立构建,独立部署的能力。

... 不行换别的,有可能会付出更多的工作量,理解一下,有句难听的话是这样:要不你来?

@zllkjc
Copy link
Member

zllkjc commented Oct 16, 2024

不饶这么一圈,真不知道 "约定式路由" 是 SSR 才有的能力

不是这个意思,约定式路由最终会转换成 SPA 路由,而 SPA 路由无法通过 /index.html 这个路径访问,这会使你的 React Router 无法工作。

可能字节内部还没用到 MF 2.0 + CSR 😜 场景

字节内部有非常多这样的场景,你的问题和 MF 甚至都没有关系,就像我一直说的,是对 SPA 应用部署的问题。

我决定放弃 Modern.js 能力,切成 Vite + vite-plugin-pages 魔改成 Next.js app-router + MF 2.0。

嗯,你可以尝试用其他的技术栈,但你仍旧会遇到你的问题,可能你还没有理解 SPA 的部署,当你直接通过 /index.html 这个路由访问的时候,你是无法让客户端路由工作的。

@cheng4kn
Copy link
Author

cheng4kn commented Oct 16, 2024 via email

@zllkjc
Copy link
Member

zllkjc commented Oct 16, 2024

就是 CSR 在客户端浏览器中指定 react-router-dom 的代码渲染一个 <BrowserRouter />

嗯,你可以试试用一个纯 Webpack 项目 + React Router,直接通过 http server + /index.html,访问产物的 index.html,也是无法达到你想要的效果的。我的意思就是 SPA 项目无法通过 /index.html 来达到预期的效果。

@cheng4kn
Copy link
Author

就是 CSR 在客户端浏览器中指定 react-router-dom 的代码渲染一个 <BrowserRouter />

嗯,你可以试试用一个纯 Webpack 项目 + React Router,直接通过 http server + /index.html,访问产物的 index.html,也是无法达到你想要的效果的。我的意思就是 SPA 项目无法通过 /index.html 来达到预期的效果。

谢谢回复,无论是 CSR 还是 SSR 只要 MicroApp + Module Federation 2.0 工作正常即可,但是我试过使用 SSR 去渲染,先 modern buildmodern serve mono-repo 下的应用 host 跟 remote,一样无法正常工作。

看了下 issue#6378,如果 SSR 也无法正常工作,那么只能在 CSR 环境使用应用级别模块

这些信息如此重要能否应该在文档中使用 danger notes 提示出来。

@zllkjc
Copy link
Member

zllkjc commented Oct 16, 2024

这些信息如此重要能否应该在文档中使用 danger notes 提示出来。

其实现在是有的,https://modernjs.dev/guides/topic-detail/module-federation/ssr.html,我们可以在「应用级别模块」这章也说明一下。

现在,我现在理解你的应用了。你希望能用到「应用级别模块」的 MF,并且也希望用到这个 MF 的路由能力。那我可以完整的说明一下:

  1. 上面的内容你应该已经了解,为什么 SPA 页面不能直接通过 /index.html 访问,以及为什么会 404。
  2. 你可以参考 https://github.com/web-infra-dev/modern-js-examples/tree/main/examples/module-federation/app-export 这个例子,运行 host 和 remote
  3. 当你需要部署的时候,你的 host 必须通过 Nginx 或者某个 Web Server,将 / 这个路由(也可能是例如 /home 或者其他)指向 /index.html 文件(remote 无所谓,可以将 JS 直接上传到 CDN)。因为如果你不这样做,当 remote 被加载在 host 里的时候,React Router 是匹配不到这个为 /index.html 的路由的。

我猜测你现在是有一个现成的已经用 CDN 部署的 host,然后想接入一个「应用级别模块」的 remote,这在不改造 host 部署的情况下,确实是不行的。

@cheng4kn
Copy link
Author

用户的诉求是:

请在文档中给出一个可以直接跑起来的 DEMO,无论是 Node.js or Nginx,给出能 run 起来的示例即可。

@zllkjc
Copy link
Member

zllkjc commented Oct 22, 2024

一个可以直接跑起来的 DEMO

希望要一个通过 Node.js 或 Nginx 直接托管 HTML 的 Demo 吗

@cheng4kn
Copy link
Author

一个可以直接跑起来的 DEMO

希望要一个通过 Node.js 或 Nginx 直接托管 HTML 的 Demo 吗

是的,谢谢

@zllkjc
Copy link
Member

zllkjc commented Nov 1, 2024

#6467 在部署相关文档中追加了一些用例。 https://modernjs.dev/guides/basic-features/deploy.html
自建 node 服务的简单 Demo:Example

@zllkjc zllkjc closed this as completed Nov 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants