實現微前端的十種方式 【第二種】

實現微前端的十種方式 【二】

  • 實現微前端,我想了一想,大概有十種方式
  • 想學習微前端的小夥伴,能夠看我以前對微前端源碼解析、加載方式、以及我開源的微前端框架chunchao源碼
  • 簡單的文章,通俗易懂,感受不錯記得點個在看關注

目前主流的微前端實現方式(基座加載式)

  • 以基座爲入口,配置不一樣的子應用入口地址,達到實現微前端的效果
  • 目前微前端開源的框架:chunchaoqiankun,其中chunchao僅僅200行代碼就實現了,是一個很是值得定製開發的微前端雛形框架
  • 微前端基座模式配置

  • 加載示意圖

如何實現基座模式加載子應用?

  • 劫持前端路由,重寫hashchangepopstate事件
const HIJACK_EVENTS_NAME = /^(hashchange|popstate)$/i;
const EVENTS_POOL = {
  hashchange: [],
  popstate: [],
};

window.addEventListener('hashchange', loadApps);
window.addEventListener('popstate', loadApps);

const originalAddEventListener = window.addEventListener;
const originalRemoveEventListener = window.removeEventListener;
window.addEventListener = function (eventName, handler) {
  if (
    eventName &&
    HIJACK_EVENTS_NAME.test(eventName) &&
    typeof handler === 'function'
  ) {
    EVENTS_POOL[eventName].indexOf(handler) === -1 &&
      EVENTS_POOL[eventName].push(handler);
  }
  return originalAddEventListener.apply(this, arguments);
};
  • 根據不一樣的入口,去拉取子應用的jscss等資源

  • 註冊子應用後存入隊列中
/**
 *
 * @param {string} entry
 * @param {string} function
 */
const Apps = [] //子應用隊列
function registryApp(entry,activeRule) {
    Apps.push({
        entry,
        activeRule
    })
}
  • 註冊完了以後,就要找到須要加載的app,而且拉取資源
export async function loadApp() {
  const shouldMountApp = Apps.filter(shouldBeActive);
  const App = shouldMountApp.pop();
  fetch(App.entry)
    .then(function (response) {
      return response.text();
    })
    .then(async function (text) {
      const dom = document.createElement('div');
      dom.innerHTML = text;
      const entryPath = App.entry;
      const scripts = dom.querySelectorAll('script');
      const subapp = document.querySelector('#subApp-content');
      const paromiseArr =
        scripts &&
        Array.from(scripts).map((item) => {
          if (item.src) {
            const url = window.location.protocol + '//' + window.location.host;
            return fetch(`${entryPath}/${item.src}`.replace(url, '')).then(
              function (response) {
                return response.text();
              }
            );
          } else {
            return Promise.resolve(item.textContent);
          }
        });
      subapp.appendChild(dom);
      const res = await Promise.all(paromiseArr);
      if (res && res.length > 0) {
        res.forEach((item) => {
          const script = document.createElement('script');
          script.innerText = item;
          subapp.appendChild(script);
        });
      }
    });
}
  • shouldBeActive根據傳入的規則去判斷是否須要此時掛載:
export function shouldBeActive(app){
    return app.activeRule(window.location)
}
  • 處理腳本文件
export async function handleScripts(entryPath,subapp,dom) {
  const scripts = dom.querySelectorAll('script');
  const paromiseArr =
    scripts &&
    Array.from(scripts).map((item) => {
      if (item.src) {
        const url = window.location.protocol + '//' + window.location.host;
        return fetch(`${entryPath}/${item.src}`.replace(url, '')).then(
          function (response) {
            return response.text();
          }
        );
      } else {
        return Promise.resolve(item.textContent);
      }
    });
  const res = await Promise.all(paromiseArr);
  if (res && res.length > 0) {
    res.forEach((item) => {
      const script = document.createElement('script');
      script.innerText = item;
      subapp.appendChild(script);
    });
  }
}
  • 處理樣式文件
export async function handleStyles(entryPath, subapp, dom) {
  const arr = [];
  const styles = dom.querySelectorAll('style');
  const links = Array.from(dom.querySelectorAll('link')).filter(
    (item) => item.rel === 'stylesheet'
  );
  const realArr = arr.concat(styles,links)
  const paromiseArr =
    arr &&
    Array.from(realArr).map((item) => {
      if (item.rel) {
        const url = window.location.protocol + '//' + window.location.host;
        return fetch(`${entryPath}/${item.href}`.replace(url, '')).then(
          function (response) {
            return response.text();
          }
        );
      } else {
        return Promise.resolve(item.textContent);
      }
    });
  const res = await Promise.all(paromiseArr);
  if (res && res.length > 0) {
    res.forEach((item) => {
      const style = document.createElement('style');
      style.innerHTML = item;
      subapp.appendChild(style);
    });
  }
}
  • 此時,咱們已經能夠加載不一樣的子應用了。

最後

  • 認真收藏這個系列吧,記得點個關注和在看,相信你能收穫不少不少~
  • 我是Peter,架構設計過桌面跨平臺IM軟件、重型Saas平臺以及手機端跨平臺APP,個人微信:CALASFxiaotan
  • 另外歡迎收藏個人資料網站:前端生活社區:https://qianduan.life,感受對你有幫助,能夠右下角點個在看關注一波公衆號:[前端巔峯]
相關文章
相關標籤/搜索