UNPKG

t-comm

Version:

专业、稳定、纯粹的工具库

173 lines (171 loc) 5.89 kB
var addScript = function addScript(script) { var _a; // Get the first script element, we're just going to use it // as a reference for where to insert ours. Do NOT try to do // this just once at the top and then re-use the same script // as a reference later. Some weird loaders *remove* script // elements after the browser has executed their contents, // so the same reference might not have a parentNode later. var firstScript = document.getElementsByTagName('script')[0]; // Append the script to the DOM, triggering execution. (_a = firstScript === null || firstScript === void 0 ? void 0 : firstScript.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(script, firstScript); }; /** * 以 Callback 的方式加载 js 文件 * @param {String} src js文件路径 * @param {Function|Object} callback 加载回调 * @param {String} charset 指定js的字符集 * @param {Object} context Callback context * @example * ```ts * loader('https://example.com/lib.js', (err) => { * if (err) return console.error(err); * console.log('loaded'); * }); * * // 传递 setup 回调以修改 script 标签 * loader('https://example.com/lib.js', { * callback: (err) => {}, * setup: (script) => script.setAttribute('data-foo', '1'), * }); * ``` */ var loader = function loader(src, callback) { var charset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'utf-8'; var context = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; var setup; if (callback && typeof callback !== 'function') { context = callback.context || context; setup = callback.setup; callback = callback.callback; } var done = false; var err; var privateCleanup; // _must_ be set below. /** * Final handler for error or completion. * * **Note**: Will only be called _once_. * * @returns {void} */ var privateFinish = function privateFinish() { // Only call once. if (done) { return; } done = true; // Internal cleanup. privateCleanup === null || privateCleanup === void 0 ? void 0 : privateCleanup(); // Callback. if (callback) { callback.call(context, err); } }; /** * Error handler * * @returns {void} */ var privateError = function privateError() { err = new Error(src || 'EMPTY'); privateFinish(); }; var curScript = document.querySelector("script[src=\"".concat(src, "\"]")); if (curScript) { var tc = setInterval(function () { if (curScript.isready) { // 判断js加载完成 privateFinish(); clearInterval(tc); } }, 20); } else { var script = document.createElement('script'); script.isready = false; if (script.readyState && !('async' in script)) { var isReady = { loaded: true, complete: true }; var inserted = false; // Clear out listeners, state. privateCleanup = function privateCleanup() { script.onreadystatechange = null; script.onerror = null; }; // Attach the handler before setting src, otherwise we might // miss events (consider that IE could fire them synchronously // upon setting src, for example). script.onreadystatechange = function () { var firstState = script.readyState; // Protect against any errors from state change randomness. if (err) { return; } if (!inserted && isReady[firstState]) { inserted = true; // Append to DOM. addScript(script); } if (firstState === 'loaded') { // The act of accessing the property should change the script's // `readyState`. // // And, oh yeah, this hack is so hacky-ish we need the following // eslint-disable-next-line @typescript-eslint/no-unused-expressions script.children; if (script.readyState === 'loading') { // State transitions indicate we've hit the load error. // // **Note**: We are not intending to _return_ a value, just have // a shorter short-circuit code path here. return privateError(); } } // It's possible for readyState to be "complete" immediately // after we insert (and execute) the script in the branch // above. So check readyState again here and react without // waiting for another onreadystatechange. if (script.readyState === 'complete') { script.isready = true; privateFinish(); } }; // Onerror handler _may_ work here. script.onerror = privateError; // call the setup callback to mutate the script tag if (setup) { setup.call(context, script); } // This triggers a request for the script, but its contents won't // be executed until we append it to the DOM. script.src = src; // In some cases, the readyState is already "loaded" immediately // after setting src. It's a lie! Don't append to the DOM until // the onreadystatechange event says so. } else { // This section is for modern browsers, including IE10+. // Clear out listeners. privateCleanup = function privateCleanup() { script.onload = null; script.onerror = null; }; script.onerror = privateError; script.onload = function () { script.isready = true; privateFinish(); }; script.async = true; script.charset = charset || 'utf-8'; // call the setup callback to mutate the script tag if (setup) { setup.call(context, script); } script.src = src; // Append to DOM. addScript(script); } } }; export { loader };