模块联邦(Vite)
模块联邦
什么是模块联邦?模块联邦是
Webpack 5
的新特性,它允许不同的应用或组件之间进行动态的模块共享。为什么要使用模块联邦?模块联邦可以解决多个应用或组件之间的依赖问题,避免重复安装依赖,提高开发效率。
项目地址
项目目录
1 | ├── 根目录 |
项目搭建
配置 pnpm workspaces (也可以直接创建两个项目)
在根目录下执行
pnpm init -y
初始化项目然后在
package.json
中配置workspaces
1
2
3
4
5
6
7
8
9
10
11
12
13{
"name": "root",
"private": true,
"workspaces": ["packages/*"],
"scripts": {
"preinstall": "npx only-allow pnpm",
"remote": "pnpm -F remote build && pnpm -F remote run preview",
"host": "pnpm -F host dev",
"clean": "pnpm -r exec rm -rf node_modules && rimraf ./node_modules"
},
"dependencies": {},
"devDependencies": {}
}在根目录下创建
pnpm-workspace.yaml
并添加下面配置1
2packages:
- 'packages/*'然后在根目录下创建
packages
目录 (注意:名称要跟package.json
中workspaces
以及pnpm-workspace.yaml
中packages
一致)
创建 host
、remote
应用
依次执行下面命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14# 进入 packages 目录
cd ./packages
# 创建项目 host
pnpm create vite
# Project name
host
# Select a framework
React
# Select a variant
TypeScript + SWC在
packages/host/vite.config.ts
中配置启动端口
1
2
3
4
5
6export default defineConfig({
plugins: [react()],
server: {
port: 8000,
},
});
依次执行下面命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14# 进入 packages 目录
cd ./packages
# 创建项目 remote
pnpm create vite
# Project name
remote
# Select a framework
React
# Select a variant
TypeScript + SWC在
packages/remote/vite.config.ts
中配置启动端口
1
2
3
4
5
6export default defineConfig({
plugins: [react()],
server: {
port: 8001,
},
});
安装依赖项
为两个项目添加 @originjs/vite-plugin-federation
1 | # -r 为所有项目执行命令 |
配置 host
、remote
应用
在
packages/host/src/App.tsx
文件中导出并使用CustomButton
组件1
2
3
4
5
6
7
8
9
10
11
12
13
14import './App.css';
import React from 'react';
const CustomButton = React.lazy(() => import('remote/CustomButton'));
function App() {
return (
<>
<CustomButton text="Hello Host" />
</>
);
}
export default App;在
packages/host/src/vite-env.d.ts
文件中添加下面代码, 防止引入远程模块时爆红
1
2
3
4
5
6
7/// <reference types="vite/client" />
declare module 'remote/*' {
import { ComponentType } from 'react';
const component: ComponentType<any>;
export default component;
}在
packages/host/vite.config.ts
文件中配置@originjs/vite-plugin-federation
插件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react-swc';
import federation from '@originjs/vite-plugin-federation';
// https://vite.dev/config/
export default defineConfig({
plugins: [
react(),
federation({
// 必填, 作为远程模块的模块名称
name: 'host',
remotes: {
// 作为本地模块引用的远程模块入口文件, [远程模块名称]: [远程模块入口文件地址]
remote: 'http://localhost:8001/assets/remoteEntry.js',
},
shared: ['react', 'react-dom'],
}),
],
server: {
port: 8000,
},
});
在
packages/remote/src/components/CustomButton/index.tsx
中自定义CustomButton
组件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18interface Props {
/** 按钮文案 */
text?: string;
/** 按钮点击事件 */
onClick?: () => void;
/** 按钮样式 */
style?: React.CSSProperties;
/** 按钮类名 */
className?: string;
}
/** 自定义按钮 */
const CustomButton = (props: Props) => {
const { text, ...rest } = props;
return <button {...rest}>{text}</button>;
};
export default CustomButton;在
packages/remote/src/components/index.ts
中导出CustomButton
组件1
export { default as CustomButton } from './CustomButton/index.tsx';
在
packages/remote/src/App.tsx
文件使用CustomButton
组件1
2
3
4
5
6
7
8
9
10
11
12import './App.css';
import { CustomButton } from './components';
function App() {
return (
<>
<CustomButton text="Hello Remote" />
</>
);
}
export default App;在
packages/remote/vite.config.ts
文件中配置@originjs/vite-plugin-federation
插件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react-swc';
import federation from '@originjs/vite-plugin-federation';
// https://vite.dev/config/
// federation参数参考 https://github.com/originjs/vite-plugin-federation?tab=readme-ov-file#configuration
export default defineConfig({
plugins: [
react(),
federation({
// 必填, 作为远程模块的模块名称
name: 'remote',
// 非必填, 作为远程模块的入口文件, 默认为 remoteEntry.js
filename: 'remoteEntry.js',
// 向公众公开的组件列表
exposes: {
'./CustomButton': './src/components/CustomButton/index.tsx',
},
// 本地和远程模块共享的依赖项. 本地模块需要配置所有使用的远程模块的依赖; 远程模块需要配置外部提供的组件的依赖.
shared: ['react', 'react-dom'],
}),
],
server: {
port: 8001,
},
preview: {
// 生产端口
// 只有 Host 端支持 dev 模式,Remote 端要求使用 生成 RemoteEntry.js 包.
// 详见: https://github.com/originjs/vite-plugin-federation?tab=readme-ov-file#vite-dev-mode
port: 8001,
},
build: {
// 处理报错: ERROR: await is not available in the configured target environmentTop-level
// 详见: https://github.com/originjs/vite-plugin-federation?tab=readme-ov-file#error-top-level-await-is-not-available-in-the-configured-target-environment
target: 'esnext',
},
});
启动项目
先启动
remote
项目 (根目录下执行)1
2
3
4
5# 需要先打包项目, 生成 remoteEntry.js
pnpm -F remote build
# 启动项目
pnpm -F remote run preview再启动
host
项目 (根目录下执行)1
pnpm -F host dev