/** * H5 polyfill: 在 UniApp 框架初始化完成前,为 uni API 提供浏览器降级实现。 * * 核心策略: * - 用 Object.defineProperty 拦截 window.uni 的赋值,防止 UniApp 运行时 * 用存根对象覆盖我们的实现。 * - 当 UniApp 将 stub 对象赋给 window.uni 时,把 stub 合并到我们的代理 * 对象中,同时保留我们对"未实现"API 的真实降级实现。 * - 之后 UniApp 注册真正的 H5 实现(如 uni.request = xhrImpl),属性直接 * 写到代理对象上,我们的 polyfill 就会被替换,互不干扰。 */ ;(function () { if (typeof window === 'undefined') return; /* -------- 各 API 的降级实现 -------- */ var polyfills = {}; // uni.request → XHR polyfills.request = function (opts) { opts = opts || {}; var xhr = new XMLHttpRequest(); var method = (opts.method || 'GET').toUpperCase(); var url = opts.url || ''; // GET/HEAD 把 data 追加到 query string if (opts.data && (method === 'GET' || method === 'HEAD')) { var qs = Object.keys(opts.data).map(function (k) { return encodeURIComponent(k) + '=' + encodeURIComponent(opts.data[k]); }).join('&'); if (qs) url += (url.indexOf('?') >= 0 ? '&' : '?') + qs; } try { xhr.open(method, url, true); } catch (e) { opts.fail && opts.fail({ errMsg: 'request:fail ' + e.message }); return; } // 设置请求头 var header = opts.header || opts.headers || {}; Object.keys(header).forEach(function (k) { try { xhr.setRequestHeader(k, header[k]); } catch (e) {} }); xhr.onreadystatechange = function () { if (xhr.readyState !== 4) return; var statusCode = xhr.status; var data; try { data = JSON.parse(xhr.responseText); } catch (e) { data = xhr.responseText; } // 解析响应头 var resHeader = {}; try { (xhr.getAllResponseHeaders() || '').split('\r\n').forEach(function (line) { var idx = line.indexOf(':'); if (idx > 0) resHeader[line.slice(0, idx).trim().toLowerCase()] = line.slice(idx + 1).trim(); }); } catch (e) {} var res = { statusCode: statusCode, data: data, header: resHeader }; if (statusCode >= 200 && statusCode < 400) { opts.success && opts.success(res); } else { opts.fail && opts.fail({ errMsg: 'request:fail statusCode ' + statusCode }); } opts.complete && opts.complete(res); }; xhr.onerror = function () { var err = { errMsg: 'request:fail network error' }; opts.fail && opts.fail(err); opts.complete && opts.complete(err); }; xhr.ontimeout = function () { var err = { errMsg: 'request:fail timeout' }; opts.fail && opts.fail(err); opts.complete && opts.complete(err); }; var body = null; if (opts.data && method !== 'GET' && method !== 'HEAD') { body = typeof opts.data === 'string' ? opts.data : JSON.stringify(opts.data); } try { xhr.send(body); } catch (e) { opts.fail && opts.fail({ errMsg: 'request:fail ' + e.message }); } }; // getStorageSync → localStorage polyfills.getStorageSync = function (key) { try { var v = localStorage.getItem(key); if (v === null) return undefined; try { return JSON.parse(v); } catch (e) { return v; } } catch (e) { return undefined; } }; polyfills.setStorageSync = function (key, data) { try { localStorage.setItem(key, typeof data === 'object' ? JSON.stringify(data) : String(data)); } catch (e) {} }; polyfills.removeStorageSync = function (key) { try { localStorage.removeItem(key); } catch (e) {} }; // getWindowInfo → 浏览器 window polyfills.getWindowInfo = function () { return { windowWidth: window.innerWidth || 375, windowHeight: window.innerHeight || 667, screenWidth: window.screen ? window.screen.width : 375, screenHeight: window.screen ? window.screen.height : 667, statusBarHeight: 0, safeAreaInsets: { top: 0, bottom: 0, left: 0, right: 0 } }; }; // getEnterOptionsSync → 解析当前 URL query polyfills.getEnterOptionsSync = function () { var query = {}; try { var search = location.search.slice(1); if (search) { search.split('&').forEach(function (pair) { var kv = pair.split('='); if (kv[0]) query[decodeURIComponent(kv[0])] = decodeURIComponent(kv[1] || ''); }); } } catch (e) {} return { query: query, path: location.pathname }; }; /* -------- 代理对象:稳定的 window.uni -------- */ // 创建持久代理对象,把 polyfill 方法写入 var _proxy = {}; Object.keys(polyfills).forEach(function (k) { _proxy[k] = polyfills[k]; }); /** * 把一个 uni 对象(可能含存根)合并到 _proxy。 * 原则:如果 polyfill 中有对应 key,则仅当对方的实现不是存根时才覆盖; * 其余 key 直接合并,以保留 UniApp 运行时的其他功能(路由、组件等)。 */ function mergeIntoProxy(srcObj) { if (!srcObj || typeof srcObj !== 'object') return; Object.keys(srcObj).forEach(function (k) { var val = srcObj[k]; if (polyfills[k]) { // 对于我们有 polyfill 的 key:只有对方是真正的实现时才覆盖 // 简单判断:UniApp 存根函数不含 prototype.constructor 以外的属性且长度固定 // 最安全的方式:暂时保留我们的实现,等真实实现直接赋值给 uni.xxx 时再替换 // 这里什么都不做——让 UniApp 运行时后续直接赋值来替换 } else { _proxy[k] = val; } }); // 把 prototype 链上的方法也复制(如 $emit/$on 等) try { var proto = Object.getPrototypeOf(srcObj); if (proto && proto !== Object.prototype) { Object.getOwnPropertyNames(proto).forEach(function (k) { if (k !== 'constructor' && !_proxy[k]) { try { _proxy[k] = proto[k].bind(srcObj); } catch (e) {} } }); } } catch (e) {} } /* 拦截 window.uni 赋值 */ try { Object.defineProperty(window, 'uni', { get: function () { return _proxy; }, set: function (newVal) { // UniApp 运行时把存根对象赋给 window.uni 时在这里被拦截 mergeIntoProxy(newVal); }, configurable: true, enumerable: true }); } catch (e) { // 极少数情况下 defineProperty 失败,退化为直接赋值 window.uni = _proxy; } })();