nextjs初探之微前端

908 阅读2分钟

当前讨论的范围为主子应用都为nextjs。
此文档作为调研的一部分总结,有很多没考虑的点希望大家指出。

背景

在对项目进行模块拆分时也希望对一些web服务进行拆分。
比如一些独立的应用可以通过微前端的形式部署在主应用下。
有开发联调的成本也有隔离后独立部署的便利,看大家的取舍。
假设有两个web项目(知识库和一个主应用)

image.png

qiankun

子应用下需要设置webpack的配置

const nextConfig = {
  //   assetPrefix: process.env.ASSET_PREFIX || '', // 子应用的资源路径,
  //   reactStrictMode: true,
  //   swcMinify: true,
  //   crossOrigin: 'anonymous',
  webpack: (config) => {
    config.output.library = `${packageName}-[name]`;
    config.output.libraryTarget = 'umd';
    config.output.chunkLoadingGlobal = `webpackJsonp_${packageName}`;

    return config;
  },
}

其中设置libraryTarget会导致子应用启动报错。 关于这一点基本就不考虑qiankun了 还有一点是nextjs下如何设置qiankun的生命周期也暂时没找到方案。
然后去看了下single-spa

single-spa

github.com/single-spa/… 提示暂不支持

模块联邦

github.com/module-fede… 这里有具体的例子 github.com/module-fede…

image.png 上面给我们演示了next14下应该如何使用 可以看到文件夹组织形式必须是pages/_app
这样会导致nextjs14的一些新的路由特性没法用。

wujie

暂时考虑用wujie
主要是因为不需要对webpack进行配置, 看下主应用的代码:

'use client';

function Home() {
    if (typeof window !== 'undefined') {
      const degrade =
        window.localStorage.getItem('degrade') === 'true' ||
        !window.Proxy ||
        !window.CustomElementRegistry;
      const { setupApp, preloadApp } = WujieReact;
      /**
       * 配置应用,主要是设置默认配置
       * preloadApp、startApp的配置会基于这个配置做覆盖
       */
      setupApp({
        name: 'knowledge',
        url: hostMap('//localhost:4001/'),
        exec: true,
        fetch: (url: RequestInfo, options: any) => {
          return window.fetch(url, { ...options, credentials: 'omit' });
        },
        plugins,
        // prefix: { 'prefix-dialog': '/dialog', 'prefix-location': '/location' },
        degrade,
        ...lifecycle,
      } as any);

      if (window.localStorage.getItem('preload') !== 'false') {
        preloadApp({
          name: 'knowledge',
          url: '//localhost:4001/',
        });
      }
    }
    
    // ...
 }

引入子应用

'use client';
import { usePathname } from 'next/navigation';
import WujieReact from 'wujie-react';
import hostMap from '~/micro/hostmap';
export default function Page() {
  const path = usePathname().replace('/knowledge', '').replace('/', '');
  const reacturl = hostMap('//localhost:4001/') + path;

  const props = {
    jump: (name: string) => {
      console.log('name::', name);
    },
  };

  return (
    <WujieReact
      width="100%"
      height="100%"
      name="knowledge"
      url={reacturl}
      sync={!path}
      props={props}
    ></WujieReact>
  );
}

子应用下配置一下跨域(next.config.mjs)

/** @type {import('next').NextConfig} */

const nextConfig = {
  assetPrefix: process.env.ASSET_PREFIX || 'http://localhost:4001', // 子应用的资源路径,
  async headers() {
    return [
      {
        source: '/:path*', // 适用所有路由
        headers: [
          {
            key: 'Content-Security-Policy',
            value: "frame-ancestors 'self' *;", // 允许 iframe 加载
          },
          {
            key: 'Access-Control-Allow-Origin',
            value: '*',
          },
          {
            key: 'Access-Control-Allow-Methods',
            value: 'GET,POST,OPTIONS',
          },
          {
            key: 'Access-Control-Allow-Headers',
            value: 'Origin, X-Requested-With, Content-Type, Accept',
          },
        ],
      },
    ];
  },
};

export default nextConfig;

这样基本就可以实现一个主应用加载子应用了。

所以暂时用wujie在做,但是在nextjs框架+monorepo的背景下对于微前端应该要再思考一下出发点

再收集一些资料结合一些开发实践再总结了。