Мы структурировали фронтенд компании!

внешний фреймворк
Мы структурировали фронтенд компании!

введение

Автор учился в университете Шуанфэй в Чэнду.Четыре года назад, когда я искал работу на последнем курсе, поскольку не было хороших компаний, готовых нанимать людей в нашем университете, я пошел в 985 Университет электронных наук и Технологии по соседству каждый день ходить на лекцию.После более чем месяца беспардонной борьбы я наконец получил предложение от компании-единорога в Ханчжоу с месячной зарплатой 8к.Я был очень счастлив.Вскоре после этого я пошел работать в эта компания.

исходный адресприветственная звезда

Исходное состояние проекта

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

image-20201119205537278

В очень большом Java-проекте используется JavaWeb-фреймворк Ali с открытым исходным кодом Webx, а затем используется что-то вроде JSP, то есть шаблон скорости, для рендеринга страницы, а передний конец должен написать сценарий jQuery и сценарий CSS для выполнения функции, эти скрипты размещаются в каталоге статических ресурсов Webx, введите соответствующий скрипт в шаблон скорости.

Видно, что эта модель для нас сейчас сильно устарела, классическая модель MVC имеет явные недостатки.

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

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

Первая трансформация фронтенда: обновление стека технологий

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

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

Для проблемы зависимости html от рендеринга скорости нам нужно только согласиться отрисовать узел DOM, соответствующий идентификатору, и данные инициализации в скорости, а затем в проекте React ReactDOM.render узел, соответствующий идентификатору, и передать инициализацию данные, так что рендеринг может быть решен.Проблема зависимости рендеринга html бэкэнд-проекта.

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

Когда проект будет выпущен, укажите каталог вывода веб-пакета проекта React в каталог статических ресурсов Webx, а затем введите соответствующие результаты компиляции в скорость. Например, сейчас есть новый модуль А, тогда шаблон по скорости такой:

<link rel="stylesheet" href="/static/xxx_module/moduleA/main.css?v=hash" />
<script src="/static/xxx_module/moduleA/main.js?v=hash"></script>
<script>
  var _velocity_init_data_ = {
    // 渲染velocity数据
  };
</script>
<div id="pageA"></div>

И проект React такой

import React from 'react';
import React from 'react-dom';
import App from './App';

// 在开发时,声明一个带ID为pageA的空页面就可以
const container = document.getElementById('pageA');
const initData = window._velocity_init_data_;

ReactDom.render(<App initData={initData}/>, container);

Конфигурация проекта веб-пакета:

const path = require('path');

module.exports = {
  entry: './src/main.js',
  output: {
    path: path.resolve('后端项目路径', 'static', 'moduleA') // 对应的模块目录
  },
  // ...其他配置
}

На данный момент структура проекта выглядит следующим образом:

image-20201119205628118

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

Вторая трансформация передка: микро передок

С развитием бизнеса компании весь back-end проект становится все больше и больше, на одно обновление и развертывание проекта уходит не менее 20-30 минут, а в связи с требованиями бизнес-сценария, back-end -конечный проект должен улучшить свою высокую доступность и стабильность, что заставляет бэкэнд разделять проект, разрабатывать каждый модуль отдельно и развертывать разное количество машин и контейнеров отдельно в соответствии с условиями их доступа. Такое разделение модулей можно понять, так как серверный проект думает об эволюции микросервисной архитектуры, у каждого модуля есть свой маршрут, и они взаимодействуют внутри через http, rpc или kafka. В то время влияние фронтенда в компании было невелико, так что в то время была упущена возможность взять на себя маршрутизацию и позволить фронтенду управлять в процессе разделения бэкенд проекта .

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

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

После разделения внутреннего модуля для большинства фреймворков проектов используется Spring Boot, а шаблонизатор также переключается с скорости на freemarker.После завершения архитектура выглядит следующим образом:

image-20201120100214453

Поскольку в начале подробного рассмотрения не было, постепенно выявлялись недостатки архитектуры микрофронтенда в стиле iframe, как заявило сообщество:

  • Производительность загрузки страницы: переключение между страницами должно перезагрузить страницу один раз, а на событие onload главной страницы влияет iframe.
  • Плохой пользовательский интерфейс: iframes должны иметь высоту, иначе они свернутся, что может привести к появлению полос прокрутки как на главной странице, так и на подстраницах. Кроме того, стиль фиксированного узла в iframe ограничен, например, компонент сообщения Antd и модальный компонент, которые будут установлены в центре iframe, а не в центре окна браузера.

Поскольку проект является проектом toB, мы уделяем больше внимания удобству использования проекта, а не производительности, поэтому в то время мы проигнорировали проблему производительности загрузки страницы. Для проблем с пользовательским интерфейсом мы решаем проблему коллапса высоты, вычисляя высоту в реальном времени в iframe и отправляя ее на главную страницу с помощью postMessage.Главная страница динамически устанавливает высоту iframe. Стиль фиксированного узла в iframe ограничен, и видны только трюки, например, компонент сообщения и модальный компонент Antd, упомянутые выше, после установки домена главной страницы и подстраницы для решения междоменной проблемы. , установив компонентgetContainerметод, отобразите фиксированный узел на главной странице, а затем добавьте соответствующий стиль css на главную страницу.

Третья трансформация фронтенда: фронтенд независимая издательская система

После доработки микрофронтенда в стиле iframe, для улучшения влияния фронтенда, мой наставник сначала предложил идею самостоятельной публикации фронтенда, извлекая статические ресурсы фронтенда которые нужно выпустить из бэкенд-сервиса, и развернуть их на CDN компании ( По моему мнению, мой наставник, похоже, бывший сотрудник HUAWEI CLOUD. Говорят, что идея этого фронтенда независима выпуск был предложен и отработан в HUAWEI CLOUD).

Ввиду ситуации фронтенда в то время наша трудность очевидна.Роутинг распределяется бэкенд проектом, рендеринг HTML также контролируется бэкендом.Если ресурсы фронтенда выпускаются отдельно, то когда публикуется только фронтенд, он должен быть гарантированным. Когда HTML остается неизменным (HTML определяет, какой CSS и JS загружать), обновить ресурсы фронтенда, которые необходимо загрузить, т. е. обновить JS и CSS, которые необходимо загрузить.

Чтобы решить эту трудность, мы внедрили независимую систему публикации фронтенд-ресурсов, кодовое название проекта прелюдия. Модули каждого проекта нуждаются в прелюдии для определения приложения приложения и пакета модулей. Когда ресурс высвобождается, приложение использует модуль в качестве гранулярности и соответствующую версию (номер версии должен следовать заСпецификация семантической версии) статические ресурсы публикуются в соответствующую папку CDN. Например, если используется модуль bundleA приложения A и выпущенная версия V1.1.0, путь запроса ресурса должен быть следующим:

https://static.xxx.com/appA/bundleA/1.1.0/

В консоли прелюдии необходимо настроить модуль bundleA для инициализации загружаемых ресурсов, а также можно настроить предварительно зависимые модули.Например, если в конфигурации инициализации необходимо загрузить vender-chunk.js, main.js , vender-chunk.css и main.css, то если вам нужно использовать этот модуль, вам нужно загрузить следующие ресурсы:

<link rel="stylesheet" href="https://static.xxx.com/appA/bundleA/1.1.0/vender-chunk.css" />
<link rel="stylesheet" href="https://static.xxx.com/appA/bundleA/1.1.0/main.css" />

<script src="https://static.xxx.com/appA/bundleA/1.1.0/vender-chunk.js"></script>
<script src="https://static.xxx.com/appA/bundleA/1.1.0/main.js"></script>

Зная, какие ресурсы необходимо загрузить модулю, prelude предоставляет интерфейс загрузчика, который получает три параметра: приложение, пакет и версию, а затем отображает сценарий js для внедрения соответствующей конфигурации приложения/пакета/версии на страницу. ресурсы, которые необходимо загрузить, такие как в предыдущем примере, нужно только ввести следующий скрипт в Velocity или FreeMarker:

<script src="https://prelude.xxx.com/prelude-loader?app=appA&bundle=bundleA&version=V1.1.0"></script>

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

var assets = {
  css: [
    'https://static.xxx.com/appA/bundleA/1.1.0/vender-chunk.css',
    'https://static.xxx.com/appA/bundleA/1.1.0/main.css'
  ],
  js: [
    'https://static.xxx.com/appA/bundleA/1.1.0/vender-chunk.js',
    'https://static.xxx.com/appA/bundleA/1.1.0/main.js'
  ]
};
// 加载CSS
assets.css.forEach(href => {
  var link = document.createElement('link');
  // 其他逻辑
  link.rel = 'stylesheet';
  link.href = hrefs;
  document.head.appendChild(link);
});
// 加载JS
assets.css.forEach(src => {
  var script = document.createElement('script');
  // 其他逻辑
  script.async = false; // 顺序执行
  script.src = src;
  document.body.appendChild(script);
});

Этого недостаточно, поскольку параметр версии интерфейса жестко закодирован в версии 1.1.0. или freemarker, который не соответствует требованиям. Таким образом, мы обновили параметр версии, вы можете использовать стандартные подстановочные знаки, такие как передача версии = *, что означает, что всегда используется последняя версия модуля, тогда скрипт, введенный скоростью или фримаркером, становится следующим:

<script src="https://prelude.xxx.com/preluer-loader?app=appA&bundle=bundleA&version=*"></script>

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

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

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

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

Например, когда текущая серверная часть выпускается одновременно, клиентская часть сначала выпускает обновленную версию дляV1.2.0, когда серверная часть не выпущена, она использовалаV1.1.*версия, когда серверная часть будет выпущена, обновите параметр версии доV1.2.*, она будет автоматически загружена как версия V1.2.0 после подключения к Интернету.

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

После того, как проект завершен и реализован, архитектура постепенно отделяет внешний интерфейс.

image-20201120131044267

Когда проект прелюдии завершен, мы используем преимущества прелюдии, чтобы по-новому компенсировать недостатки архитектуры микроинтерфейса в стиле iframe. Тег prelude-loader используется вместо тега iframe для создания поддельной архитектуры микрофронтенда iframe.

Четвертая трансформация фронтенда: непрерывная интеграция CI/CD

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

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

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

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

image-20201120141102717

При непрерывной интеграции нам не нужно собирать весь код в рабочей среде на собственном компьютере, достаточно создать новый скрипт build.sh в корневом каталоге проекта, который содержит все команды для компиляции и сборки, разрешить выполнение платформы непрерывной интеграции. , вывести output.tar.gz в корневой каталог после компиляции, а затем включить выходной пакет в файл описания параметров app/bundle/version или напрямую передать параметры в прелюдию, когда выходные данные будут выпущены, и позвольте прелюдии отправить его в CDN, чтобы вы могли завершить выпуск версии.

На данный момент вся интерфейсная архитектура в основном полностью отделена.

image-20201120142134478

Заканчивать

Разработка всей архитектуры заняла около двух-трех лет, и в середине было встречено много ухабистых проблем.Хотя у нас нет глобально оптимального решения, мы в основном используем локально оптимальное решение для решения проблемы.Эти два В За три года я тоже превратился из фронтенд-новичка в хорошего фронтенд-духовника, и я очень благодарен своему тогдашнему наставнику.