前文 中粗糙的实现了动态脚本加载,这里我们在进一步优化一下异步动态的脚本加载:

declare global {
  interface Window {
    scriptPromiseMap: Map<string, Promise<void>>;
  }
}
if (!window.scriptPromiseMap) {
  window.scriptPromiseMap = new Map();
}
export function loadScript(url: string, preserve = true) {
  let resolve: (value: void) => void;
  let reject: (reason?: any) => void;
  const promise = new Promise<void>((_resolve, _reject) => {
    resolve = _resolve;
    reject = _reject;
  });
  if (window.scriptPromiseMap.get(url) && preserve) {
    return window.scriptPromiseMap.get(url) || Promise.resolve();
  }
  const script = document.createElement('script');
  script.src = url;
  script.async = true;
  if (preserve) {
    script.setAttribute('spa-preserve', 'true');
  }
  script.onload = () => {
    resolve();
  };
  script.onerror = () => {
    reject(new Error(`Failed to load script: ${url}`));
  };
  document.head.appendChild(script);
  if (preserve) {
    window.scriptPromiseMap.set(url, promise);
  }
  return promise
}

需要注意的是,由于 quartz 特殊的 spa 加载原理,如果开启了 spa 模式,希望切换页面的时候保留脚本的话,需要在 script 添加 spa-preserve

而 spa 模式下也会带来额外的问题,如果你导入的是一个 vue 或者 react 的应用,他们通常会添加 style 标签来动态加载样式,这种情况下 spa 切换就会这些动态加载的样式。譬如 twikoo 和 Excalidraw 都有这个问题。

目前暂时的解决方案是每次都重新加载。

本文标题:quartz中使用动态脚本加载

永久链接:https://iceprosurface.com/blog/dev/script/

查看源码: