Skip to content

Commit 6bc13cf

Browse files
Dell-itdanpeen
andauthored
fix(vue3-bridge): resolved remote component prop handling and root container attribute passing (#3562)
Co-authored-by: CocooDanielle <[email protected]>
1 parent 5b391b5 commit 6bc13cf

File tree

6 files changed

+64
-25
lines changed

6 files changed

+64
-25
lines changed

.changeset/tough-pets-learn.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@module-federation/bridge-vue3': patch
3+
---
4+
5+
Fixed several issues:
6+
7+
1. Resolved inconsistencies in naming between `name` and `moduleName` to align with the type defined in `packages/bridge/bridge-shared/src/type.ts`. This also fixed an issue where `name` was being passed to the remote component, and if it was `<router-view>`, it caused rendering issues.
8+
9+
2. Issue: When passing props from a Vue 3 host application to a Vue 3 remote application created with `createRemoteComponent`, the props were being applied as attributes on the root container instead of being passed to the remote component.
10+
Fix: Set `inheritAttrs: false` in `remoteApp.tsx` and explicitly pass all attributes to the remote component using `useAttrs()`.
11+
12+
3. Added a `rootAttrs` parameter to `createRemoteComponent` to allow passing attributes to the root container where the remote application is mounted. This enables setting classes, identifiers, and other attributes for the container element.

apps/website-new/docs/en/practice/bridge/vue-bridge.mdx

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@ import { PackageManagerTabs } from '@theme';
2020
function createRemoteComponent<T, E extends keyof T>(
2121
options: {
2222
// Function to load remote application, e.g., loadRemote('remote1/export-app') or import('remote1/export-app')
23-
loader: () => Promise<T>,
23+
loader: () => Promise<T>;
2424
// Default is 'default', used to specify module export
25-
export?: E,
25+
export?: E;
2626
// Parameters that will be passed to defineAsyncComponent
2727
asyncComponentOptions?: Omit<AsyncComponentOptions, 'loader'>;
28+
// Attributes that will be bound to the root container where the remote Vue application will be mounted
29+
rootAttrs?: Record<string, unknown>;
2830
}
2931
): (props: {
3032
basename?: string;
@@ -115,14 +117,14 @@ export default defineConfig({
115117
// ./src/router.ts
116118
import * as bridge from '@module-federation/bridge-vue3';
117119

118-
const Remote2 = bridge.createRemoteComponent({ loader: () => loadRemote('remote1/export-app') });
120+
const Remote2 = bridge.createRemoteComponent({ loader: () => loadRemote('remote1/export-app'), rootAttrs: {class: 'root-element-class'} });
119121

120122
const router = createRouter({
121123
history: createWebHistory(),
122124
routes: [
123125
// Define your routes here
124126
{ path: '/', component: Home },
125-
{ path: '/remote1/:pathMatch(.*)*', component: Remote2 },
127+
{ path: '/remote1/:pathMatch(.*)*', component: Remote2, props: { foo: 'bar' } },
126128
// Other routes
127129
],
128130
});
@@ -137,11 +139,13 @@ export default router;
137139
function createRemoteComponent<T, E extends keyof T>(
138140
options: {
139141
// Function to load remote application, e.g., loadRemote('remote1/export-app') or import('remote1/export-app')
140-
loader: () => Promise<T>,
142+
loader: () => Promise<T>;
141143
// Default is 'default', used to specify module export
142144
export?: E;
143145
// Parameters that will be passed to defineAsyncComponent
144146
asyncComponentOptions?: Omit<AsyncComponentOptions, 'loader'>;
147+
// Attributes that will be bound to the root container where the remote Vue application will be mounted
148+
rootAttrs?: Record<string, unknown>;
145149
}
146150
): (props: {
147151
basename?: string;
@@ -164,6 +168,9 @@ const Remote1App = createRemoteComponent({ loader: () => loadRemote('remote1/exp
164168
* `asyncComponentOptions`
165169
* type: `Omit<AsyncComponentOptions, 'loader'>`
166170
* Purpose: Parameters that will be passed to defineAsyncComponent, except for the loader parameter
171+
* `rootAttrs`
172+
* type: `Record<string, unknown>`
173+
* Purpose: Attributes that will be bound to the root container where the remote Vue application will be mounted
167174
```tsx
168175
// remote
169176
export const provider = createBridgeComponent({
@@ -184,14 +191,14 @@ const Remote1App = createRemoteComponent({
184191
```tsx
185192
import * as bridge from '@module-federation/bridge-vue3';
186193

187-
const Remote2 = bridge.createRemoteComponent({ loader: () => loadRemote('remote1/export-app') });
194+
const Remote2 = bridge.createRemoteComponent({ loader: () => loadRemote('remote1/export-app'), rootAttrs: {class: 'root-element-class'} });
188195

189196
const router = createRouter({
190197
history: createWebHistory(),
191198
routes: [
192199
// Define your routes here
193200
{ path: '/', component: Home },
194-
{ path: '/remote1/:pathMatch(.*)*', component: Remote2 },
201+
{ path: '/remote1/:pathMatch(.*)*', component: Remote2, props: { foo: 'bar' } },
195202
// Other routes
196203
],
197204
});

apps/website-new/docs/zh/practice/bridge/vue-bridge.mdx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ import { PackageManagerTabs } from '@theme';
2121
function createRemoteComponent<T, E extends keyof T>(
2222
options: {
2323
// Function to load remote application, e.g., loadRemote('remote1/export-app') or import('remote1/export-app')
24-
loader: () => Promise<T>,
24+
loader: () => Promise<T>;
2525
// Default is 'default', used to specify module export
26-
export?: E,
26+
export?: E;
2727
// Parameters that will be passed to defineAsyncComponent
2828
asyncComponentOptions?: Omit<AsyncComponentOptions, 'loader'>;
29+
// Attributes that will be bound to the root container where the remote Vue application will be mounted
30+
rootAttrs?: Record<string, unknown>;
2931
}
3032
): (props: {
3133
basename?: string;
@@ -141,11 +143,13 @@ export default router;
141143
function createRemoteComponent<T, E extends keyof T>(
142144
options: {
143145
// Function to load remote application, e.g., loadRemote('remote1/export-app') or import('remote1/export-app')
144-
loader: () => Promise<T>,
146+
loader: () => Promise<T>;
145147
// Default is 'default', used to specify module export
146148
export?: E;
147149
// Parameters that will be passed to defineAsyncComponent
148150
asyncComponentOptions?: Omit<AsyncComponentOptions, 'loader'>;
151+
// Attributes that will be bound to the root container where the remote Vue application will be mounted
152+
rootAttrs?: Record<string, unknown>;
149153
}
150154
): (props: {
151155
basename?: string;
@@ -168,6 +172,9 @@ const Remote1App = createRemoteComponent({ loader: () => loadRemote('remote1/exp
168172
* `asyncComponentOptions`
169173
* type: `Omit<AsyncComponentOptions, 'loader'>`
170174
* Purpose: Parameters that will be passed to defineAsyncComponent, except for the loader parameter
175+
* `rootAttrs`
176+
* type: `Record<string, unknown>`
177+
* Purpose: Attributes that will be bound to the root container where the remote Vue application will be mounted
171178
```tsx
172179
// remote
173180
export const provider = createBridgeComponent({
@@ -188,14 +195,14 @@ const Remote1App = createRemoteComponent({
188195
```tsx
189196
import * as bridge from '@module-federation/bridge-vue3';
190197

191-
const Remote2 = bridge.createRemoteComponent({ loader: () => loadRemote('remote1/export-app') });
198+
const Remote2 = bridge.createRemoteComponent({ loader: () => loadRemote('remote1/export-app'), rootAttrs: {class: 'root-element-class'} });
192199

193200
const router = createRouter({
194201
history: createWebHistory(),
195202
routes: [
196203
// Define your routes here
197204
{ path: '/', component: Home },
198-
{ path: '/remote1/:pathMatch(.*)*', component: Remote2 },
205+
{ path: '/remote1/:pathMatch(.*)*', component: Remote2, props: { foo: 'bar' } },
199206
// Other routes
200207
],
201208
});

packages/bridge/vue3-bridge/src/create.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export function createRemoteComponent(info: {
99
loader: () => Promise<any>;
1010
export?: string;
1111
asyncComponentOptions?: Omit<AsyncComponentOptions, 'loader'>;
12+
rootAttrs?: Record<string, unknown>;
1213
}) {
1314
return defineAsyncComponent({
1415
__APP_VERSION__,
@@ -39,7 +40,7 @@ export function createRemoteComponent(info: {
3940

4041
LoggerInstance.debug(
4142
`createRemoteComponent LazyComponent loadRemote info >>>`,
42-
{ name: moduleName, module, exportName, basename, route },
43+
{ moduleName, module, exportName, basename, route },
4344
);
4445

4546
if (exportName in module && typeof exportFn === 'function') {
@@ -49,6 +50,7 @@ export function createRemoteComponent(info: {
4950
moduleName,
5051
providerInfo: exportFn,
5152
basename,
53+
rootAttrs: info.rootAttrs,
5254
});
5355
},
5456
};

packages/bridge/vue3-bridge/src/provider.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export function createBridgeComponent(bridgeInfo: ProviderFnParams) {
3030
LoggerInstance.debug(`createBridgeComponent render Info`, info);
3131
const { moduleName, dom, basename, memoryRoute, ...propsInfo } = info;
3232
const app = Vue.createApp(bridgeInfo.rootComponent, propsInfo);
33-
rootMap.set(info.dom, app);
33+
rootMap.set(dom, app);
3434

3535
const beforeBridgeRenderRes =
3636
await instance?.bridgeHook?.lifecycle?.beforeBridgeRender?.emit(info);
@@ -61,19 +61,19 @@ export function createBridgeComponent(bridgeInfo: ProviderFnParams) {
6161
routes: bridgeOptions.router.getRoutes(),
6262
});
6363

64-
LoggerInstance.log(`createBridgeComponent render router info>>>`, {
65-
name: info.moduleName,
64+
LoggerInstance.debug(`createBridgeComponent render router info>>>`, {
65+
moduleName,
6666
router,
6767
});
6868
// memory route Initializes the route
69-
if (info.memoryRoute) {
70-
await router.push(info.memoryRoute.entryPath);
69+
if (memoryRoute) {
70+
await router.push(memoryRoute.entryPath);
7171
}
7272

7373
app.use(router);
7474
}
7575

76-
app.mount(info.dom);
76+
app.mount(dom);
7777
instance?.bridgeHook?.lifecycle?.afterBridgeRender?.emit(info);
7878
},
7979
destroy(info: { dom: HTMLElement }) {

packages/bridge/vue3-bridge/src/remoteApp.tsx

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import { ref, onMounted, onBeforeUnmount, watch, defineComponent } from 'vue';
1+
import {
2+
ref,
3+
onMounted,
4+
onBeforeUnmount,
5+
watch,
6+
defineComponent,
7+
useAttrs,
8+
} from 'vue';
29
import { dispatchPopstateEnv } from '@module-federation/bridge-shared';
310
import { useRoute } from 'vue-router';
411
import { LoggerInstance } from './utils';
@@ -11,20 +18,24 @@ export default defineComponent({
1118
basename: String,
1219
memoryRoute: Object,
1320
providerInfo: Function,
21+
rootAttrs: Object,
1422
},
23+
inheritAttrs: false,
1524
setup(props) {
1625
const rootRef = ref(null);
1726
const providerInfoRef = ref(null);
1827
const pathname = ref('');
1928
const route = useRoute();
2029
const hostInstance = getInstance();
30+
const componentAttrs = useAttrs();
2131

22-
const renderComponent = () => {
32+
const renderComponent = async () => {
2333
const providerReturn = props.providerInfo?.();
2434
providerInfoRef.value = providerReturn;
2535

2636
let renderProps = {
27-
name: props.moduleName,
37+
...componentAttrs,
38+
moduleName: props.moduleName,
2839
dom: rootRef.value,
2940
basename: props.basename,
3041
memoryRoute: props.memoryRoute,
@@ -35,9 +46,9 @@ export default defineComponent({
3546
);
3647

3748
const beforeBridgeRenderRes =
38-
hostInstance?.bridgeHook?.lifecycle?.beforeBridgeRender?.emit(
49+
(await hostInstance?.bridgeHook?.lifecycle?.beforeBridgeRender?.emit(
3950
renderProps,
40-
) || {};
51+
)) || {};
4152

4253
renderProps = { ...renderProps, ...beforeBridgeRenderRes.extraProps };
4354
providerReturn.render(renderProps);
@@ -92,6 +103,6 @@ export default defineComponent({
92103
});
93104
});
94105

95-
return () => <div ref={rootRef}></div>;
106+
return () => <div {...(props.rootAttrs || {})} ref={rootRef}></div>;
96107
},
97108
});

0 commit comments

Comments
 (0)