Исследование микроинтерфейса

внешний фреймворк
Исследование микроинтерфейса

1. Происхождение микроинтерфейса

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

(1) режим МРА

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

(2) СПА-режим

Я считаю, что интерфейсные приложения сейчас почти созданы и разрабатываются тремя основными вагонами SPA: Vue, React и Angular.Переходы страниц между приложениями выгружаются/монтируются путем прослушивания URL-адреса браузера, поэтому преимущество в том, что это имеет неотъемлемые преимущества в опыте. Нет необходимости обновлять браузер для переключения между несколькими продуктами, что может в значительной степени обеспечить поток операций процесса между несколькими продуктами. Микроинтерфейс — это архитектура, аналогичная микросервисам, которая применяет концепцию микросервисов к стороне браузера, то есть одностраничное интерфейсное приложение преобразуется из единого монолитного приложения в приложение, объединяющее несколько небольшие интерфейсные приложения в одно. Он сочетает в себе преимущества режима MPA и режима SPA, а обычная архитектура микро-фронтенда имеет следующие преимущества:

  • Независимость от стека технологий: используйте несколько интерфейсных фреймворков на одной странице без обновления страницы (Vue, React, Angular и т. д.);

  • Сильная независимость: независимая разработка, независимое развертывание и постепенное обновление различных бизнес-приложений;

  • Изоляция и совместное использование во время выполнения: данные могут совместно использоваться и передаваться между различными бизнес-подприложениями, но js и css не могут влиять друг на друга;

  • Преимущество опыта: он обеспечивает непрерывность работы одностраничного приложения, и переключение страниц не требует обновления;

2. Проблемы с фреймами

在早期,微前端概念出现之前,我们整合多个团队多个应用,我们不约而同的选择即为 iframe。 Самая большая особенность iframe заключается в том, чтобы обеспечить собственное решение жесткой изоляции браузера, будь то изоляция стиля, изоляция js и другие проблемы, которые могут быть идеально решены.但它的最大问题也在于它的隔离性无法被突破,导致应用间上下文无法被共享,随之带来的开发体验、产品体验的问题。

  • URL-адрес не синхронизирован. Состояние URL-адреса обновления браузера iframe теряется, кнопки «Назад» и «Вперед» использовать нельзя.

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

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

  • Первая партия подприложений работает медленно. Каждая запись подприложения — это процесс перестройки контекста браузера и перезагрузки ресурсов.

Три, пример с одним спа

single-spa является краеугольным камнем многих фреймворков микро-интерфейса (сам по себе является фреймворком микро-интерфейса, но многие крупные производители и фреймворки микро-интерфейса осуществляют вторичную инкапсуляцию на его основе), поэтому глубокое понимание его принципы являются основой для исследования и практики микроинтерфейса. single-spa — это инфраструктура микроинтерфейса javascript, которая объединяет несколько одностраничных приложений в монолитное приложение. Конкретный учебник и API single-spa можно просмотретьофициальный сайт спа-салона, Также рекомендуется, чтобы при чтении следующих глав вы сначала кратко прочитали его официальный сайт, чтобы узнать, что это такое и что оно может делать. В этой главе мы напрямую используем практические примеры, представленные на официальном веб-сайте single-spa, для описания использования single-spa и переходим к принципиальному анализу следующей главы с размышлениями, лежащими в основе этого явления.

1. Клонируйте экземпляр проекта

git clone https://github.com/joeldenning/coexisting-vue-microfrontends.git

2. Запустите проект

В каталогах базового приложения и подприложения установите пакеты зависимостей соответственно, а затем запустите приложения соответственно:

// root-html-file
cd root-html-file
npm install
npm run serve​
// navbar
cd navbar
npm install
npm run serve
​// app1 
cd app1
npm install
npm run serve​
// app2
cd app2
npm install
npm run serve

3. Наблюдение за примерами и размышление

(1) Мы смотрим на пример кода базового приложения. Регистрация подприложения (registerApplication) в базовом приложении вызывает функцию registerApplication single-spa, и то, что реализовано внутри. Начал делать Что, как это работает внутренне?

(2) Мы смотрим на входной исполняемый файл main.js субприложения и обнаруживаем, что субприложение будет экспортировать три периодические функции bootstrap/mount/unmount Когда эти три периодические функции выполняются?

(3) Доступ через браузерhttp://localhost:5000/, доступны следующие страницы. Глядя на пример кода, мы можем обнаружить, что следующая страница является страницей панели навигации базового приложения, как single-spa соответствует этому маршруту, а нажатие на App1 и App2 приведет к переходу на страницы приложения app1 и app2. приложения соответственно, и страница не будет обновляться, что является ключевой функциональной точкой сопоставления маршрутов с одним спа-адресом;

(4) Мы продолжаем нажимать на App1 App2 и наблюдаем за опцией вкладки сети в окне отладки браузера.Мы обнаружим, что при переключении на другое подприложение ресурсы, необходимые для рендеринга подприложения, будут загружены только тогда, когда под- приложение рендерится в первый раз. , последующее переключение не будет загружать связанные ресурсы; наблюдая за опцией Elements окна отладки браузера, мы обнаружим, что: структура Dom различных подприложений будет монтироваться и выгружаться соответственно с переключением суб-приложений, что single-spa Как загружать, монтировать и выгружать ресурсы суб-приложений?

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

4. Принцип одиночного спа

Обобщите принцип single-spa в одном предложении: **single-spa — это конечный автомат. Фреймворк отвечает только за поддержание состояния каждого подприложения. -приложения и т. д. все определяются самими подприложениями.Контроль, поэтому инфраструктура с одним спа имеет хорошую расширяемость. **У нас можно заказатьОдин-спа Официальный сайт GitHubПроверьте исходный код клона.Исходный код функции sinlge-spa в основном сосредоточен в каталоге src.Основные функции каждого файла в каталоге src резюмируются следующим образом.

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

1. Что делает регистрация приложения register?

Метод регистрации приложения registerApplication определен в файле /src/applications/app.js. Код и комментарии выглядят следующим образом. В основном он делает три вещи:

  • sanitizeArguments: нормализация параметров, чтобы гарантировать, что параметры, зарегистрированные каждым подприложением, являются допустимыми;

  • apps.push: добавление зарегистрированных подприложений в массив приложений и добавление внутренних свойств для каждого подприложения, таких как статус очень важного атрибута, чтобы отметить статус подприложения;

  • REROUTE: этот метод фокусируется на следующем разделе, мы временно и знаем, что метод в методе будет определять, зарегистрирована ли загрузка ресурсов (метод LoadApps), и добавляет связанные крючки жизненного цикла в APP дочернего приложения при загрузке (TOLOADPROMIMIMIMIMIMIMIMIMIMIMI);

(1) Код метода регистрации registerApplication выглядит следующим образом:

export function registerApplication(
  appNameOrConfig,
  appOrLoadApp,
  activeWhen,
  customProps
) {
  // hb: 格式化用户传入的应用配置参数,保证传入的参数是合法的
  const registration = sanitizeArguments(
    appNameOrConfig,
    appOrLoadApp,
    activeWhen,
    customProps
  );

  // hb: 已经存在相同名称的应用报错
  if (getAppNames().indexOf(registration.name) !== -1)
    throw Error(
      formatErrorMessage(
        21,
        __DEV__ &&
          `There is already an app registered with name ${registration.name}`,
        registration.name
      )
    );

  // 将每个应用的配置信息都存放到 apps 数组中
  apps.push(
    assign(
      {
        loadErrorTime: null,
        status: NOT_LOADED,
        parcels: {},
        devtools: {
          overlays: {
            options: {},
            selectors: [],
          },
        },
      },
      registration
    )
  );

  if (isInBrowser) {
    ensureJQuerySupport();
    reroute();
  }
}

(2) Код метода loadApps выглядит следующим образом:

   // hb: 整体返回一个立即resolved的promise,通过微任务来加载apps
  function loadApps() {
    return Promise.resolve().then(() => {
      // hb: 返回封装后的 app,包括给其附上生命周期等
      const loadPromises = appsToLoad.map(toLoadPromise);
      console.log('查看 loadPromises :', loadPromises);

      return (
        Promise.all(loadPromises)
          .then(callAllEventListeners)
          // there are no mounted apps, before start() is called, so we always return []
          .then(() => [])
          .catch((err) => {
            callAllEventListeners();
            throw err;
          })
      );
    });
  }

(3) Основной код метода toLoadPromise выглядит следующим образом:

export function toLoadPromise(app) {
  return Promise.resolve().then(() => {
    if (app.loadPromise) {
      // hb: 说明 app 已经被加载
      return app.loadPromise;
    }
    if (app.status !== NOT_LOADED && app.status !== LOAD_ERROR) {
      return app;
    }
    app.status = LOADING_SOURCE_CODE;
    let appOpts, isUserErr;

    return (app.loadPromise = Promise.resolve()
      .then(() => {
        // hb: loadApp 即是用户传入的参数  () => System.import('navbar'), 
        // 所以加载子应用其实就是通过用户自己传入的加载方式,即使如果不用 System.import 也可以;
        // 没有明白属性获取来干嘛用 getProps(app) ???
        const loadPromise = app.loadApp(getProps(app));

        // hb: 子应用导出的必须是个对象,且包含 3 个生命周期:bootstrap、mount、unmount
        return loadPromise.then((val) => {
          app.loadErrorTime = null;
          appOpts = val;
          app.status = NOT_BOOTSTRAPPED;
          // hb: 在app对象上挂载生命周期方法,每个方法都接收一个props作为参数,方法内部执行子应用导出的生命周期函数,并确保生命周期函数返回一个promise
          app.bootstrap = flattenFnArray(appOpts, "bootstrap");
          app.mount = flattenFnArray(appOpts, "mount");
          app.unmount = flattenFnArray(appOpts, "unmount");
          app.unload = flattenFnArray(appOpts, "unload");
          app.timeouts = ensureValidAppTimeouts(appOpts.timeouts);
          // hb: 执行到这里说明子应用已成功加载,删除app.loadPromise属性
          delete app.loadPromise;
          return app;
        });
      })
      .catch((err) => {
        // hb: 加载失败,稍后重新加载
        delete app.loadPromise;
        let newStatus;
        if (isUserErr) {
          newStatus = SKIP_BECAUSE_BROKEN;
        } else {
          newStatus = LOAD_ERROR;
          app.loadErrorTime = new Date().getTime();
        }
        handleAppError(err, app, newStatus);

        return app;
      }));
  });
}

2. Что делает метод start?

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

  • appsToUnload // Приложения, которые необходимо удалить, удаляются

  • Appstounmount // Приложение должно быть удалено для удаления

  • appsToLoad // Загружается приложение, которое нужно загрузить

  • appsToMount // Необходимо смонтировать приложения для монтирования

(1) код метода запуска

// hb: 调用 start 之前,应用会被加载,但不会初始化、挂载和卸载,有了 start 可以更好的控制应用的流程
export function start(opts) {
  started = true;
  if (opts && opts.urlRerouteOnly) {
    setUrlRerouteOnly(opts.urlRerouteOnly);
  }
  if (isInBrowser) {
    reroute();
  }
}

(2) метод перенаправления

export function reroute(pendingPromises = [], eventArguments) {
  const {
    appsToUnload,  // hb: 需要被移除的
    appsToUnmount, // hb: 需要被卸载的
    appsToLoad,    // hb: 需要被加载的
    appsToMount,   // hb: 需要被挂载的
  } = getAppChanges();

  let appsThatChanged;
  // hb: 是否 start 调用 isStarted 为 false 时表示是 start 调用
  if (isStarted()) {  
    appChangeUnderway = true;
    appsThatChanged = appsToUnload.concat(
      appsToLoad,
      appsToUnmount,
      appsToMount
    );
    return performAppChanges();
  } else {
    appsThatChanged = appsToLoad;
    return loadApps();
  }

  // hb: 整体返回一个立即resolved的promise,通过微任务来加载apps
  function loadApps() {
    return Promise.resolve().then(() => {
      // hb: 返回封装后的 app,包括给其附上生命周期等
      const loadPromises = appsToLoad.map(toLoadPromise);
      return (
        Promise.all(loadPromises)
          .then(callAllEventListeners)
          // there are no mounted apps, before start() is called, so we always return []
          .then(() => [])
          .catch((err) => {
            callAllEventListeners();
            throw err;
          })
      );
    });
  }

  function performAppChanges() {
    return Promise.resolve().then(() => {
      // hb: 移除应用 => 更改应用状态,执行unload生命周期函数,执行一些清理动作
      // 其实一般情况下这里没有真的移除应用
      const unloadPromises = appsToUnload.map(toUnloadPromise);
      // hb: 先卸载再移除
      const unmountUnloadPromises = appsToUnmount
        .map(toUnmountPromise)
        .map((unmountPromise) => unmountPromise.then(toUnloadPromise));
      const allUnmountPromises = unmountUnloadPromises.concat(unloadPromises);
      const unmountAllPromise = Promise.all(allUnmountPromises);

      unmountAllPromise.then(() => {
        window.dispatchEvent(
          new CustomEvent(
            "single-spa:before-mount-routing-event",
            getCustomEventDetail(true)
          )
        );
      });

      // hb: 待加载的进行加载 并且进行挂载
      const loadThenMountPromises = appsToLoad.map((app) => {
        return toLoadPromise(app).then((app) =>
          tryToBootstrapAndMount(app, unmountAllPromise)
        );
      });

      // hb: 待挂载的进行挂载
      const mountPromises = appsToMount
        .filter((appToMount) => appsToLoad.indexOf(appToMount) < 0)
        .map((appToMount) => {
          return tryToBootstrapAndMount(appToMount, unmountAllPromise);
        });
      return unmountAllPromise
        .catch((err) => {
          callAllEventListeners();
          throw err;
        })
        .then(() => {});
    });
  }
}

3. Момент времени выполнения функции жизненного цикла, экспортируемой субприложением.

Мы проверили входной исполняемый файл main.js субприложения и обнаружили, что субприложение будет экспортировать три периодические функции bootstrap/mount/unmount Когда эти три периодические функции выполняются? Фактически, после загрузки ресурсов подприложения функция жизненного цикла подприложения будет добавлена ​​в свойства приложения (каждое подприложение в single-spa является объектом приложения, а затем агрегировано в массив apps), и тогда single-spa будет в подприложении, при обновлении состояния app соответственно выполняется его функция жизненного цикла.

(1) Код в методе загрузки, который обрабатывает метод жизненного цикла в поддержании

export function toLoadPromise(app) {
   app.status = NOT_BOOTSTRAPPED;
   // hb: 在app对象上挂载生命周期方法,每个方法都接收一个props作为参数,方法内部执行子应用导出的生命周期函数,并确保生命周期函数返回一个promise
   app.bootstrap = flattenFnArray(appOpts, "bootstrap");
   app.mount = flattenFnArray(appOpts, "mount");
   app.unmount = flattenFnArray(appOpts, "unmount");
   app.unload = flattenFnArray(appOpts, "unload");
   app.timeouts = ensureValidAppTimeouts(appOpts.timeouts);
  });
})

(2) метод flattenFnArray

// hb: 返回一个接受 props 作为参数的函数,这个函数负责执行子应用中的生命周期函数,
// 并确保生命周期函数返回的结果为promise
export function flattenFnArray(appOrParcel, lifecycle) {
  let fns = appOrParcel[lifecycle] || [];
  fns = Array.isArray(fns) ? fns : [fns];
  if (fns.length === 0) {
    fns = [() => Promise.resolve()];
  }

  return function (props) {
    return fns.reduce((resultPromise, fn, index) => {
      return resultPromise.then(() => {
        const thisPromise = fn(props);
      });
    }, Promise.resolve());
  };
}

4. Переключение подприложений

В этом примере обнаружено, что наше базовое приложение может монтировать/выгружать наши подприложения в соответствии с разными URL-адресами без обновления страницы, так как же single-spa соответствует маршруту URL-адреса? На самом деле, если вы поняли принцип переключения маршрутизации одностраничных приложений, таких как vue-router и react-router, это не что иное, как применение события hashchange для отслеживания изменения хеш-маршрута и события popstate для отслеживания изменение маршрута истории.Принцип сопоставления маршрута url точно такой же (базовый апи только те, его еще можно изменить), а связанные операции определяются в файле /src/navigation/navigation-events.js файл.

  window.addEventListener("hashchange", urlReroute);
  window.addEventListener("popstate", urlReroute);

И когда мы зарегистрировали субприложение, мы передали правило сопоставления маршрутизации субприложения в качестве параметра, и single-spa использует это условие оценки соответствия входящего маршрута в /src/applications/app.helpers.js для оценки субприложения. Должен ли он быть в активном (подключенном) состоянии, соответствующий код выглядит следующим образом, где app.activeWhen — функция сопоставления входящего маршрута. На данный момент мы почти знаем, как single-spa соответствует соответствующему подприложению при переключении URL.

// hb: 是否应该活跃状态(url 匹配到路由)
export function shouldBeActive(app) {
  try {
    return app.activeWhen(window.location);
  } catch (err) {
    handleAppError(err, app, SKIP_BECAUSE_BROKEN);
    return false;
  }
}

5. Как монтировать/размонтировать подприложения

Мы уже знаем из вышесказанного, одной спа управления изменений состояния суб-приложений, таких как загрузка ресурсов суб-приложений во время регистрации, а также монтаж / выгрузки суб-приложений. В третьей главе, мы проходим, когда мы зарегистрировать приложение . метод загрузки из System.import используется для загрузки, и нет ресурсов загрузки метод внутри одного-шпа, насчет монтирования / деинсталляции? На самом деле, установка / разгрузка также осуществляется через соответствующие жизненный цикл функции, передаваемые в каждом субприложении сам. Давай проверит крепление / выгрузки жизненного цикл функция плагина одного сп-вю (не одного спа), используемая суб- приложения. Вы можете видеть , что соответствующие функции жизненного цикла монтирует и выгружает элементы DOM. В одном-шпа, он выполняет только функцию жизненного цикла , соответствующего субприложения в соответствующем состоянии суб-приложений. Сам сингл-шпа только играет роль контроля состояния. И так далее, это может также достичь лучшего масштабируемость, как пользователи хотят скачать, установить и деинсталлировать, они решают сами по себе, до тех пор, как вы передаете в стандартных параметрах.

(1) Методы жизненного цикла с одним спа-VEUE

// 挂载生命周期函数
function mount(opts, mountedInstances, props) {
  return Promise
    .resolve()
    .then(() => {
      const appOptions = {...opts.appOptions}
      if (props.domElement && !appOptions.el) {
        appOptions.el = props.domElement;
      }

      if (!appOptions.el) {
        const htmlId = `single-spa-application:${props.name}`
        appOptions.el = `#${htmlId.replace(':', '\\:')} .single-spa-container`
        let domEl = document.getElementById(htmlId)
        if (!domEl) {
          domEl = document.createElement('div')
          domEl.id = htmlId
          document.body.appendChild(domEl)
        }

        // single-spa-vue@>=2 always REPLACES the `el` instead of appending to it.
        // We want domEl to stick around and not be replaced. So we tell Vue to mount
        // into a container div inside of the main domEl
        if (!domEl.querySelector('.single-spa-container')) {
          const singleSpaContainer = document.createElement('div')
          singleSpaContainer.className = 'single-spa-container'
          domEl.appendChild(singleSpaContainer)
        }

        mountedInstances.domEl = domEl
      }

      if (!appOptions.render && !appOptions.template && opts.rootComponent) {
        appOptions.render = (h) => h(opts.rootComponent)
      }

      if (!appOptions.data) {
        appOptions.data = {}
      }

      appOptions.data = {...appOptions.data, ...props}

      mountedInstances.instance = new opts.Vue(appOptions);
      if (mountedInstances.instance.bind) {
        mountedInstances.instance = mountedInstances.instance.bind(mountedInstances.instance);
      }
    })
}

// 卸载生命周期函数
function unmount(opts, mountedInstances) {
  return Promise
    .resolve()
    .then(() => {
      mountedInstances.instance.$destroy();
      mountedInstances.instance.$el.innerHTML = '';
      delete mountedInstances.instance;

      if (mountedInstances.domEl) {
        mountedInstances.domEl.innerHTML = ''
        delete mountedInstances.domEl
      }
    })
}

Я полагаю, что читатели, прочитавшие это, глубоко проникнутся авторским изложением принципа single-spa.**single-spa — это конечный автомат. Фреймворк отвечает только за поддержание состояния каждого подприложения. Как загружать субприложения Приложения, монтирование субприложений, выгрузка субприложений и т. д. контролируются самими субприложениями, поэтому структура single-spa обладает хорошей масштабируемостью. **После прочтения этой главы у меня появилось глубокое понимание механизма работы инфраструктуры single-spa, но single-spa, как архитектура самого низкого уровня, по-прежнему имеет некоторые проблемы в реальной сцене, как показано ниже, в В следующей главе мы сосредоточимся на том, как решить эти проблемы.

  • single-spa использует запись js как запись подприложения, а стоимость модернизации старых проектов высока;

  • Подприложения имеют стили CSS, которые взаимодействуют друг с другом;

  • Существует глобальное загрязнение js в подприложениях;

  • Платформа single-spa не обеспечивает механизма связи между подприложениями или между подприложениями и базовым приложением;

5. Принцип цянькунь

qiankun — это относительно зрелая микроинтерфейсная среда, запущенная Ant Financial, основанная на single-spa для вторичной разработки и используемая для преобразования веб-приложений из одного приложения в несколько небольших интерфейсных приложений. Если вы еще не знакомы с фреймворком, вы можете сначала проверить его.официальный сайт Цянькунь; В этой главе мы в основном сосредоточимся на вопросах, поднятых в третьем разделе, чтобы обсудить, как обращаться с цянькунь (天地).

1. Подприложения работают независимо

использование qiankunimport-html-entry

2. Изоляция стилей CSS

Поскольку в сценарии микроинтерфейса субприложения разных технологических стеков будут интегрированы в одну и ту же среду выполнения, неизбежно возникнет проблема интерференции стилей между субприложениями. Есть две идеи для изоляции стилей.Первая заключается в использовании решения, аналогичного CSS-модулю или БЭМ, что, по сути, позволяет избежать конфликтов из-за условностей.Для новых проектов это решение очень рентабельно, но если оно предполагает использование старые проекты. В совокупности стоимость преобразования будет очень высока; вторая идея — удалить таблицу стилей одновременно с удалением субприложения. Технический принцип заключается в том, что браузер рефакторит всю CSSOM для вставка и удаление всех таблиц стилей. , чтобы достичь цели вставки и выгрузки стилей, что гарантирует, что только одна примененная таблица стилей действительна в определенный момент времени. Фреймворк qiankun принимает вторую идею, используяimport-html-entry, получить информацию о стиле, проанализировав теги и в записи html, загрузить файл стиля и, наконец, вставить его в контейнер основного фрейма в виде тега и удалить его вместе при удалении подприложения, поэтому чтобы избежать конфликтов между различными субприложениями.

3. глобальная изоляция js

По сравнению с изоляцией стиля изоляция js важнее. Потому что в сценарии SPA влияние таких проблем, как утечка памяти и конфликты глобальных переменных, будет усилено, а проблемы в подприложении могут повлиять на работу других приложений. Кроме того, такого рода проблемы обычно очень трудно устранить и локализовать, а если они возникают, то стоимость их решения очень высока. Платформа qiankun обеспечивает среду песочницы для каждого подприложения на основе прокси, и доступ всех подприложений к значениям объекта прокси/окна контролируется. Установленное значение будет применяться только к коллекции updateValueMap внутри песочницы, а значение будет взято сначала из независимого пула состояний субприложений (updateValueMap).Это гарантирует, что глобальные js каждого субприложения конфликтуют друг с другом и загрязняют друг друга.

4. Связь между подприложениями

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

  • setGlobalState: set globalState — при установке нового значения будет выполняться внутренняя поверхностная проверка.Если будет обнаружено, что globalState изменилось, будет запущено уведомление, чтобы уведомить все функции наблюдателя;

  • onGlobalStateChange: зарегистрировать функцию наблюдателя — в ответ на изменения в globalState запускать функцию наблюдателя при изменении globalState;

  • offGlobalStateChange: отменить функцию наблюдателя — экземпляр больше не реагирует на изменения globalState.

6. Резюме

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

Адрес блога на github:fengshi123 , в котором собраны все блоги автора, приглашаем подписаться и отметиться ~

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

1,официальный сайт спа-салона

2,официальный сайт Цянькунь

3.Вероятно, самое полное решение для микроинтерфейса, которое вы когда-либо видели.