Давайте поговорим о проблеме агрегации микро-интерфейса из этой статьи Meituan.

внешний интерфейс анализ данных React.js Redux

Недавно я планирую добавить вопрос в свой банк вопросов для собеседования, чтобы проверить возможности дизайна приложения кандидата.Этот вопрос также является производным от реальной бизнес-проблемы, возникшей недавно:

Мой нынешний технический мидл-офисный отдел разработал множество инструментальных приложений для бизнес-отдела компании, таких как анализ данных, A/B-тестирование, развертывание и выпуск платформ и так далее. Все они основаны на фреймворке React, а затем разработаны с использованием различных фреймворков управления состоянием, таких как redux, mobx или dva. Эти приложения поддерживаются и разрабатываются разными фронтенд-командами, поэтому нетрудно представить, что все разрабатывали одни и те же функции или одни и те же компоненты с перекрытием, такие как проверка входа (все на основе OAuth на уровне компании), такие как ошибка обработка и, например, отправка сообщения пользователю и так далее. Поскольку они разбросаны по разным доменным именам, но тесно связаны между собой и неудобны в использовании, мы намерены объединить их под одним порталом (сайтом) с общедоступной панелью навигации вверху и текущим конкретным приложением внизу. Переключайтесь между различными приложениями на текущей странице через меню панели навигации. Дайте пользователям ощущение использования одного приложения. Как разработать эту схему агрегации?

Если вы слышали понятие «Микросервисы», вам нетрудно понять «Микрофронтенды». Как и в случае с внутренними приложениями, когда внешнее приложение становится очень большим, его становится сложно поддерживать и оно становится нестабильным. Разделение больших приложений на более мелкие позволяет каждой профессиональной команде сосредоточиться на своей собственной функциональности, упрощая тестирование, развертывание и выпуск.

Но таких разделений много. Наиболее распространенным является разделение одного приложения на несколько независимых приложений и достижение плавного переключения через панели навигации и динамическую загрузку. Такие приложения могут даже создаваться с использованием разных технологических стеков. :

Модули на одной странице также можно разделить на разные микроинтерфейсы, разработанные разными командами с использованием разных технологических стеков:

Это две крайности. Другие сценарии включают, но не ограничиваются

  • Интеграция сторонних приложений SaaS,
  • Интегрируйте сторонние частные серверные приложения (например, gitlab, развернутый внутри компании)
  • Обработка всех вышеперечисленных ситуаций в рамках одного и того же стека технологий и т. д.

Точно так же иногда нам также необходимо выполнять обратные (пост) операции, например, в примере в начале, когда есть несколько приложений, их нужно агрегировать в одно приложение.

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

В конечном счете проблема агрегации микрофронтендов на самом деле очень сложная, сложность заключается в том, что подсценариев слишком много, и дать единое решение невозможно, поэтому проблемы, которые предстоит решать в этой статье ограничиваются теми, что указаны в заголовке, как описано. Но даже в этом случае идеи, описанные в этой статье, и рассмотренные вопросы все же могут быть использованы для справки, когда вы будете решать связанные с ними вопросы в будущем.

Может быть, после прочтения этого вы сможете сделать паузу, взять ручку и бумагу и потратить десять-пятнадцать минут на размышления о том, как решить эту проблему. При опросе человека не важны результаты, которые он дает, важно то, как он решает проблему и хорошо ли рассматривает проблему. Эксперт и новичок могут дать один и тот же ответ, но эксперт — это результат тщательного рассмотрения, и новичку может посчастливиться подобрать ключ.

При решении этой проблемы коллега порекомендовал мне статью о микрофронтендах из технического блога Meituan«Создание одностраничных приложений с микроинтерфейсами». Эта статья предоставляет нам класс решения проблемы, и это решение может быть использовано нами в качестве подсказки для решения такого рода проблемы. Но есть еще спорные моменты в схеме. В следующей части статьи мы поговорим об этих спорных моментах, сосредоточив внимание на двух моментах в статье:Механизм регистрацииа такжеПространства имен.

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

Механизм регистрации

Потребности Meituan совпадают с нашими потребностями.

Система управления персоналом была преобразована в систему с одним доменным именем и одним набором стилей презентации.

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

Подход Meituan заключается в создании нового проекта общедоступного уровня под названием «Портал» («Портал»?), и все подпроекты должны зарегистрировать свою собственную информацию на этом портале, включая маршрутизацию и пространства имен. Позже во всех проектах эта функция будет представлена ​​как компонент. Затем, когда каждый проект подключается к сети, динамически загружайте ресурсы подпроекта (скрипты/стили) через маршрутизацию (панель навигации?), чтобы динамически загружать подпроекты.

Обычно при выполнении обратной агрегации в дополнение к созданию нового проекта, аналогичного контейнеру/уровню записи выше, вам также необходимо изменить исходный проект.Изменение исходного проекта болезненно, потому что ему нужна прямая и обратная совместимость. Если это проект, поддерживаемый другой командой, вам также нужно подтолкнуть «людей» к выполнению этой работы. Так что самое идеальное состояние — это «неинвазивная» трансформация, то есть исходный проект неосязаем. Это кажется более сложным, конечно, следующая лучшая вещь, наша цель — максимально минимизировать стоимость изменений.

В плане Meituan они хотели передать публичную библиотеку классов Portal для поддержки и загрузки проекта без навязчивой трансформации, поэтому они решили заменить подпроекты на этапе строительства.require('react')метод, изменить наwindow.app.require('react'), который является частным порталомrequireметод. Это, по-видимому, уменьшает количество проблем, связанных с подпроектами, и в то же время обеспечивает единое управление публичными библиотеками классов;

Но нужно ли нам общее управление библиотеками?

Мы можем понять, почему они управляют общедоступной библиотекой классов, с одной стороны, чтобы избежать повторной загрузки ресурсов, таких какreact, react-dom, lodashПосле классовой библиотеки использовать каждый проект, с другой стороны, чтобы убедиться, что все элементы внутри библиотеки компонентов используются в последнем выпуске

Но на практике подпроекты делаются разными командами из разных периодов, поэтому даже базовая библиотека классов будет иметь несогласованные версии (у вас могут возникнуть вопросы: почему они вовремя не обновляются? Причин много, например, если функция доступна, она больше не будет разрабатываться.Например, другие используемые библиотеки классов совместимы только с этой версией)

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

Тогда мы все еще можем сделать шаг назад и поделиться некоторыми версиями компонентов. Когда подпроект зарегистрирован, ему необходимо зарегистрировать свою зависимую библиотеку классов и соответствующую версию в Portal. Это поднимает еще один вопрос: как управлять несколькими версиями одной и той же библиотеки в одностраничном приложении? Например, проект А зависит отreact@15, проект B зависит отreact@16, когда пользователь переключается с A на B и обратно на A,react@15Вам нужно перезагрузить? Совершенно очевидно, что библиотека классов должна храниться. В лучшем случае, мы не должны полагаться на HTTP-кэш, а должны поместить уже загруженную библиотеку классов в память, тогда вам также необходимо разрешить тот же конфликт пространства имен классов в памяти.

Итак, вкратце:

  1. Если для унификации общей библиотеки классов нужно взломать проект
  2. Если вы настаиваете на повторном использовании библиотеки классов, вам нужно более сложное управление общей библиотекой классов.

Еще одна очень удачная часть решения Meituan в том, что все под-приложения написаны с использованием фреймворка Redux (ведь при регистрации в Портале нужно еще и редукторы прописывать? А про удаление редюсеров при переключении проектов я, кажется, не читал? ), поэтому они используют фреймворк Redux. Тем не менее, старая поговорка, на самом деле, такой удачи не бывает.При выполнении агрегации переднего плана вам нужно подумать, как обеспечить бесперебойную работу различных фреймворков потока данных после динамической загрузки, такой как переход с Redux на Mobx? Интегрировать все возможные фреймворки в Portal заранее? Как сделать управление версиями?

Все вроде бы старается для динамической загрузки.Но действительно ли нам нужна динамическая загрузка?

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

Но почему он должен быть динамически загружен? Более быстрая производительность? Не обязательно, нужно отрисовывать столько запросов и столько DOM-узлов (в те времена не было ни React, ни Virtual DOM), это может быть не быстрее, чем вся страница, отображаемая сервером; лучший опыт? Я не думаю, что одностраничный опыт будет плохим (обратитесь к Youku); более ужасным является то, что это вызовет трудности с обслуживанием: представьте, что новый студент, вероятно, забудет определенную функцию при изменении динамически загружаемой функции. Логика нуждается в обновлении.

Мое личное мнение, что если ваши агрегированные проекты независимы, лучше упаковать их как независимые приложения, а затем прыгать между страницами. И нет необходимости заниматься управлением общедоступной библиотекой классов, и она напрямую упаковывается в финальный скрипт проекта, даже если он повторяется.

В этот момент вы можете удивиться: после долгого прочтения статьи вы дали мне такой «простой» план? !

Молодёжь, я вас понимаю, каждый программист рождается с любовью к технологии "сумасшедшего, крутого и висящего на небе". Проще говоря, чем частичнее, тем лучше, чем сложнее, тем лучше. Но когда это реализуется на уровне компании, ваш босс, ваш босс, чего они хотят, так это стабильности и стоимости бизнеса. Поэтому при выборе плана реализации эти два пункта являются первыми моментами, которые мы должны учитывать, например:

  • Вариант 1: Реализация 3-балльной функции требует 5-балльной стоимости и 3-балльного риска.
  • Вариант 2: Для достижения функции в 1 балл требуются затраты в 1 балл и риск в 1 балл.

Последнее на самом деле выгоднее.

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

По крайней мере, в этом сценарии, я не думаю, что нам нужна более сложная схема.

В каких сценариях следует учитывать динамическую загрузку? Например, совместное использование уровня компонента (страницы) между приложениями: предположим, что одновременно существует платформа для анализа данных и платформа для A/B-тестирования. На платформе A/B-тестирования нам также необходимо наблюдать за данными, такими как влияние разных экспериментальных схем на разные показатели. Если при написании другой страницы анализа данных возникнет такое же подозрение, будет довольно неудобно сразу переходить к платформе анализа данных. Таким образом, лучший способ — упаковать компоненты или страницы платформы анализа данных независимо друг от друга, чтобы платформа A/B могла динамически загружать и просматривать страницы анализа данных.

Пространства имен

Когда подпроект регистрируется на портале, ему необходимо указать пространство имен, например код в исходном тексте:

 await window.app.init('namespace-kaoqin',reducers);

Обратите внимание, что здесь используются обе глобальные переменныеwindowИ вручную указать пространство имен подпроекта

Всем рекомендую книгу с открытым исходным кодом:"Single page apps in depth", как следует из названия, это книга о разработке одного приложения. Она родилась до появления React. В ту эпоху были только MVC-фреймворки Backbone и Angular. О содержании книги есть возможность рассказать в будущее. Дело в том, что я согласен с книгой (или, скорее, она меня убеждает), что пространства имен не являются хорошей практикой.

В те дни основным способом модульности было применение уникальных пространств имен к глобальным переменным (например,window.MyApp), а затем монтировать все под этим пространством имен, что приводит к двум проблемам:

  • В рамках этого механизма вы либо раскрываете методы и переменные, которые хотите сделать доступными глобально, либо полностью скрываете их. Не бывает ситуаций, когда вы хотите скрыть его и предоставить для использования только подмножество подсистем. Если вы полностью раскрываете его, вы не можете контролировать другой код для доступа к нему или даже зависеть от него.
  • Теперь, когда модуль становится глобально доступным, трудно гарантировать, что он не станет «грязным»: модули, на которые он ссылается, могут непреднамеренно изменить его, сделав другие модули, зависящие от него, непригодными для использования. У большинства техников нет терпения ждать, пока издатель модуля исправит ошибки или добавит функции, они предпочитают временно взломать, а потом думают, что я восстановлю модуль после того, как модуль реализует эту функцию. Но этого никогда не бывает

Так вот почему родился CommonJS, почему я думаюwindow.appне хорошая практика

С другой стороны, указание пространств имен вручную также может привести к путанице. Из того, что я понимаю, они хотят добиться здесь того, чтобы просто дать разным проектам разные области видимости, чтобы изолировать их, включая редукторы, включая css. Так что не имеет значения, что такое пространство имен. В крайнем случае, даже генерация набора случайных строк допустима (обратитесь к практике модулей css)

Даже если вы укажете пространства имен вручную, как вы можете гарантировать, что однажды они не будут конфликтовать? Кто следит за сторожем?

Суммировать

Техническое решение — это продукт каждой компании и каждой команды в конкретном бизнес-сценарии, и нет разницы между хорошим и плохим. Если технология Meituan достаточно унифицирована, версия общедоступной библиотеки классов поддерживается достаточно усердно, а разработка достаточно стандартизирована, с таким решением проблем не возникает. Угол, который я рассматриваю, больше связан с бизнес-логикой, с которой я сталкиваюсь, очевидно, мне не повезло, потому что приходится иметь дело со слишком многими ситуациями в филиалах.

Я считаю, что эта статья, которую я хорошо понимаю, не подходит для оригинала, если в этом бизнесе есть друзья в самой группе из США или знаю оригинального автора, может Huxiangzhuangao, и с нетерпением жду дальнейших обменов исправлениями

использованная литература


Эта статья была также размещена на моемЗнай колонку, приветствую всех, чтобы обратить внимание