All files / src/browser loadScript.ts

8.57% Statements 3/35
0% Branches 0/16
0% Functions 0/12
8.82% Lines 3/34

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178                                                                                                                                                                            1x                                                       1x                                                                                                                 1x            
 
/**
 * 仅触发一次事件
 *
 * @private
 *
 * @param node      DOM 节点
 * @param name      事件名称
 * @param callback  回调函数
 */
function once(
  node: HTMLScriptElement,
  name: string,
  fn: EventListener,
  options?: boolean | AddEventListenerOptions,
) {
  function listener(event: Event) {
    node.removeEventListener(name, listener);
    fn(event);
  }
  node.addEventListener(name, listener, options);
}
 
/**
 * 定义IE浏览器的 HTMLScriptElement 属性
 */
export interface MSHTMLScriptElement extends HTMLScriptElement {
  readyState: 'loaded' | 'complete';
  onreadystatechange: ((this: Window, ev: ProgressEvent<Window>) => any) | null;
}
 
/**
 * 监听脚本加载事件 - IE 兼容
 *
 * @private
 *
 * @param node     DOM 节点
 * @param callback 回调函数
 */
function listenWithIE(node: MSHTMLScriptElement, callback: () => void) {
  node.onreadystatechange = function () {
    if (node.readyState === 'loaded' || node.readyState === 'complete') {
      node.onreadystatechange = null;
      callback();
    }
  };
}
 
/**
 * 监听脚本加载事件 - 其他浏览器
 *
 * @private
 *
 * @param node     DOM 节点
 * @param callback 回调函数
 */
function listen(
  node: HTMLScriptElement,
  onSuccess: EventListener,
  onError: EventListener,
) {
  once(node, 'load', onSuccess);
  once(node, 'error', onError);
}
 
/**
 * 白名单属性
 */
export type AllowListsAttributes = {
  /** 设置 id */
  id: string;
  /** 脚本类型 */
  type: string;
  /** 异步加载 */
  async: boolean;
  /** 延迟加载 */
  defer: boolean;
  /** 跨域属性 */
  crossOrigin: string | null;
  /** Subresource Integrity (SRI) */
  integrity: string;
  /** es6 模块回退 */
  noModule: boolean;
};
 
// 允许的属性名单
const allowLists: Array<keyof AllowListsAttributes> = [
  'id',
  'type',
  'async',
  'defer',
  'crossOrigin',
  'integrity',
  'noModule',
];
 
/**
 * 动态加载脚本
 *
 * @param src    文件地址
 * @param attrs  可选的属性
 *
 * @returns dom 节点
 *
 * @example
 *
 * try {
 *  const src = 'https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js'
 *
 *  await loadScript(src, { id: 'jquery.3.6.0.min' })
 * } catch {
 *   alert('加载失败')
 * }
 */
export function loadScript(
  umd: Record<string, string>,
  progectId: string,
  attrs?: Partial<AllowListsAttributes>,
): Promise<HTMLScriptElement> {
  return new Promise((resolve, reject) => {
    const scriptNode: HTMLScriptElement = document.createElement('script');
    // const styleNode: HTMLLinkElement = document.createElement('link');
    // const id = uuidv4();
    scriptNode.id = progectId;
 
 
 
    // styleNode.id = id;
    // 监听事件
    if ((scriptNode as MSHTMLScriptElement).readyState) {
      listenWithIE(scriptNode as MSHTMLScriptElement, () => resolve(scriptNode));
    } else {
      listen(
        scriptNode,
        () => resolve(scriptNode),
        () => {
          reject();
          document.removeChild(document.getElementById(progectId) as Node);
        },
      );
    }
 
    // 设置白名单属性
    if (attrs) {
      allowLists.forEach(attrName => {
        if (attrName in attrs) {
          const value = attrs[attrName];
 
          if (typeof value === 'string') {
            scriptNode.setAttribute(attrName, value);
            // styleNode.setAttribute(attrName, value);
          } else if (typeof value === 'boolean') {
            // 布尔值与字符串的设置不太一样
            scriptNode[attrName as 'defer' | 'async' | 'noModule'] = value;
          }
        }
      });
    }
 
    // 设置资源地址
    scriptNode.src = umd.js;
    // styleNode.href = umd.css;
 
 
 
    // 插入到页面中
    document.body.appendChild(scriptNode);
    // document.body.appendChild(styleNode);
  });
}
 
export function unLoadScript(progectId: string) {
  if (document.getElementById(progectId)) {
    const scriptNode: any = document.getElementById(progectId);
    document.body.removeChild(scriptNode)
  }
}