在使用公司平台提供的组件时,遇到在6s、7p时出现白屏情况,通过review代码发现平台的组件使用了URLSearchParams和es6 的includes。
通过caniuse.com 或者MDN查看 URLSearchParams兼容发现,对于低版本的IOS 10.3 ( 2017 年 3月发布)以下是不支持的。
使用URLSearchParams 实现URL拼接源码:
export function contactUrl (url = '', params = {}) {
let qs = new URLSearchParams()
for (let key in params) {
qs.append(key, params[key])
}
if (url.includes('?')) {
return url + '&' + qs.toString()
} else {
return url + '?' + qs.toString()
}
}
如何解决?
1、使用url-search-params-polyfill
2、自己使用字符串拼接URL
乍一看这实现很简单很好修改,直接使用字符串拼接方式:
对于简单的参数(字符串,数字)这样处理是没有问题的,如果传入的params 中存在特殊参数(加密串),这种简单处理就出现无法匹配的问题了。
项目中正好有个特殊参数加密session_id ,使用上面的实现出现无法通过校验的问题。对比2个方式的请求URL发现session_id长度不一致,尝试过多种方式都无效,最后参考url-search-params-polyfill 是如何实现的。
URLSearchParamsPolyfill源码分析
在url-search-params-polyfill的 index.js 中,我们看到append(name, value)的实现是调用appendTo() 将name, value 作为一个数组的key , value 存储在this身上。
直接看appendTo(dirct, name ,value) 方法(append 调用中dirct 指向 对象this):
function appendTo(dict, name, value) {
var val = typeof value === 'string' ? value : (
value !== null && value !== undefined && typeof value.toString === 'function' ? value.toString() : JSON.stringify(value)
);
if (name in dict) {
dict[name].push(val);
} else {
dict[name] = [val];
}
}
append 没有特殊,再来看下URLSearchParamsPolyfill中的toString方法:
prototype.toString = function() {
var dict = this[__URLSearchParams__], query = [], i, key, name, value;
for (key in dict) {
name = encode(key);
for (i = 0, value = dict[key]; i < value.length; i++) {
query.push(name + '=' + encode(value[i]));
}
}
return query.join('&');
};
遍历this对象每个元素,并对每个元素进行encode后拼接成参数字符('a=b&c=d')。
重点就在于对每个元素encode 方法,通过源码分析它的实现并不是简单进行encodeURIComponent , 同时进行了字符串进行编码escape。
function encode(str) {
var replace = {
'!': '%21',
"'": '%27',
'(': '%28',
')': '%29',
'~': '%7E',
'%20': '+',
'%00': '\x00'
};
return encodeURIComponent(str).replace(/[!'\(\)~]|%20|%00/g, function(match) {
return replace[match];
});
}
看到这里就已经很明了,URLSearchParams在对象调用toString时对元素参数进行的处理。
最后在自我实现的方法拼接字符串的时候添加相应的字符编码处理,把问题解决了。
参考资料:
url-search-params-polyfill