手写一个微前端,qiankunjs


window.__POWERED_BY_QIANKUN__ = true;

let module = null;

export const registerMicroApps = (apps = []) => {
  const _pushState = history.pushState;
  history.pushState = (...args) => {
    _pushState.apply(history, args);
    handler(apps);
  };

  window.addEventListener('popstate', () => handler(apps));
  window.addEventListener('load', () => handler(apps));
};

export const start = () => {};

// 处理函数
async function handler(apps) {
  const app = findRoute(apps, location.pathname);

  // 如果没有匹配的子应用,则不处理
  if (!app) {
    // 若上一次挂载是子应用,则卸载它
    if (module) {
      module.exports.unmount();
      module.container.innerHTML = '';
      module = null;
    }
    return;
  }

  // 相同子应用,不处理
  if (module?.app?.entry === app.entry) return;

  // 切换子应用
  const container = document.querySelector(app.container);

  // 卸载上一个子应用
  if (module) {
    module.exports.unmount();
    module.container.innerHTML = '';
    module = null;
  }

  container.innerHTML = await parseDom(app.entry);
  const codes = await parseScript(container, app.entry);
  window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ = app.entry + '/';
  module = runCode(codes);
  module.container = container;
  module.app = app;
  module.exports.mount({ container });
}

// 根据url获取dom
function parseDom(url) {
  return fetch(url).then(res => res.text());
}

// 根据dom获取scirpt脚本
function parseScript(el, baseUrl) {
  const scripts = el.querySelectorAll('script');
  return Promise.all(
    Array.from(scripts).map(script => {
      const src = script.getAttribute('src');
      console.log('src :>> ', src);
      if (src) return fetch(baseUrl + src).then(res => res.text());
      return Promise.resolve(script.innerHTML);
    })
  );
}

// 执行代码
function runCode(codes) {
  // 构造一个commonjs环境,获取子应用暴露出来的钩子函数
  let exports = {};
  let module = { exports };
  for (const code of codes) {
    eval(code);
  }
  return module;
}

// 查找应用
function findRoute(apps, path) {
  return apps.find(app => path.startsWith(app.activeRule));
}

仅实现了一些基础的功能,样式隔离,js沙箱,数据共享等还没做。