Загрузка пакета веб-пакетов через тег скрипта для микрофронтендного подхода

Я слежу за парой статей о том, как реализовать простой подход к микро-интерфейсу с помощью React (здесь и здесь, см. образцы репозиториев внизу вопроса).

Он отлично работает, когда два приложения (корневое приложение и вспомогательное приложение) работают на своих соответствующих серверах разработки. Однако когда я развертываю артефакты сборки на реальном веб-сервере, это не работает. Это важный код в корневом приложении:

function MicroFrontend({name, host, history}) {

    useEffect(() => {
        const scriptId = `micro-frontend-script-${name}`;

        const renderMicroFrontend = () => {
            window["render" + name](name + "-container", history);
        };

        if (document.getElementById(scriptId)) {
            renderMicroFrontend();
            return;
        }

        fetch(`${host}/asset-manifest.json`)
            .then((res) => res.json())
            .then((manifest) => {
                const script = document.createElement("script");
                script.id = scriptId;
                script.crossOrigin = "";
                script.src = `${host}${manifest.files["main.js"]}`;
                script.onload = () => {
                    renderMicroFrontend();
                };
                document.head.appendChild(script);
            });

        return () => {
            window[`unmount${name}`] && window[`unmount${name}`](`${name}-container`);
        };
    });

    return <main id={`${name}-container`}/>;
}

MicroFrontend.defaultProps = {
    document,
    window,
};

Когда я нажимаю кнопку, которая загружает main.js для микроприложения, оно отлично работает до момента, когда вызывает renderMicroFrontend выше. Затем я получаю в своем браузере такую ​​ошибку: Uncaught TypeError: window[("render" + t)] is not a function

Это потому, что он не может найти функцию, которая загружает микрофронтенд, который должен быть на window. Когда я запускаю два приложения на сервере разработки, у меня есть правильная функция на window, и она работает. Когда я выполняю те же шаги с двумя приложениями, развернутыми на реальном сервере (после запуска npm run build), вместо функции renderMicroApp на моем window у меня есть другая переменная с именем: webpackJsonpmicro-app.

Я понял, что это связано с параметром output.library (и / или связанными с ним параметрами) в веб-пакете из документации веб-пакета:

output.jsonpFunction. string = 'webpackJsonp' Используется только в том случае, если для цели задано значение «web», при котором для загрузки фрагментов по запросу используется JSONP. При использовании параметра output.library имя библиотеки автоматически объединяется со значением output.jsonpFunction.

Я в основном хочу, чтобы пакет / скрипт был загружен и оценен, чтобы функция renderMicroApp была доступна в окне, но я не понимаю, какие настройки веб-пакета мне нужны, чтобы это работало, поскольку существует множество различных вариантов перестановок.

Для справки я использую следующие переопределения конфигурации в микроприложении (поверх react-app-rewired):

module.exports = {
  webpack: (config, env) => {
    config.optimization.runtimeChunk = false;
    config.optimization.splitChunks = {
      cacheGroups: {
        default: false,
      },
    };

    config.output.filename = "static/js/[name].js";

    config.plugins[5].options.filename = "static/css/[name].css";
    config.plugins[5].options.moduleFilename = () => "static/css/main.css";
    return config;
  },
};

И в моем index.tsx (в микроприложении) я выставляю функцию в окне:

window.renderMicroApp = (containerId, history) => {
  ReactDOM.render(<AppRoot />, document.getElementById(containerId));
  serviceWorker.unregister();
};

Некоторые образцы репозиториев:


person Otto    schedule 11.09.2020    source источник


Ответы (1)


Вы можете попробовать с помощью terser-webpack-plugin.

const TerserPlugin = require('terser-webpack-plugin');

module.exports = {
  webpack: (config, env) => {
    config.optimization.minimize = true;
    config.optimization.minimizer = [new TerserPlugin({
      terserOptions: { keep_fnames: true }
    })];

    config.optimization.runtimeChunk = false;
    config.optimization.splitChunks = {
      cacheGroups: {
        default: false,
      },
    };

    config.output.filename = "static/js/[name].js";

    config.plugins[5].options.filename = "static/css/[name].css";
    config.plugins[5].options.moduleFilename = () => "static/css/main.css";
    return config;
  }
};
person Daniele Ricci    schedule 15.09.2020
comment
Так вы говорите, проблема в том, что имена функций не сохраняются? Разве публичные функции не должны всегда сохранять имена функций? Если я помещаю функцию в переменную окна, как показано ниже, не следует ли ее всегда сохранять ?: window.renderSubApp = (containerId, history) = ›{ReactDOM.render (‹ AppRoot / ›, document.getElementById (containerId)); serviceWorker.unregister (); }; - person Otto; 17.09.2020