在 前文 中粗糙的实现了动态脚本加载,这里我们在进一步优化一下异步动态的脚本加载:
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 都有这个问题。
目前暂时的解决方案是每次都重新加载。