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

Support Modern.js SSR #2348

Open
2heal1 opened this issue Apr 18, 2024 · 0 comments
Open

Support Modern.js SSR #2348

2heal1 opened this issue Apr 18, 2024 · 0 comments
Assignees
Milestone

Comments

@2heal1
Copy link
Member

2heal1 commented Apr 18, 2024

Changelog 2024-05-22:draft
Implementation PR  WIP...

Summary

Module Federation supports Modern.js SSR , includes stream ssr and string ssr two modes.

Basic example

  • Static remote (register remotes in build config)
// config
new ModuleFederation({
  remotes: {
    remote: 'remote@http://localhost:3001/mf-manifest.json'
  }
})

// usage
import Comp from 'remote/Content'
  • Dynamic remote(register remotes in runtime)
import React, { Suspense } from 'react';
import Button from 'antd/lib/button';
import {
  loadRemote,
  registerRemotes,
} from '@module-federation/enhanced/runtime';

registerRemotes([
  {
    name: 'dynamic_remote',
    entry: 'http://localhost:3008/mf-stats.json',
  },
]);

const Comp = React.lazy(() =>
loadRemote('dynamic_remote/Image').then((m) => {
    return m;
  }),
);

export default (): JSX.Element => (
  <Suspense fallback={'loading'}>
    <Comp />
  </Suspense>
);

Motivation

  • Optimize first screen performance
  • Support ssr can help to investigate more performance optimization ways

Detailed design

Update stats/manifest/snapshot

Stats/Manifest

Add ssr related fields

// metaData
interface BasicStatsMetaData {
+ ssrRemoteEntry?: ResourceInfo;
}

Snapshot

Add ssr related fields

interface BasicProviderModuleInfo extends BasicModuleInfo {
  // ssrRemoteEntry/ssrRemoteEntryType only appear while manifest has serveSideRemoteEntry field
+ ssrRemoteEntry?: string;
+ ssrRemoteEntryType?: RemoteEntryType
}

render mode

only support stream ssr mode

CSS flickering issue

Leverage mf-stats.json , we can easily get the module css assets, and insert them to html

// encapsulation component for users
const Comp = React.lazy(() =>
  loadRemote('dynamic_remote/Image').then((m) => {
    return {
      default:()=><div>
        <link href='http://localhost:3008/static/css/async/__federation_expose_Image.css' rel="stylesheet" type="text/css" />
        <span>11</span>
        <m.default />
      </div>
    };
  }),
);

Cache Strategy

For static remote , there won't be any serious problems. But for dynamic remotes , it may add endless dynamic remotes , and cause memory crashes.

To avoid the issue , it needs to add LRU cache:

  • Delete the reference of least usage mf instance, include the shared and expose modules

Data Fetch

Use Modernjs Data loader

Downgrade strategy

Provide two ways to help CSR cab work normally while SSR failed

  1. Use the runtime version plugin to downgrade the latest available modules
  • runtime version plugin: allows users downgrade specific fallback modules while the entry can not get normally
  1. Provide encapsulation component for users which provide fallback

Dev

LiveReload

fetch all loaded remotes before render host , and judge whether remoteEntry content hash has changed . If yes , flush chunks .

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

export const mfPluginSSR = (): Plugin => ({
  name: '@module-federation/modern-js',

  setup: () => ({
    async init({ context }, next) {
      if (typeof window !== 'undefined') {
        return next({ context });
      }
      const nodeUtils = await import('@module-federation/node/utils');
      const shouldUpdate = await nodeUtils.revalidate();
      if (shouldUpdate) {
        console.log('should HMR', shouldUpdate);
      }
      return next({ context });
    },
  }),
});

Dynamic remote type hints

notify local broker server while calling loadRemotes , and then the broker server will have the full relationship .

import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime';

const notifyDynamicRemotePlugin: () => FederationRuntimePlugin = () => ({
  name: 'notify-dynamic-remote-plugin',
  loadRemote(args) {
    if(isDynamicRemote(args)){
        notifyBrokerServer(args.remote);
    }
    return args;
  },
});
export default notifyDynamicRemotePlugin;

debug

Because node server can not access local env , so not support yet.

It will auto forceCSR when accepting the debug info:

  • Chrome devtools
  • Skip collect downgrade rate

Drawbacks

  • Only support stream ssr , it means that react under version 18 is not supported
@2heal1 2heal1 added this to the 0.2 milestone Apr 18, 2024
@2heal1 2heal1 self-assigned this Apr 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant