# Modern.js :::tip - 示例 Demo [Modern.js SSR](https://github.com/module-federation/module-federation-examples/tree/master/modernjs-ssr) ::: ## 环境准备 ### Node.js 在开始使用前,你需要安装 [Node.js](https://nodejs.org/),并保证 Node.js 版本不低于 16.2.0,**我们推荐使用 Node.js 18 的 LTS 版本**。 你可以通过以下命令检查当前使用的 Node.js 版本: ```bash node -v ``` 如果你当前的环境中尚未安装 Node.js,或是安装的版本低于 16,可以通过 [nvm](https://github.com/nvm-sh/nvm) 或 [fnm](https://github.com/Schniz/fnm) 安装需要的版本。 下面是通过 nvm 安装 Node.js 18 LTS 版本的例子: ```bash # 安装 Node.js 18 的长期支持版本 nvm install 18 --lts # 将刚安装的 Node.js 18 设置为默认版本 nvm alias default 18 # 切换到刚安装的 Node.js 18 nvm use 18 ``` :::tip nvm 和 fnm nvm 和 fnm 都是 Node.js 版本管理工具。相对来说,nvm 较为成熟和稳定,而 fnm 是使用 Rust 实现的,比 nvm 提供了更好的性能。 ::: 此外,在安装 nvm 或 fnm 后,然后只要仓库根目录下有内容为 `lts/hydrogen` 的 `.nvmrc` 文件,进入这个仓库时就会自动安装或切换到正确的 Node.js 版本。 ### pnpm 推荐使用 [pnpm](https://pnpm.io/installation) 来管理依赖: ```bash npm install -g pnpm@8 ``` :::note Modern.js 同样支持使用 `yarn`、`npm` 进行依赖管理。 ::: ## 创建项目 Modern.js 提供了 `@modern-js/create` 工具来创建项目,不需要全局安装,直接使用 `npx` 按需运行即可。 ### 创建消费者 ```bash npx @modern-js/create@latest modern-consumer ``` ### 创建生产者 ```bash npx @modern-js/create@latest modern-provider ``` ### 安装插件 Module Federation 为 Modern.js 提供了 配套的插件 `@module-federation/modern-js-v3`。 ```bash pnpm add @module-federation/modern-js-v3 ``` ## 设置模块联邦配置 ### 生产者 #### 1. 创建配置文件 在项目根目录创建 `module-federation.config.ts` 文件,并写入下列内容: ```ts title='module-federation.config.ts' import { createModuleFederationConfig } from '@module-federation/modern-js-v3'; export default createModuleFederationConfig({ name: 'provider', filename: 'remoteEntry.js', exposes: { './Image': './src/components/Image.tsx', }, shared: { react: { singleton: true }, 'react-dom': { singleton: true }, }, }); ``` #### 2. 应用插件 在 `modern.config.ts` 应用 `@module-federation/modern-js-v3`: ```ts title='modern.config.ts' import { appTools, defineConfig } from '@modern-js/app-tools'; import { moduleFederationPlugin } from '@module-federation/modern-js-v3'; // https://modernjs.dev/en/configure/app/usage export default defineConfig({ server: { ssr: { mode: 'stream', }, port: 3006, }, plugins: [appTools(), moduleFederationPlugin()], }); ``` #### 3. 创建导出组件 创建文件 `src/components/Image.tsx` ,内容如下: ```tsx title='Image.tsx' import React from 'react'; import styles from './Image.module.css'; export default (): JSX.Element => ( <div id="remote-components" style={{ backgroundColor: '#1ee9c1', color: 'lightgrey', padding: '1rem', }} > <h2> <strong>remote</strong>&nbsp;image </h2> <button id="remote-components-button" style={{ marginBottom: '1rem' }} onClick={() => alert('[remote-components] Client side Javascript works!')} > Click me to test i'm interactive! </button> <img id="remote-components-image" src="https://module-federation.io/module-federation-logo.svg" style={{ width: '100px' }} alt="serge" /> <button className={styles['button']}>Button from remote</button> </div> ); ``` 并创建对应的样式文件,内容如下: ```css title='Image.module.css' .button { background: red; } ``` ### 消费者 #### 1. 创建配置文件 在项目根目录创建 `module-federation.config.ts` 文件,并写入下列内容: ```ts title='module-federation.config.ts' import { createModuleFederationConfig } from '@module-federation/modern-js-v3'; export default createModuleFederationConfig({ name: 'consumer', remotes: { remote: 'provider@http://localhost:3006/mf-manifest.json', }, shared: { react: { singleton: true }, 'react-dom': { singleton: true }, }, }); ``` #### 2. 应用插件 在 `modern.config.ts` 应用 `@module-federation/modern-js-v3`: ```ts title='modern.config.ts' import { appTools, defineConfig } from '@modern-js/app-tools'; import { moduleFederationPlugin } from '@module-federation/modern-js-v3'; // https://modernjs.dev/en/configure/app/usage export default defineConfig({ server: { ssr: { mode: 'stream', }, port: 3007, }, plugins: [appTools(), moduleFederationPlugin()], }); ``` #### 3. 引用类型 在 `tsconfig.json` 添加 `paths` 以获取生产者的类型: ```json { "compilerOptions": { "paths": { "*": ["./@mf-types/*"] } } } ``` #### 4. 消费生产者 修改入口页面(`src/routes/page.tsx`),引用生产者提供的组件,内容如下: ```tsx title='page.tsx' import ProviderImage from 'remote/Image'; import './index.css'; const Index = () => ( <div className="container-box"> <ProviderImage /> </div> ); export default Index; ``` ## CSS 闪烁问题 启动项目访问 `http://localhost:3007/`,发现SSR 正常工作,页面可以正常渲染,但是会有样式闪烁的问题。 这是因为生产者的样式文件无法注入到对应的 html 中。 此问题可以通过使用 `@module-federation/modern-js-v3` 提供的 [createremotessrcomponent](/zh/guide/framework/modernjs.md#createremotessrcomponent) 解决。 修改消费者引用生产者处的代码(`src/routes/page.tsx`): ```tsx title='page.tsx' import { getInstance } from '@module-federation/modern-js-v3/runtime'; import { createLazyComponent } from '@module-federation/modern-js-v3/react' import './index.css'; const RemoteSSRComponent = createLazyComponent({ instance: getInstance(), loader: () => import('remote/Image'), loading: 'loading...', export: 'default', fallback: ({ error }) => { if (error instanceof Error && error.message.includes('not exist')) { return <div>fallback - not existed id</div>; } return <div>fallback</div>; }, }); const Index = () => ( <div className="container-box"> <RemoteSSRComponent /> </div> ); export default Index; ``` 修改后重新访问页面,可以观测返回的 html 中会自动注入生产者的样式文件,从而解决 CSS 闪烁问题。